Private/TimedEvent.ps1
|
# The Timed Event primitive. # # A Timed Event is one thing a Check read out of a log that happened at a time: a # connection to a Server that was lost at 08:05 on the third, the machine's network going # away, a program that crashed, a sign-in. It is what the Report's 30 days are drawn from, # beside the Findings that count the same events in words. See ADR-0010. # # Not a Measurement: a Measurement is one number for one subject and one metric, and has # no time. Not a row of events.csv either: that is every event the Checks read, as Windows # wrote it, and says neither which lane it belongs in nor which Finding judged its kind - # only the Judge knows that (ADR-0002). A Judge returns it among its Findings, as it does # its Measurements, and the Run tells the three apart by type. # # Lane and Mark are closed vocabularies, for the reason a Measurement's Metric is one: a # misspelt Mark would be a lane of its own. # Where the timeline draws it, and what it is there. A Server's lane holds what happened # to the connections to that Server; the machine's lane its own network going away; the # markers what happened to programs and Users on the same axis; and a log's reach is how # far back the events of that log can be there at all. $script:TimedEventMarks = [ordered]@{ Server = @('Lost', 'Failed', 'ShareNotFound', 'Context') Machine = @('Lost', 'Context') Marker = @('Crash', 'Hang', 'Start', 'SignIn') Log = @('Reaches', 'Unread') } function New-TimedEvent { <# .SYNOPSIS One thing that happened at a time, as the Report's timeline draws it. Pure. .DESCRIPTION Validated here, like a Finding: an unknown Lane, or a Mark that is not of its Lane, throws where the Judge is written. Every event has a time, but for a log that could not be read: that one reaches back to nothing. .PARAMETER Time When it happened; for a log's reach, the time of the oldest record the log holds. .PARAMETER Lane Server, Machine, Marker or Log. .PARAMETER Mark What it is in its Lane. Server: Lost, Failed, ShareNotFound, Context. Machine: Lost, Context. Marker: Crash, Hang, Start, SignIn. Log: Reaches, Unread. Context is an event shown and not counted: a resume, a link that came back. .PARAMETER Subject The Server as the event named it - a name, an FQDN or an address -, the program that crashed, the account that signed in. Empty for the machine itself. .PARAMETER Address The Server's address, where the event or a lookup the Kind already made has it. Not looked up for this: the timeline merges a Server by its address first and its name second, as the path does. .PARAMETER Detail What it was, in words: the kind of the event as the Report calls it. .PARAMETER Log The event log it was read from, which a log's reach is about. .PARAMETER Finding The Finding that judged this kind of event, whose Severity the timeline colours it with: the Finding itself, or its Id as it arrived from the Elevated Part. Nothing where no Finding judges it, and the timeline then draws it grey. #> [CmdletBinding()] [OutputType([psobject])] param( [AllowNull()]$Time, [Parameter(Mandatory)][AllowEmptyString()][string]$Lane, [Parameter(Mandatory)][AllowEmptyString()][string]$Mark, [AllowNull()][AllowEmptyString()][string]$Subject = '', [AllowNull()][AllowEmptyString()][string]$Address = '', [AllowNull()][AllowEmptyString()][string]$Detail = '', [AllowNull()][AllowEmptyString()][string]$Log = '', [AllowNull()]$Finding ) $laneValue = Get-MeasurementVocabularyValue -Field 'Lane of a Timed Event' -Value $Lane -Permitted @($script:TimedEventMarks.Keys) $marks = $script:TimedEventMarks[$laneValue] $markValue = @($marks | Where-Object { $_ -eq $Mark } | Select-Object -First 1) if (-not $markValue.Count) { throw ("'{0}' is no Mark of the Lane {1}. Permitted: {2}." -f $Mark, $laneValue, ($marks -join ', ')) } $markValue = $markValue[0] # A time or nothing, and nothing only where a log reaches back to nothing. Text that is # no time is refused rather than placed at a guess. $at = $null if ($null -ne $Time -and "$Time".Trim()) { $at = ConvertTo-DataTime $Time if ($null -eq $at) { throw ("'{0}' is not a time. A Timed Event is placed by its time." -f $Time) } } if ($null -eq $at -and -not ($laneValue -eq 'Log' -and $markValue -eq 'Unread')) { throw ("A Timed Event {0} {1} needs a time: the timeline cannot place it without one." -f $laneValue, $markValue) } if ($laneValue -eq 'Server' -and -not "$Subject".Trim() -and -not "$Address".Trim()) { throw 'A Timed Event of a Server names the Server, by its name or its address: it has no lane to be drawn in otherwise.' } if ($laneValue -eq 'Log' -and -not "$Log".Trim()) { throw "A log's reach names the log it is about." } [pscustomobject]@{ PSTypeName = 'Gutcheck.TimedEvent' Time = $at Lane = $laneValue Mark = $markValue Subject = "$Subject".Trim() Address = "$Address".Trim() Detail = "$Detail".Trim() Log = "$Log".Trim() Finding = ConvertTo-FindingId -Finding $Finding -Of 'a Timed Event' # Stamped on by the Run, as on a Finding. See Set-CheckOrigin. CheckName = '' App = '' } } function ConvertTo-LogCoverageTimedEvent { <# .SYNOPSIS How far back each log reaches, as the timeline marks it: one Log Reaches per log that was read and said, one Log Unread per log this Run's rights were refused. Pure. .DESCRIPTION A log overwrites its oldest records, so one that reaches back a week says nothing of the three weeks before; and a log that was not read says nothing at all. An empty stretch on the timeline is otherwise read as a quiet one. A log that would not say how far it reaches - an empty one - is left out: nothing says whether it has been cleared or nothing ever happened. .PARAMETER Coverage { Log, Oldest } per log, as the NetworkDisruption and Logon Gatherers return it. .PARAMETER Unreadable The logs this Run's rights were refused, by name. #> [CmdletBinding()] [OutputType([psobject])] param([AllowNull()][AllowEmptyCollection()]$Coverage, [AllowNull()][AllowEmptyCollection()]$Unreadable) foreach ($entry in @($Coverage | Where-Object { $_ })) { $log = "$(Get-DataProperty $entry 'Log')".Trim() $oldest = ConvertTo-DataTime (Get-DataProperty $entry 'Oldest') if (-not $log -or $null -eq $oldest) { continue } New-TimedEvent -Time $oldest -Lane Log -Mark Reaches -Log $log } foreach ($log in @($Unreadable | ForEach-Object { "$_".Trim() } | Where-Object { $_ })) { New-TimedEvent -Lane Log -Mark Unread -Log $log } } |