Class ::nsmcp::AccessFileSummaryTool (public)

 ::nx::Class ::nsmcp::AccessFileSummaryTool[i]

Defined in /usr/local/ns/tcl/nsmcp/log-traffic-summary.tcl

Testcases:
No testcase defined.
Source code:
    :property {name access_log_summary}
    :property {description {Scan configured access-log files in a background job, independent of cache retention. Default selection ordered reads endpoint records and seeks by request-start timestamps under an explicit approximate ordering assumption, with five seconds of selection padding filtered against the original bounds; completion timestamps are fallback values. Observed inversions exceeding five seconds or unusable probes fall back to bounded scanning. This does not certify unsampled ordering or include every delayed completion. Use selection exhaustive for full verification. For prompt origin hints start with mode evidence (default 5000 matching records, at most 50000 parsed candidates, 32MiB byte budget, maxRecords 1..10000). Its bounded prefix rankings describe only the observed evidence span, not the entire requested interval; follow with mode summary only when full counts are needed. Poll log_results with jobHandle until complete; the result is a summary, not record pages. Omit pathLiteral for all parsed request paths, including empty or non-slash targets. Use pathLiteral ".php" for case-sensitive literal substring matching on the logged queryless path, never query values; no regexp or URL decoding. Defaults to today UTC; set timezone for today/yesterday, or lastMinutes, or explicit from/to. includeRotations defaults true and includes recognized numbered/dated siblings and gzip rotations. ASCII literals use bounded ns_fseekchars candidate searches; Unicode/ANSI cases retain conservative parsing. In exhaustive selection, a bounded server-local time index stores exhaustive start-time bounds for byte ranges independently of path filters. It skips unrelated rotations and seeks over disjoint ranges without assuming timestamp order. Cold scans build the index; changed/append-only snapshots verify complete cached ranges by digest before exclusion, counting verification reads against the scan budget. Unknown, malformed, oversized and unindexed ranges are scanned. Cache metadata is bounded and lost on process restart; filenames and endpoint samples never prove absence. coverage.rotationsSkippedOutsideWindow and eligibleFilesFullyExamined distinguish verified pruning from missing coverage. Parsing diagnostics in literal mode concern candidates and fallback rows, not every unrelated record. Fixed snapshots, bounded streaming/decompression, file/byte/cardinality limits and changed/unreadable files are reported. Counts use logged request start time, falling back to completion time; end is exclusive. Returns all-match totals before bounded path, method, status, pool, UTC-hour, redacted User-Agent and address-free peer-concentration rankings, authentication states, timing totals and distributed samples including status below 400. maxGroups 1..50 limits rankings, not scanning. Inspect breakdowns capacityOmittedRecords versus rankedOmittedRecords, unknown/truncated User-Agents and distinct-peer lower bounds. Counts are access records, not unique requests/incidents. No user IDs, peer addresses, query strings or caller-supplied filenames are exposed. sourceFullyExamined does not certify full-day retention; windowComplete remains false without verified rotation history.}}
    :property {inputSchema {{"type":"object","properties":{"selection":{"type":"string","enum":["ordered","exhaustive"],"default":"ordered","description":"ordered uses endpoint start times and byte bisection, assuming sorted start times; exhaustive retains full scanning/index verification"},"mode":{"type":"string","enum":["summary","evidence"],"default":"summary"},"maxRecords":{"type":"integer","minimum":1,"maximum":10000,"default":5000,"description":"Maximum matching records in evidence mode; a separate candidate ceiling is max(50000,10*maxRecords). Results are a bounded prefix, not an unbiased window distribution"},"pathLiteral":{"type":"string","minLength":1,"maxLength":256,"description":"Case-sensitive literal substring in the logged queryless path, at most 256 UTF-8 bytes; no regexp or decoding"},"includeRotations":{"type":"boolean","default":true},"period":{"type":"string","enum":["today","yesterday"]},"timezone":{"type":"string"},"lastMinutes":{"type":"integer","minimum":1,"maximum":10080},"from":{"type":"integer","minimum":0},"to":{"type":"integer","minimum":1},"maxGroups":{"type":"integer","minimum":1,"maximum":50}},"additionalProperties":false}}}
    :method options {arguments} {
        if {[lindex $arguments 1] ne "object"} {error "Expected arguments object"}
        set base {}; set seen {}; set extras {pathLiteral {} includeRotations true selection ordered mode summary maxRecords 5000}
        foreach {key type value} [lindex $arguments 2] {
            if {$key in $seen} {error "Duplicate access summary argument"}; lappend seen $key
            if {$key in {selection mode}} {
                if {$type ne "string" || ($key eq "selection" && $value ni {ordered exhaustive}) || ($key eq "mode" && $value ni {summary evidence})} {error "Invalid access summary selection or mode"}
                dict set extras $key $value
            } elseif {$key eq "maxRecords"} {
                if {$type ne "number" || ![string is integer -strict $value] || $value<1 || $value>10000} {error "maxRecords must be 1..10000"}
                dict set extras $key $value
            } elseif {$key eq "pathLiteral"} {
                if {$type ne "string" || [string length [encoding convertto utf-8 $value]]<1
                    || [string length [encoding convertto utf-8 $value]]>256 || [regexp {[\x00-\x1f\x7f]} $value]} {error "pathLiteral must be 1..256 UTF-8 bytes without control characters"}
                dict set extras $key $value
            } elseif {$key eq "includeRotations"} {
                if {$type ne "boolean"} {error "includeRotations must be boolean"}
                dict set extras $key [expr {$value eq "true"}]
            } elseif {$key in {period timezone lastMinutes from to maxGroups}} {
                lappend base $key $type $value
            } else {error "Unknown access summary argument"}
        }
        return [dict merge [next [list [list {} object $base]]] $extras]
    }
    :public method execute {context arguments} {
        return [${:service} start $context accessFileSummary [:options $arguments]]
    }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: