- Methods: All Methods Documented Methods Hide Methods
- Source: Display Source Hide Source
- Variables: Show Variables Hide Variables
Class ::nsmcp::AccessRecordParser
::nsmcp::AccessRecordParser-1 detects common/combined layouts; -2 disables timing detection. Explicit 0 (common) or 2 (combined) can pin the configured column.create ... \
[ -timingColumn:integer (default "-1") ]
Defined in /usr/local/ns/tcl/nsmcp/access-record.tcl
Class Relations
::nx::Class create ::nsmcp::AccessRecordParser \ -superclass ::nsmcp::ObjectMethods (to be applied on instances)
completionTime (scripted, public)
<instance of nsmcp::AccessRecordParser> completionTime line
Extract the completion timestamp from an access-log line. Accept the supported epoch or common-log timestamp without interpreting request data as executable Tcl.
- Parameters:
- line (required)
- Raw physical log line to inspect.
- Returns:
- Epoch seconds, or an empty result when the timestamp cannot be parsed.
- 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 $timefailureReason (scripted, public)
<instance of nsmcp::AccessRecordParser> failureReason line
Classify why an access-log line could not be parsed. This diagnostic path runs only for rejected rows and distinguishes layout, timestamp and request failures.
- Parameters:
- line (required)
- Raw physical log line to inspect.
- Returns:
- The failure category: layout, timestamp or request.
- 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 requestfields (scripted, public)
<instance of nsmcp::AccessRecordParser> fields peer identifier \ user stamp request status bytes tail [ scannedFields ] \ [ parsedTiming ]
Normalize decoded access-log fields into a request record. Combine scanned metadata and optional C-parsed timing with the same validation used by Tcl scanning.
- Parameters:
- peer (required)
- Logged peer address, including the supported unknown-value sentinel.
- identifier (required)
- Complete log-thread identifier, or the raw identifier field when parsing access records.
- user (required)
- Logged user identifier used to derive authentication state.
- stamp (required)
- Original access-log timestamp field.
- request (required)
- Decoded HTTP request-line field.
- status (required)
- Decoded HTTP response status.
- bytes (required)
- Logged response-byte value, retaining the supported unknown-value sentinel.
- tail (required)
- Remaining access-log fields after status and response size.
- scannedFields (optional)
- Optional metadata already decoded by the C scanner.
- parsedTiming (optional)
- Optional timing dictionary already decoded by the C scanner.
- Returns:
- A normalized request dictionary, or an empty result for invalid fields.
- Testcases:
- No testcase defined.
if {![regexp {^[1-5][0-9]{2}$} $status]} { return {} } set timestamp [string trim $stamp {[]}] set fields $scannedFields if {$fields eq {}} { set fields [regexp -all -inline {"(?:[^"\\]|\\.)*"} $tail] } set combined [expr {[llength $fields]>=2}] set agent {} set escapes [list {\\} \\ {\"} \" {\n} \n {\r} \r {\t} \t] if {$combined} { set agent [string map $escapes [string range [lindex $fields 1] 1 end-1]] } set request [string map $escapes $request] if {[string index $user 0] eq {"}} { set user [string map $escapes [string range $user 1 end-1]] } if {$parsedTiming eq {}} { set parsedTiming [:timingFields $timestamp $fields] } if {$parsedTiming eq {}} { return {} } set responseBytes 0 if {$bytes ne "-"} { set responseBytes [scan $bytes %lld] } return [:requestFields $peer $identifier $user $timestamp $request [scan $status %d] $responseBytes $combined $agent $parsedTiming]indexTime (scripted, public)
<instance of nsmcp::AccessRecordParser> indexTime line
Extract a bounded timestamp suitable for access-log indexing. Reject NUL-bearing or unsupported lines before deriving the record's index time.
- Parameters:
- line (required)
- Raw physical log line to inspect.
- Returns:
- The index timestamp, or an empty result for an unusable line.
- 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 -> stamp tail]} { return {} } set fields [regexp -all -inline {"(?:[^"\\]|\\.)*"} $tail] set timing [:timingFields [string trim $stamp {[]}] $fields] if {$timing eq "" || [dict exists $timing timeDependent]} { return {} } return [dict get $timing startTime]parse (scripted, public)
<instance of nsmcp::AccessRecordParser> parse line
Parse one physical access-log line into a request record. Validate the supported layout and delegate decoded fields to the common field normalizer.
- Parameters:
- line (required)
- Raw physical log line to inspect.
- Returns:
- A request dictionary, or an empty result for a rejected line.
- 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 user stamp request status bytes tail]} { return {} } return [:fields $peer $identifier $user $stamp $request $status $bytes $tail]requestFields (scripted, public)
<instance of nsmcp::AccessRecordParser> requestFields peer \ identifier user timestamp request status bytes combined agent \ timing
Build a request record from validated fields and timing. Derive authentication and request metadata while retaining explicit unknown-value representations.
- Parameters:
- peer (required)
- Logged peer address, including the supported unknown-value sentinel.
- identifier (required)
- Complete log-thread identifier, or the raw identifier field when parsing access records.
- user (required)
- Logged user identifier used to derive authentication state.
- timestamp (required)
- Parsed timestamp or timing base supplied by the parser.
- request (required)
- Decoded HTTP request-line field.
- status (required)
- Decoded HTTP response status.
- bytes (required)
- Logged response-byte value, retaining the supported unknown-value sentinel.
- combined (required)
- Whether the decoded access line uses the combined format.
- agent (required)
- Decoded User-Agent value.
- timing (required)
- Normalized request-timing dictionary.
- Returns:
- The normalized request dictionary.
- Testcases:
- No testcase defined.
set authenticationState [expr {$user in {0 -} ? "anonymous" : ($user in {{} {[REDACTED]}} ? "unknown" : "authenticated")}] set result [dict create authenticationState $authenticationState timestamp $timestamp peerAddress $peer requestIdentifier {} pool unknown method unknown url {} status $status responseBytes $bytes timingSource completion] if {[::nsmcp::logRecordParser validRequestIdentifier $identifier]} { dict set result requestIdentifier $identifier } set result [dict merge $result [::nsmcp::accessRecordIdentity $identifier]] if {[dict get $result recordSource] eq "driver"} { # Request authentication was not performed by an application worker. dict set result authenticationState unknown } # 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 } if {$combined && ${:timingColumn} in {-2 -1 2}} { dict set result userAgent $agent } set result [dict merge $result $timing] set available [expr {[dict get $timing timingSource] eq "start" && [dict get $result recordSource] ne "driver"}] dict set result phaseTimingAvailable $available dict set result phaseTimingReason [expr {$available ? "logged-worker-phases" : ([dict get $result recordSource] eq "driver" ? "driver-synthetic-phases" : "no-phase-counters")}] if {!$available} { foreach key {acceptTime queueTime filterTime runTime} { dict unset result $key } } 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 $resulttimingMicroseconds (scripted, public)
<instance of nsmcp::AccessRecordParser> timingMicroseconds \ timestamp start accept queue filter run
Convert C-parsed request timing values to the parser's timing fields. Use supplied microsecond values directly, normalizing start time and phase durations to seconds.
- Parameters:
- timestamp (required)
- Parsed timestamp or timing base supplied by the parser.
- start (required)
- Request start timestamp in microseconds.
- accept (required)
- Accept-phase duration in microseconds.
- queue (required)
- Queue-phase duration in microseconds.
- filter (required)
- Filter-phase duration in microseconds, or the line-prefilter flag for nextLine.
- run (required)
- Run-phase duration in microseconds.
- Returns:
- A timing dictionary containing the available parsed phases.
- Testcases:
- No testcase defined.
set phases {} if {$start ne {}} { set start [expr {$start / 1000000.0}] foreach value [list $accept $queue $filter $run] { lappend phases [expr {$value eq {} ? 0.0 : $value / 1000000.0}] } } return [:timingValues [string trim $timestamp {[]}] $start $phases]
- Methods: All Methods Documented Methods Hide Methods
- Source: Display Source Hide Source
- Variables: Show Variables Hide Variables
![[i]](/resources/acs-subsite/ZoomIn16.gif)