::nx::Object ::nx::Object ::nsmcp::AccessRecordParser ::nsmcp::AccessRecordParser completionTime failureReason init parse ::nsmcp::AccessRecordParser->::nx::Object

Class ::nsmcp::AccessRecordParser

::nsmcp::AccessRecordParser[i] create ... \
           [ -timingColumn:integer (default "-1") ]

-1 detects common/combined layouts; -2 disables timing detection. Explicit 0 (common) or 2 (combined) can pin the configured column.
Defined in /usr/local/ns/tcl/nsmcp/lib/access-record.tcl

Class Relations

  • class: ::nx::Class[i]
  • superclass: ::nx::Object[i]
::nx::Class create ::nsmcp::AccessRecordParser \
     -superclass ::nx::Object

Methods (to be applied on instances)

  • completionTime (scripted, public)

     <instance of nsmcp::AccessRecordParser[i]> completionTime line
    Parameters:
    line (required)

    Testcases:
    No testcase defined.
    if {![regexp {^\S+\s+\S+\s+(?:"(?:[^"\\]|\\.)*"|\S+)\s+(\[[^\]\r\n]+\]|[0-9]+)\s+"} $line -> stamp]} {return {}}
    set stamp [string trim $stamp {[]}]
    if {[string is entier -strict $stamp]} {return $stamp}
    if {[catch {clock scan $stamp -format {%d/%b/%Y:%H:%M:%S %z} -locale C} time]} {return {}}
    return $time
  • failureReason (scripted, public)

     <instance of nsmcp::AccessRecordParser[i]> failureReason line
    Parameters:
    line (required)

    Testcases:
    No testcase defined.
    if {[string first \x00 $line]>=0} {return layout}
    if {![regexp {^\S+\s+\S+\s+(?:"(?:[^"\\]|\\.)*"|\S+)\s+(\[[^\]\r\n]+\]|[0-9]+)\s+"((?:[^"\\\r\n]|\\.)*)"\s+([1-5][0-9]{2})\s+(-|[0-9]+)(.*)$} $line]} {return layout}
    if {[:completionTime $line] eq ""} {return timestamp}
    return request
  • parse (scripted, public)

     <instance of nsmcp::AccessRecordParser[i]> parse line
    Parameters:
    line (required)

    Testcases:
    No testcase defined.
    if {[string first \x00 $line]>=0} {return {}}
    if {![regexp {^(\S+)\s+(\S+)\s+(?:"(?:[^"\\]|\\.)*"|\S+)\s+(\[[^\]\r\n]+\]|[0-9]+)\s+"((?:[^"\\\r\n]|\\.)*)"\s+([1-5][0-9]{2})\s+(-|[0-9]+)(.*)$} $line -> peer identifier stamp request status bytes tail]} {return {}}
    set timestamp [string trim $stamp {[]}]
    set result [dict create timestamp $timestamp peerAddress $peer requestIdentifier {} pool unknown method unknown url {} status [scan $status %d] responseBytes 0 timingSource completion]
    if {$bytes ne "-"} {dict set result responseBytes [scan $bytes %lld]}
    if {[::nsmcp::logRecordParser validRequestIdentifier $identifier]} {
        dict set result requestIdentifier $identifier
    }
    # Only connection headers identify a connection pool.
    if {[::nsmcp::logRecordParser validConnectionIdentifier $identifier]} {
        dict set result pool [lindex [split $identifier :] 2]
    }
    # Empty/binary/probe requests remain valid access records. Extract the
    # ends of a request line, allowing embedded spaces in the logged target.
    set first [string first " " $request]
    set last [string last " " $request]
    if {$first>0 && $last>$first} {
        set protocol [string range $request [expr {$last+1}] end]
        if {[string match */* $protocol]} {
            dict set result method [string range $request 0 [expr {$first-1}]]
            dict set result url [string range $request [expr {$first+1}] [expr {$last-1}]]
            dict set result protocol $protocol
        }
    } elseif {[string match /* $request]} {
        dict set result url $request
    }
    set fields [regexp -all -inline {"(?:[^"\\]|\\.)*"} $tail]
    set index ${:timingColumn}
    if {$index == -1} {set index [expr {[llength $fields]>=2 ? 2 : 0}]}
    foreach index [expr {$index<0 ? {} : [list $index]}] {
        set field [string trim [lindex $fields $index] {"}]
        if {![regexp {^([0-9]+\.[0-9]+) ([0-9]+(?:\.[0-9]+)?) ([0-9]+(?:\.[0-9]+)?) ([0-9]+(?:\.[0-9]+)?) ([0-9]+(?:\.[0-9]+)?)$} $field -> start accept queue filter run]} continue
        if {$start < 946684800 || $start > [clock seconds]+86400} continue
        dict set result startTime $start
        dict set result timingSource start
        foreach {key value} [list acceptTime $accept queueTime $queue filterTime $filter runTime $run] {
            dict set result $key [expr {double($value)}]
        }
        break
    }
    if {![dict exists $result startTime]} {
        if {[string is entier -strict $timestamp]} {
            set time $timestamp
        } elseif {[catch {clock scan $timestamp -format {%d/%b/%Y:%H:%M:%S %z} -locale C} time]} {return {}}
        dict set result startTime $time
    }
    set path [lindex [split [dict get $result url] ?] 0]
    if {[regexp {^[A-Za-z][A-Za-z0-9+.-]*://[^/]*(/.*)?$} $path -> suffix]} {
        set path [expr {$suffix eq "" ? "/" : $suffix}]
    }
    dict set result path $path
    return $result