nsmcp::CachedLogSearch method errorSummary (public)
<instance of nsmcp::CachedLogSearch> errorSummary options \ redactor
Defined in /usr/local/ns/tcl/nsmcp/log-summary.tcl
- Parameters:
- options (required)
- redactor (required)
- Testcases:
- No testcase defined.
Source code: set from [dict get $options from] set to [dict get $options to] set maxGroups [dict get $options maxGroups] set severities [expr {[dict exists $options severities] ? [dict get $options severities] : {Error Fatal Bug}}] file stat ${:path} before set source [open ${:path} rb] set groups {} set matched 0 set ungrouped 0 set overflowSample {entries {} maximum 0} set overflowFamilySamples {} set sampleSeed {} set familyTotals [::nsmcp::summaryFamilyCounts] set overflowFamilyTotals [::nsmcp::summaryFamilyCounts] set applicationMarkers 0 set applicationSuppressed 0 set categoryTotals {} set duplicateOrigins [::nsmcp::warningOriginEmpty] set parameterOrigins {} set omittedOriginParameterRecords 0 set previousByThread {} set repeatMarkers 0 set reportedSuppressed 0 set attributedMarkers 0 set unattributedMarkers 0 set crossWindowMarkers 0 set repeatExamples {} set unknownTime 0 set unrecognized 0 set examples {} set timestampedRecords 0 set earliestAt {} set latestAt {} set firstEntryAt {} set lastEntryAt {} set firstEntryOffset {} set lastEntryOffset {} set cachedStamp {} set cachedSeconds {} set postgresRecords 0 set postgresExamples {} set previousTimestamp {} set pendingPostgres {} set mutexTraces 0 set mutexGroups {} set mutexUngrouped 0 set shortEntries 0 set untimestamped 0 set untimestampedExamples {} set severityTotals {}; set unknownTimeTotals {} foreach severity $severities {dict set severityTotals $severity 0; dict set unknownTimeTotals $severity 0} set pending false set clipped 0 set firstTime {} set lastTime {} set deadline [expr {[clock milliseconds]+1000}] set start 0 set examined 0 set stable true set prefix {} if {[dict exists $options scanState]} { set state [dict get $options scanState] foreach name {groups matched ungrouped overflowSample overflowFamilySamples sampleSeed familyTotals overflowFamilyTotals applicationMarkers applicationSuppressed categoryTotals duplicateOrigins parameterOrigins omittedOriginParameterRecords previousByThread repeatMarkers reportedSuppressed attributedMarkers unattributedMarkers crossWindowMarkers repeatExamples unknownTime unrecognized examples severityTotals unknownTimeTotals pending clipped firstTime lastTime start examined prefix mutexTraces mutexGroups mutexUngrouped shortEntries untimestamped untimestampedExamples postgresRecords postgresExamples previousTimestamp pendingPostgres timestampedRecords earliestAt latestAt firstEntryAt lastEntryAt firstEntryOffset lastEntryOffset cachedStamp cachedSeconds} { if {[dict exists $state $name]} {set $name [dict get $state $name]} } if {$matched > 0 && ![dict exists $state familyTotals]} { close $source error "system-log family summary changed during scan; start a new summary" } if {![dict exists $state mutexGroups]} {set mutexUngrouped $mutexTraces} if {[list $before(dev) $before(ino)] ne [dict get $state identity] || $before(size) < [dict get $state sourceBytes]} { close $source error "system log changed during summary scan; start a new summary" } set before(size) [dict get $state sourceBytes] } if {$sampleSeed eq ""} {set sampleSeed [list $before(dev) $before(ino) $before(size) $from $to $severities]} set chunkEnd [expr {min($before(size),$examined+4194304)}] set reader {} try { set currentPrefix [read $source [expr {min(256,$before(size))}]] if {$prefix ne "" && $prefix ne $currentPrefix} { error "system log changed during summary scan; start a new summary" } set prefix $currentPrefix seek $source $examined set reader [::nsmcp::LogSummaryReader new -channel $source -limit $before(size) -offset $examined -maxBytes ${:maxMessageBytes}] while {$examined < $chunkEnd && [clock milliseconds] < $deadline} { set offset $examined set event [$reader next] set examined [dict get $event endOffset] if {$examined <= $offset} {break} if {![dict get $event complete]} {set pending true} # Neighboring timestamp strings are contextual estimates, never dates # assigned to the diagnostic or used for time-window error totals. if {[regexp {^\[([0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[^\]\r\n]+)\]\[[^\]]+\]\[[^\]]+\]} [dict get $event message] -> stamp]} { if {$stamp ne $cachedStamp} { set cachedStamp $stamp set cachedSeconds [${:recordParser} timestampSeconds $stamp] } if {$cachedSeconds ne ""} { incr timestampedRecords if {$firstEntryAt eq ""} {set firstEntryAt $cachedSeconds; set firstEntryOffset $offset} set lastEntryAt $cachedSeconds set lastEntryOffset $offset if {$earliestAt eq "" || $cachedSeconds<$earliestAt} {set earliestAt $cachedSeconds} if {$latestAt eq "" || $cachedSeconds>$latestAt} {set latestAt $cachedSeconds} } foreach i $pendingPostgres { set fields [lindex $postgresExamples $i] lappend fields followingTimestamp string $stamp lset postgresExamples $i $fields } set pendingPostgres {} set previousTimestamp $stamp } # Most records are Notice/Debug output. Avoid parser dispatch and # metadata allocation for valid headers outside the counted levels. if {[regexp {^\[[^\]\r\n]+\]\[[^\]\r\n]+\]\[[^\]\r\n]+\][ \t]+([A-Za-z][A-Za-z0-9_()-]*):[ \t]*} [dict get $event message] -> level] && $level ni $severities} {continue} set meta [${:recordParser} metadata [dict get $event message]] set recordType [dict get $meta recordType] if {$recordType eq "postgres"} { incr postgresRecords if {[llength $postgresExamples]<5} { set text [$redactor message [dict get $event message]] lappend pendingPostgres [llength $postgresExamples] lappend postgresExamples [list offset number $offset endOffset number $examined severity string [dict get $meta severity] example string [string range $text 0 511] truncated boolean [expr {[dict get $event truncated] || [string length $text]>512}] timeSource string inferred-neighbors precedingTimestamp string $previousTimestamp] } } if {$recordType eq "mutex-trace"} { # Scheduler condition waits are expected implementation details, # not contention evidence. Exclude only this exact hold trace; # ns:sched lock waits and negative-wait warnings remain visible. if {[dict get $meta mutexName] eq "ns:sched" && [dict get $meta mutexOperation] eq "unlock" && [dict get $meta mutexMetric] eq "hold"} {continue} incr mutexTraces set mutexKey [list [dict get $meta mutexName] [dict get $meta mutexOperation] [dict get $meta mutexMetric]] set duration [dict get $meta mutexDurationSeconds] if {[dict exists $mutexGroups $mutexKey]} { set group [dict get $mutexGroups $mutexKey] dict incr group count dict set group total [expr {[dict get $group total]+$duration}] dict set group min [expr {min([dict get $group min],$duration)}] dict set group lastOffset $offset if {$duration > [dict get $group max]} { dict set group max $duration dict set group maxOffset $offset dict set group maxThread [$redactor message [dict get $meta logThread]] } if {$duration<0} {dict incr group negative} dict set mutexGroups $mutexKey $group } elseif {[dict size $mutexGroups]<128} { dict set mutexGroups $mutexKey [dict create count 1 total $duration min $duration max $duration firstOffset $offset lastOffset $offset maxOffset $offset maxThread [$redactor message [dict get $meta logThread]] negative [expr {$duration<0}]] } else { incr mutexUngrouped } } if {$recordType eq "system-short"} {incr shortEntries} if {$recordType in {mutex-trace system-short postgres}} { incr untimestamped if {[llength $untimestampedExamples]<5} { set text [$redactor message [dict get $event message]] set fields [list offset number $offset endOffset number $examined recordType string $recordType logThread string [dict get $meta logThread] severity string [dict get $meta severity] example string [string range $text 0 511] truncated boolean [expr {[dict get $event truncated] || [string length $text]>512}]] foreach key {mutexName mutexOperation mutexMetric} { if {[dict exists $meta $key]} {lappend fields $key string [$redactor message [dict get $meta $key]]} } if {[dict exists $meta mutexDurationSeconds]} { lappend fields mutexDurationSeconds number [dict get $meta mutexDurationSeconds] } lappend untimestampedExamples $fields } } if {$recordType eq "mutex-trace"} {continue} set severity [dict get $meta severity] if {$severity eq ""} { incr unrecognized if {[llength $examples] < 5} { lappend examples [list offset number $offset endOffset number $examined example string [string range [$redactor message [dict get $event message]] 0 511] truncated boolean [expr {[dict get $event truncated] || [string length [dict get $event message]]>512}]] } continue } if {$severity ni $severities} {continue} set timestamp [dict get $meta timestamp] if {$timestamp eq $cachedStamp} { set seconds $cachedSeconds } else { set seconds [${:recordParser} timestampSeconds $timestamp] } if {$seconds eq "" || ![string is double -strict $seconds]} { if {$severity in {Error Fatal Bug}} {incr unknownTime} dict incr unknownTimeTotals $severity continue } set text [$redactor message [dict get $event message]] set normalized [::nsmcp::summaryHeading $text] set category [dict get $normalized category] set threadKey {} regexp {^\[[^\]]+\]\[([^\]]+)\]} [dict get $event message] -> threadKey set prior {} if {$threadKey ne "" && [dict exists $previousByThread $threadKey]} {set prior [dict get $previousByThread $threadKey]} if {$category ne "log-repeat-marker" && $threadKey ne "" && ([dict exists $previousByThread $threadKey] || [dict size $previousByThread] < 2048)} { dict set previousByThread $threadKey [dict create timestamp $seconds severity $severity offset $offset pattern [dict get $normalized pattern]] } if {$seconds < $from || $seconds >= $to} {continue} incr matched dict incr severityTotals $severity if {$firstTime eq "" || $seconds < $firstTime} {set firstTime $seconds} if {$lastTime eq "" || $seconds > $lastTime} {set lastTime $seconds} dict incr categoryTotals $category set family [::nsmcp::summaryFamily $normalized] dict incr familyTotals $family if {[dict get $normalized suppressedOccurrences] > 0} { incr applicationMarkers incr applicationSuppressed [dict get $normalized suppressedOccurrences] } if {$category eq "log-repeat-marker"} { incr repeatMarkers # Native sameLineCount includes the originally written message. scan [dict get $normalized repeats] %lld runCount set suppressed [expr {max(0,$runCount-1)}] incr reportedSuppressed $suppressed set attributed [expr {$prior ne "" && [dict get $prior severity] eq $severity}] if {$attributed} { incr attributedMarkers if {[dict get $prior timestamp] < $from || [dict get $prior timestamp] >= $to} {incr crossWindowMarkers} } else {incr unattributedMarkers} if {[llength $repeatExamples] < 5} { set fields [list markerOffset number $offset markerTimestamp number $seconds reportedSuppressedOccurrences number $suppressed attributed boolean $attributed] if {$attributed} { lappend fields precedingOffset number [dict get $prior offset] precedingTimestamp number [dict get $prior timestamp] precedingPattern string [dict get $prior pattern] } lappend repeatExamples $fields } } if {$category eq "duplicate-parameter"} { set requestMetadata [::nsmcp::warningRequestMetadata [dict get $event message] $redactor] ::nsmcp::warningOriginAdd duplicateOrigins $requestMetadata 128 set parameter [dict get $normalized parameter] if {[dict exists $parameterOrigins $parameter] || [dict size $parameterOrigins] < 128} { set origins [expr {[dict exists $parameterOrigins $parameter] ? [dict get $parameterOrigins $parameter] : [::nsmcp::warningOriginEmpty]}] ::nsmcp::warningOriginAdd origins $requestMetadata 32 dict set parameterOrigins $parameter $origins } else {incr omittedOriginParameterRecords} } set pattern [dict get $normalized pattern] set key [list $severity $pattern] if {![dict exists $groups $key]} { if {[dict size $groups] >= 512} { incr ungrouped dict incr overflowFamilyTotals $family set priority [zlib crc32 [list $sampleSeed $offset]] set sample [expr {[dict exists $overflowFamilySamples $family] ? [dict get $overflowFamilySamples $family] : {entries {} maximum 0}}] if {[llength [dict get $overflowSample entries]] < 5 || $priority < [dict get $overflowSample maximum] || [llength [dict get $sample entries]] < 3 || $priority < [dict get $sample maximum]} { set fields [list offset number $offset endOffset number $examined timestamp number $seconds severity string $severity category string $category family string $family pattern string $pattern example string [string range $text 0 511] truncated boolean [expr {[dict get $event truncated] || [string length $text] > 512}]] ::nsmcp::summaryOverflowSample overflowSample 5 $priority $offset $fields ::nsmcp::summaryOverflowSample sample 3 $priority $offset $fields dict set overflowFamilySamples $family $sample } continue } dict set groups $key [dict create severity $severity pattern $pattern category $category parameter {} samples {} parameterCounts {} omittedParameterRecords 0 schedulerStats {} suppressedSuffixRecords 0 reportedSuppressedOccurrences 0 count 0 firstAt $seconds lastAt $seconds example [string range $text 0 2047] requestIdentifier [dict get $meta requestIdentifier] exampleTruncated false] } set group [dict get $groups $key] dict incr group count if {[dict get $normalized suppressedOccurrences] > 0} { dict incr group suppressedSuffixRecords dict incr group reportedSuppressedOccurrences [dict get $normalized suppressedOccurrences] } if {$category eq "duplicate-parameter"} { set parameter [dict get $normalized parameter] set counts [dict get $group parameterCounts] if {[dict exists $counts $parameter] || [dict size $counts] < 128} { dict incr counts $parameter dict set group parameterCounts $counts } else {dict incr group omittedParameterRecords} } if {$category eq "scheduler-lateness"} { dict set group scheduler [dict get $normalized scheduler] dict for {name value} [dict get $normalized timings] { if {[dict exists $group schedulerStats $name]} { set metric [dict get $group schedulerStats $name] dict incr metric count dict set metric total [expr {[dict get $metric total] + $value}] dict set metric min [expr {min([dict get $metric min],$value)}] dict set metric max [expr {max([dict get $metric max],$value)}] } else {set metric [dict create count 1 total $value min $value max $value]} dict set group schedulerStats $name $metric } } if {$category eq "duplicate-parameter" && [llength [dict get $group samples]] < 12 && [::nsmcp::logRecordParser validConnectionIdentifier [dict get $meta requestIdentifier]]} { set retainedForParameter 0 foreach retained [dict get $group samples] { if {[dict exists $retained parameter] && [dict get $retained parameter] eq $parameter} {incr retainedForParameter} } set sample [dict merge [dict create parameter $parameter requestIdentifier [dict get $meta requestIdentifier] timestamp $seconds] $requestMetadata] if {$retainedForParameter < 3 && $sample ni [dict get $group samples]} {dict lappend group samples $sample} } dict set group firstAt [expr {min([dict get $group firstAt],$seconds)}] dict set group lastAt [expr {max([dict get $group lastAt],$seconds)}] if {[dict get $event truncated] || [string length $text]>2048} { dict set group exampleTruncated true incr clipped } dict set groups $key $group } file stat ${:path} after seek $source 0 set stable [expr {[list $before(dev) $before(ino)] eq [list $after(dev) $after(ino)] && $after(size)>=$before(size) && [read $source [string length $prefix]] eq $prefix}] } finally { if {$reader ne ""} {$reader destroy} close $source } if {!$stable} {error "system log changed during summary scan; start a new summary"} set state {} if {$examined < $before(size)} { foreach name {groups matched ungrouped overflowSample overflowFamilySamples sampleSeed familyTotals overflowFamilyTotals applicationMarkers applicationSuppressed categoryTotals duplicateOrigins parameterOrigins omittedOriginParameterRecords previousByThread repeatMarkers reportedSuppressed attributedMarkers unattributedMarkers crossWindowMarkers repeatExamples unknownTime unrecognized examples severityTotals unknownTimeTotals pending clipped firstTime lastTime start examined prefix mutexTraces mutexGroups mutexUngrouped shortEntries untimestamped untimestampedExamples postgresRecords postgresExamples previousTimestamp pendingPostgres timestampedRecords earliestAt latestAt firstEntryAt lastEntryAt firstEntryOffset lastEntryOffset cachedStamp cachedSeconds} { dict set state $name [set $name] } dict set state identity [list $before(dev) $before(ino)] dict set state sourceBytes $before(size) } set exampleEntries {} set i 0 foreach example $examples {lappend exampleEntries $i object $example; incr i} set untimestampedEntries {} set i 0 foreach example $untimestampedExamples { lappend untimestampedEntries $i object $example incr i } set mutexEntries {}; set i 0 dict for {key group} $mutexGroups { lassign $key mutex operation metric lappend mutexEntries $i object [list mutexName string [$redactor message $mutex] operation string $operation metric string $metric count number [dict get $group count] minSeconds number [dict get $group min] maxSeconds number [dict get $group max] meanSeconds number [expr {[dict get $group total]/double([dict get $group count])}] negativeDurations number [dict get $group negative] firstOffset number [dict get $group firstOffset] lastOffset number [dict get $group lastOffset] maxOffset number [dict get $group maxOffset] maxLogThread string [dict get $group maxThread]] incr i } set postgresEntries {} set i 0 foreach example $postgresExamples { if {[lsearch -exact $example followingTimestamp]<0} {lappend example followingTimestamp string {}} lappend postgresEntries $i object $example incr i } set severityFields {} dict for {severity count} $severityTotals {lappend severityFields $severity number $count} set selectedFields {}; set i 0 foreach severity $severities {lappend selectedFields $i string $severity; incr i} set unknownFields {}; set unknownSelected 0 dict for {severity count} $unknownTimeTotals { lappend unknownFields $severity number $count incr unknownSelected $count } set ordered {} dict for {key group} $groups {lappend ordered [list [dict get $group count] $group]} set entries {} set index 0 set omittedGroups 0 set omittedGroupRecords 0 foreach pair [lsort -integer -decreasing -index 0 $ordered] { set group [lindex $pair 1] if {$index >= $maxGroups} { incr omittedGroups incr omittedGroupRecords [dict get $group count] continue } set fields {} foreach name {severity pattern category parameter example requestIdentifier} {lappend fields $name string [dict get $group $name]} foreach name {count firstAt lastAt suppressedSuffixRecords reportedSuppressedOccurrences} {lappend fields $name number [dict get $group $name]} if {[dict get $group category] eq "duplicate-parameter"} { set parameters {} dict for {parameter count} [dict get $group parameterCounts] {lappend parameters $parameter number $count} lappend fields parameterCounts object $parameters parameterCapacity number 128 omittedParameterRecords number [dict get $group omittedParameterRecords] parameterBreakdownComplete boolean [expr {[dict get $group omittedParameterRecords] == 0}] } if {[dict get $group category] eq "scheduler-lateness"} { set scheduler [dict get $group scheduler] set details [list jobId number [dict get $scheduler jobId] flags string [dict get $scheduler flags] intervalSeconds number [dict get $scheduler intervalSeconds]] set timings {} dict for {name metric} [dict get $group schedulerStats] { lappend timings $name object [list count number [dict get $metric count] minSeconds number [dict get $metric min] maxSeconds number [dict get $metric max] meanSeconds number [expr {[dict get $metric total]/double([dict get $metric count])}]] } lappend details timings object $timings lappend fields scheduler object $details } set samples {}; set sampleIndex 0 foreach sample [dict get $group samples] { set sampleFields [list requestIdentifier string [dict get $sample requestIdentifier] timestamp number [dict get $sample timestamp]] if {[dict exists $sample parameter]} {lappend sampleFields parameter string [dict get $sample parameter]} foreach name {authenticationState authenticationSource} { if {[dict exists $sample $name]} {lappend sampleFields $name string [dict get $sample $name]} } if {[dict exists $sample userAgent]} { lappend sampleFields userAgent string [dict get $sample userAgent] userAgentSource string system-log userAgentMayBeTruncated boolean [dict get $sample userAgentMayBeTruncated] } lappend samples $sampleIndex object $sampleFields incr sampleIndex } lappend fields requestSamples array $samples exampleTruncated boolean [dict get $group exampleTruncated] lappend entries $index object $fields incr index } # Observed entry times describe only bytes scanned so far, not rotation or # proof of complete coverage. File mtime is deliberately not used as a date. set bounds [list source string observed-timestamped-entries scope string scanned-file timestampedRecords number $timestampedRecords] foreach name {earliestAt latestAt firstEntryAt lastEntryAt firstEntryOffset lastEntryOffset} { set value [set $name] lappend bounds $name [expr {$value eq "" ? "null" : "number"}] $value } set logTimezone :localtime if {[info exists ::env(TZ)] && $::env(TZ) ne ""} {set logTimezone $::env(TZ)} # System-log rotations may be scheduled by application code or external # tools. nslog's logrollhour describes access logs, not this source. set rotation [list scheduleKnown boolean false schedule null {} lastVerifiedAt null {} timezone string $logTimezone onSignal boolean [ns_config -bool ns/parameters logrollonsignal [ns_config -bool ns/parameters logroll true]] filenameFormat string [ns_config ns/parameters logrollfmt {}] configurationScope string process-system-log limitations string {Signal handling and filename format are configuration, not proof of rotation. A daily schedule and last successful rotation time are not exposed by this interface; application or external rotation is possible.}] set categories {} dict for {category count} $categoryTotals {lappend categories $category number $count} set repeatFields {}; set repeatIndex 0 foreach example $repeatExamples {lappend repeatFields $repeatIndex object $example; incr repeatIndex} set familyFields {}; set overflowFamilyFields {}; set familySamples {}; set familyIndex 0 dict for {family count} $familyTotals {lappend familyFields $family number $count} dict for {family count} $overflowFamilyTotals { lappend overflowFamilyFields $family number $count if {$count == 0} {continue} set examples [::nsmcp::summaryOverflowExamples [dict get $overflowFamilySamples $family]] set rows {}; set n 0 foreach example $examples {lappend rows $n object $example; incr n} lappend familySamples $familyIndex object [list family string $family count number $count exampleLimit number 3 examples array $rows omittedExampleRecords number [expr {$count-[llength $examples]}]] incr familyIndex } set overflowExamples [::nsmcp::summaryOverflowExamples $overflowSample] set overflowFields {}; set overflowIndex 0 foreach example $overflowExamples {lappend overflowFields $overflowIndex object $example; incr overflowIndex} set dedup [list applicationMarkerRecords number $applicationMarkers applicationReportedSuppressedOccurrences number $applicationSuppressed configuredNow boolean [ns_config -bool ns/parameters logdeduplicate false] markerRecords number $repeatMarkers messageRecords number [expr {$matched-$repeatMarkers}] reportedSuppressedOccurrences number $reportedSuppressed attributedMarkers number $attributedMarkers unattributedMarkers number $unattributedMarkers crossWindowMarkers number $crossWindowMarkers examples array $repeatFields limitations string {Severity totals count physical records including repeat markers, not expanded occurrences. Markers inherit the preceding message severity. Native sameLineCount includes the originally written message; reported suppressed occurrences subtract one per marker. A marker timestamp does not locate suppressed occurrences in time or identify their requests. Attribution uses a bounded per-process/thread map over selected severities; rotation, map limits and missing prior entries prevent attribution. Current configuration is not historical configuration. Recognized trailing application/core suppression suffixes are counted separately in applicationMarkerRecords and applicationReportedSuppressedOccurrences, never added to severity totals or native reportedSuppressedOccurrences. Their suppressed event times and severity history are not established.}] set parameterFields {}; set parameterIndex 0 dict for {parameter origins} $parameterOrigins { lappend parameterFields $parameterIndex object [concat [list parameter string $parameter] [::nsmcp::warningOriginFields $origins 32]] incr parameterIndex } set originFields [list scope string selected-window-duplicate-records overall object [::nsmcp::warningOriginFields $duplicateOrigins 128] parameters array $parameterFields parameterCapacity number 128 omittedParameterRecords number $omittedOriginParameterRecords parameterBreakdownComplete boolean [expr {$omittedOriginParameterRecords == 0}] limitations string {Aggregates include all selected timestamped duplicate-parameter records in the scanned window before group/evidence limits, including grouping overflow. Counts are warning records, not distinct requests or users. Authentication is derived from the explicit OpenACS user_id diagnostic before redaction (0 anonymous, positive decimal authenticated, missing/invalid unknown); actual user IDs are never retained. User-Agents are decoded and redacted before grouping and may merge after redaction or truncation. Missing, empty or invalid User-Agents are unknown. Cardinality-overflow records, retained ranking omissions and truncated prefixes are separate. Rankings are not global rankings when omittedUserAgentRecords is nonzero. Self-reported User-Agents and pool membership do not verify bot/human identity. No access cache or peer addresses are required; file/window coverage limitations still apply.}] set result [list {} object [list server string [ns_info server] scope string process from number $from to number $to timezone string [dict get $options timezone] timestampTimezone string [clock format [clock seconds] -format %Z] selectedSeverities array $selectedFields severityTotals object $severityFields matchedRecords number $matched ungroupedRecords number $ungrouped groupingComplete boolean [expr {$ungrouped == 0}] groupCapacity number 512 familyTotals object $familyFields overflowFamilyTotals object $overflowFamilyFields overflowSamplesByFamily array $familySamples overflowFamilyExampleLimit number 3 overflowSamplingMethod string deterministic-crc32-priority overflowExamples array $overflowFields overflowExampleLimit number 5 overflowExamplesOmittedRecords number [expr {max(0,$ungrouped-[llength $overflowExamples])}] omittedGroups number $omittedGroups omittedGroupRecords number $omittedGroupRecords categoryTotals object $categories duplicateParameterOrigins object $originFields deduplication object $dedup groups array $entries grouping string {Known duplicate-parameter warnings share one group per severity with up to 128 parameterCounts and explicit omittedParameterRecords. Scheduler lateness groups retain job ID, flags and interval; varying durations are aggregated separately. Recognized suppression suffixes do not fragment keys. familyTotals classify every selected record into a fixed broad message family (other is explicit), independently of detailed-group capacity. overflowFamilyTotals are exact counts of excess records by family. These are physical record classifications, not verified causes or incidents. overflowExamples retain up to five redacted hash-priority samples across the examined overflow stream; overflowSamplesByFamily retains up to three per fixed family, with explicit omittedExampleRecords. Sampling uses deterministic CRC-32 priorities from fixed snapshot identity, bounds and byte offsets, retains state across scan continuation, and returns samples in byte order. It is not cryptographic or a guaranteed statistically unbiased sample; per-family examples are stratified, not proportional frequency evidence. Use exact family counts for distribution claims. Missing examples do not mean absent records. Up to 512 distinct groups are counted independently of maxGroups; the largest counted groups are returned. omittedGroupRecords are classified but not returned; ungroupedRecords exceeded internal capacity. Rankings are globally complete only when groupingComplete=true. Unknown message keys use redacted first lines with numeric tokens and hex addresses normalized.} coverage object [list logBounds object $bounds rotation object $rotation sourceBytes number $before(size) examinedFromOffset number $start examinedToOffset number $examined sourceFullyExamined boolean [expr {$stable && $start==0 && $examined==$before(size)}] sourceChanged boolean [expr {!$stable}] historyUnreadBytes number $start unreadBytes number [expr {max(0,$before(size)-$examined)}] unknownTimeErrors number $unknownTime unknownTimeRecords number $unknownSelected unknownTimeSeverityTotals object $unknownFields unrecognizedRecords number $unrecognized unrecognizedExamples array $exampleEntries mutexTraceRecords number $mutexTraces mutexTraceGroups array $mutexEntries mutexTraceUngroupedRecords number $mutexUngrouped shortLogRecords number $shortEntries untimestampedRecords number $untimestamped untimestampedExamples array $untimestampedEntries postgresRecords number $postgresRecords postgresExamples array $postgresEntries diagnosticScope string scanned-file clippedExamples number $clipped pendingFinalRecord boolean $pending rotationsExamined boolean false windowComplete boolean false limitations string {Only the configured current system log is examined. Rotated files and time before this file are not certified. Counts include observed final records, whose continuations may still grow. Expected ns:sched unlock/hold diagnostics are omitted entirely. Other mutex traces are grouped by mutex name, operation and wait/hold metric over every scanned trace (up to 128 groups; excess records are counted in mutexTraceUngroupedRecords). Durations are individual observations, not additive wall time or proof of contention. Negative durations indicate timing diagnostics, not valid waits. Mutex traces and short system-log entries have no timestamps; diagnostic counters and examples cover the scanned file, not necessarily the requested time window. PostgreSQL DETAIL, HINT and CONTEXT fields are grouped with their diagnostic. postgresExamples provides explicitly inferred neighboring timestamp bounds, which may be missing or out of order under concurrent logging; these are contextual estimates, not guaranteed event times or request attribution. No timestamps are inherited from adjacent entries. Untimestamped selected severities contribute to unknownTimeRecords and unknownTimeSeverityTotals, not window severityTotals; unknownTimeErrors retains only Error/Fatal/Bug.}]]] if {$state ne ""} {lappend result $state} return $resultXQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)