Class ::nsmcp::LogRecordParser (public)

 ::nx::Class ::nsmcp::LogRecordParser[i]

Defined in /usr/local/ns/tcl/nsmcp/log-record.tcl

Testcases:
No testcase defined.
Source code:
    :public method validRequestIdentifier {identifier} {
        # System-log identifiers are thread names, including sched/nsjob/SMTP.
        return [regexp {^-[^\x00\[\]\s]+-$} $identifier]
    }
    :public method validConnectionIdentifier {identifier} {
        return [regexp {^-conn:[^:\[\]\r\n]+:[^:\[\]\r\n]+:[0-9]+:[0-9]+-$} $identifier]
    }
    :variable colorCodeMap
    :public method stripAnsi {message} {
        if {[string first \x1b $message] < 0} {return $message}
        if {![info exists :colorCodeMap]} {
            set :colorCodeMap [list  [binary decode hex 1b5b303b33326d] {}  [binary decode hex 1b5b303b33396d] {}  [binary decode hex 1b5b306d] {}  [binary decode hex 1b5b313b33316d] {}  [binary decode hex 1b5b313b33396d] {}]
        }
        set message [string map ${:colorCodeMap} $message]
        # Preserve support for uncommon/custom SGR sequences, off the fast path.
        if {[string first \x1b $message] >= 0} {regsub -all {\x1b\[[0-9;]*m} $message {} message}
        return $message
    }
    :public method accessRequestIdentifier {message} {
        # OpenACS access format: peer request-id user [timestamp] ...
        if {[regexp {^\S+[ \t]+(\S+)[ \t]+\S+[ \t]+\[} $message -> identifier] &&
            [:validRequestIdentifier $identifier]} {
            return $identifier
        }
        return {}
    }
    # PostgreSQL fields belong only to a preceding PostgreSQL diagnostic.
    :public method continuation {message line} {
        return [expr {[string index $line 0] eq ":" ||
            ([regexp {^(DEBUG[1-5]?|LOG|INFO|NOTICE|WARNING|ERROR|FATAL|PANIC):[ \t]+} $message] &&
             [regexp {^(DETAIL|HINT|CONTEXT):[ \t]+} $line])}]
    }
    # System-log timestamps use the process timezone unless an offset is present.
    # Return unknown on invalid input; fractional seconds are preserved.
    :public method timestampSeconds {timestamp} {
        if {![regexp {^([0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2})(\.[0-9]+)?( [+-][0-9]{4})?$} $timestamp -> base fraction zone]} {return {}}
        set format {%d/%b/%Y:%H:%M:%S}
        if {$zone ne ""} {append base $zone; append format { %z}}
        if {[catch {clock scan $base -format $format -locale C} seconds]} {return {}}
        if {$fraction ne ""} {set seconds [expr {$seconds+double("0$fraction")}]}
        return $seconds
    }
    :public method metadata {message} {
        # Access logs contain no ANSI formatting. Strip only system-log rows.
        if {[string index $message 0] eq "\[" || [string index $message 0] eq "\x1b"} {set message [:stripAnsi $message]}
        set result [dict create timestamp {} processThread {} logThread {}  severity {} requestIdentifier {} peerAddress {} method {} url {}  protocol {} status {} responseBytes {} recordType unknown]
        if {[regexp {^\[([^\]\r\n]+)\]\[([^\]\r\n]+)\]\[([^\]\r\n]+)\][ \t]+([A-Za-z][A-Za-z0-9_()-]*):[ \t]*}  $message -> timestamp processThread thread severity]} {
            dict set result recordType system
            dict set result timestamp $timestamp
            dict set result processThread $processThread
            dict set result logThread $thread
            dict set result severity $severity
        } elseif {[regexp {^\[([^\]\r\n]+)\][ \t]+([A-Za-z][A-Za-z0-9_()-]*):[ \t]*}  $message -> thread severity]} {
            # Startup logging can omit the date and process/thread fields.
            dict set result recordType system-short
            dict set result logThread $thread
            dict set result severity $severity
        } elseif {[regexp {^\[([^\]\r\n]+)\][ \t]+Mutex (lock|unlock) ([^\r\n]+): (wait|lock) duration (-?[0-9]+(?:\.[0-9]+)?)[ \t]*(?:\r?\n|$)}  $message -> thread operation mutex metric duration]
                  && [string length $duration]<=32
                  && (($operation eq "lock" && $metric eq "wait") || ($operation eq "unlock" && $metric eq "lock"))} {
            # mutexlocktrace uses stderr to avoid recursively taking log locks.
            # No timestamp or log severity is present; neither is inferred.
            dict set result recordType mutex-trace
            dict set result logThread $thread
            dict set result mutexName $mutex
            dict set result mutexOperation $operation
            dict set result mutexMetric [expr {$metric eq "wait" ? "wait" : "hold"}]
            dict set result mutexDurationSeconds [expr {double($duration)}]
        } elseif {[regexp {^\[([^\]\r\n]+)\][ \t]+Mutex lock ([^\r\n]+) warning: wait duration (-?[0-9]+(?:\.[0-9]+)?|0\.-[0-9]{1,6}) is negative[ \t]*(?:\r?\n|$)}  $message -> thread mutex duration] && [string length $duration]<=32} {
            dict set result recordType mutex-trace
            dict set result logThread $thread
            dict set result severity Warning
            dict set result mutexName $mutex
            dict set result mutexOperation lock
            dict set result mutexMetric wait
            # NS_TIME_FMT prints a negative subsecond timeval as 0.-00001:
            # the signed integer microseconds must be divided by one million.
            if {[string match {0.-*} $duration]} {
                scan [string range $duration 3 end] %d micros
                set seconds [expr {-$micros/1000000.0}]
            } else {
                set seconds [expr {double($duration)}]
            }
            dict set result mutexDurationSeconds $seconds
        } elseif {[regexp {^(Ns_MutexLock|Ns_MutexTryLock|Ns_MutexUnlock|NsGetLock): called with uninitialized lock pointer\. This should not happen, call Ns_MutexInit\(\) before this call(?:\r?\n|$)} $message]} {
            # Historical stderr diagnostics contain no thread or timestamp.
            dict set result recordType system-short
            dict set result severity Warning
        } elseif {[regexp {^(DEBUG[1-5]?|LOG|INFO|NOTICE|WARNING|ERROR|FATAL|PANIC):[ \t]+} $message -> level]} {
            dict set result recordType postgres
            dict set result severity [string totitle $level]
        } elseif {[regexp {^(\S+)[ \t]+(\S+)[ \t]+\S+[ \t]+\[([0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}[ \t]+[+-][0-9]{4})\][ \t]+"((?:[^"\\\r\n]|\\.)*)"[ \t]+([1-5][0-9]{2})[ \t]+(-|[0-9]+)(?:[ \t]|$)}  $message -> peer identifier timestamp request status bytes]} {
            dict set result recordType access
            dict set result timestamp $timestamp
            dict set result peerAddress $peer
            if {[:validRequestIdentifier $identifier]} {dict set result requestIdentifier $identifier}
            dict set result status [scan $status %d]
            if {$bytes ne "-"} {
                set bytes [string trimleft $bytes 0]
                dict set result responseBytes [expr {$bytes eq "" ? 0 : $bytes}]
            }
            # Preserve the logged request target; do not decode percent escapes
            # or interpret the application's query parameters here.
            if {[regexp {^(\S+)[ \t]+(\S+)[ \t]+(HTTP/[0-9]+\.[0-9]+)$}  $request -> method url protocol]} {
                dict set result method $method
                dict set result url $url
                dict set result protocol $protocol
            }
        }
        set thread [dict get $result logThread]
        if {$thread ne "" && [:validRequestIdentifier $thread]} {
            dict set result requestIdentifier $thread
        }
        if {[dict get $result status] ne "" && [nsf::object::exists ::nsmcp::accessRecordParser]} {
            set access [::nsmcp::accessRecordParser parse $message]
            foreach key {requestIdentifier method url protocol pool startTime timingSource acceptTime queueTime filterTime runTime} {
                if {[dict exists $access $key]} {dict set result $key [dict get $access $key]}
            }
        }
        return $result
    }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: