Class ::nsmcp::LogService (public)
::nx::Class ::nsmcp::LogService
Defined in /usr/local/ns/tcl/nsmcp/lib/log-tools.tcl
- Testcases:
- No testcase defined.
Source code: :property {analytics {}} :property correlation:required :property authorizer:required :property redactor:required :property peerRedactor:required :property store:required :property queue:required :property {lifetime:integer 900} :property {maxJobs:integer 128} :variable mutex :method init {} { if {${:lifetime} < 1 || ${:maxJobs} < 1} {error "positive log service limits required"} set :mutex [ns_mutex create] if {${:queue} ni [ns_job queues]} {ns_job create ${:queue} 1} } :method require {context} {${:authorizer} require $context diagnostics.logs.read} :method owned {context record} { return [expr {[dict get $record principal] eq [$context principal] && [dict get $record server] eq [$context server] && [dict get $record expires] > [clock seconds]}] } :public method start {context operation {identifier {}}} { :require $context if {$operation ni {errors requestContext accessSummary}} {error "invalid log operation"} if {$operation eq "requestContext" && ![::nsmcp::logRecordParser validRequestIdentifier $identifier]} { return -code error -errorcode {NSMCP RPC -32602} "Invalid request identifier" } set requiresPeers [expr {$operation eq "accessSummary" && ([dict exists $identifier peer] || ([dict exists $identifier includePeerAddresses] && [dict get $identifier includePeerAddresses]))}] if {$requiresPeers} {${:authorizer} require $context diagnostics.logs.peers.read} ns_mutex lock ${:mutex} try { foreach key [nsv_array names ${:store}] { set record [nsv_get ${:store} $key] if {[dict get $record expires] <= [clock seconds] && [dict get $record status] in {complete failed}} { nsv_unset ${:store} $key } } if {[llength [nsv_array names ${:store}]] >= ${:maxJobs}} {error "log job limit reached"} set handle [ns_crypto::randombytes -encoding hex 32] nsv_set ${:store} $handle [dict create principal [$context principal] server [$context server] expires [expr {[clock seconds]+${:lifetime}}] status queued mappings {} requiresPeers $requiresPeers] try { ns_job queue -detached ${:queue} [list [self] run $handle $operation $identifier] } on error {message options} { nsv_unset ${:store} $handle return -options $options $message } } finally {ns_mutex unlock ${:mutex}} return [list {} object [list jobHandle string $handle status string queued server string [ns_info server]]] } :method summary {snapshot mappingsVar} { upvar 1 $mappingsVar mappings set handle [ns_crypto::randombytes -encoding hex 32] dict set mappings $handle [dict get $snapshot resultHandle] set triples [list resultHandle string $handle server string [ns_info server]] foreach key {resultCount generation sourceBytes} {lappend triples $key number [dict get $snapshot $key]} foreach key {scanIncomplete pending cacheFull} {lappend triples $key boolean [dict get $snapshot $key]} return $triples } :public method run {handle operation identifier} { ns_mutex lock ${:mutex} try { if {![nsv_exists ${:store} $handle]} return set record [nsv_get ${:store} $handle] dict set record status running nsv_set ${:store} $handle $record } finally {ns_mutex unlock ${:mutex}} set mappings {} try { if {$operation eq "errors"} { set data [:summary [${:correlation} errors] mappings] } elseif {$operation eq "accessSummary"} { if {${:analytics} eq ""} {error "access analytics is not configured"} ${:analytics} ingest [dict get $identifier from] set data [::nsmcp::accessSummaryTriples [${:analytics} summary $identifier]] } else { set context [${:correlation} requestContext $identifier] set data [list server string [ns_info server] requestIdentifier string $identifier system object [:summary [dict get $context system] mappings]] set access {}; set index 0 dict for {source snapshot} [dict get $context access] { lappend access $index object [:summary $snapshot mappings] incr index } lappend data access array $access } dict set record status complete dict set record data $data dict set record mappings $mappings } on error {message options} { dict set record status failed # No source paths, log contents, or exception details in client errors. ns_log error "nsmcp: background log operation failed: $message" } ns_mutex lock ${:mutex} try {nsv_set ${:store} $handle $record} finally {ns_mutex unlock ${:mutex}} } :public method poll {context handle} { :require $context ns_mutex lock ${:mutex} try { if {![nsv_exists ${:store} $handle] || ![:owned $context [nsv_get ${:store} $handle]]} { return -code error -errorcode {NSMCP RPC -32602} "Unknown or expired job handle" } set record [nsv_get ${:store} $handle] if {[dict exists $record requiresPeers] && [dict get $record requiresPeers]} {${:authorizer} require $context diagnostics.logs.peers.read} set data [list jobHandle string $handle status string [dict get $record status] server string [ns_info server]] if {[dict get $record status] eq "complete"} {lappend data result object [dict get $record data]} if {[dict get $record status] eq "failed"} {lappend data error string {Log search failed; consult the server log.}} return [list {} object $data] } finally {ns_mutex unlock ${:mutex}} } :public method results {context handle cursor limit includePeers} { :require $context if {$includePeers} {${:authorizer} require $context diagnostics.logs.peers.read} set internal {} ns_mutex lock ${:mutex} try { foreach key [nsv_array names ${:store}] { set record [nsv_get ${:store} $key] if {[:owned $context $record] && [dict exists $record mappings $handle]} { set internal [dict get $record mappings $handle]; break } } } finally {ns_mutex unlock ${:mutex}} if {$internal eq ""} { return -code error -errorcode {NSMCP RPC -32602} "Unknown or expired result handle" } set page [${:correlation} results $internal $cursor $limit] set policy [expr {$includePeers ? ${:peerRedactor} : ${:redactor}}] set page [$policy page $page] set records {}; set index 0 foreach entry [dict get $page records] { set fields {} foreach key {timestamp processThread logThread severity requestIdentifier peerAddress method url protocol message pool timingSource} { if {[dict exists $entry $key]} {lappend fields $key string [dict get $entry $key]} } foreach key {offset endOffset status responseBytes} { set value [dict get $entry $key] lappend fields $key [expr {$value eq "" ? "string" : "number"}] $value } foreach key {startTime acceptTime queueTime filterTime runTime} { if {[dict exists $entry $key]} {lappend fields $key number [dict get $entry $key]} } foreach key {complete truncated orphanContinuation redacted} {lappend fields $key boolean [dict get $entry $key]} lappend records $index object $fields incr index } set data [list server string [ns_info server] resultHandle string $handle records array $records nextCursor string [dict get $page nextCursor]] foreach key {resultCount generation} {lappend data $key number [dict get $page $key]} foreach key {more scanIncomplete pending cacheFull} {lappend data $key boolean [dict get $page $key]} return [list {} object $data] }XQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)