Public/Write-KritLog.ps1
|
function Start-KritLogSession { <# .SYNOPSIS Starts a PSFramework log session under the Kritical namespace. Falls back to plain file logging if PSFramework isn't loaded. #> [CmdletBinding()] [OutputType([pscustomobject])] param( [string] $Name = 'Krit', [string] $LogDir = (Join-Path $env:LOCALAPPDATA 'Kritical/Logs'), [string] $Provider = 'logfile' ) New-Item -ItemType Directory -Path $LogDir -Force -ErrorAction SilentlyContinue | Out-Null $usePsf = $false try { if (Get-Module -ListAvailable -Name PSFramework) { Import-Module PSFramework -Force -ErrorAction Stop $usePsf = $true $set = @{ Name = $Name FilePath = (Join-Path $LogDir ("$Name-{0:yyyyMMdd}.log" -f (Get-Date))) FileType = 'CMTrace' Enabled = $true } Set-PSFLoggingProvider @set -Provider $Provider -ErrorAction Stop } } catch { $usePsf = $false } [pscustomobject]@{ SessionName = $Name LogDir = $LogDir Backend = if ($usePsf) { 'PSFramework' } else { 'plain-file' } Started = (Get-Date).ToUniversalTime() } } function Stop-KritLogSession { [CmdletBinding()] param([string] $Name = 'Krit') try { if (Get-Module PSFramework) { Set-PSFLoggingProvider -Name $Name -Enabled $false -ErrorAction SilentlyContinue } } catch { } Get-PSFMessage -ErrorAction SilentlyContinue | Out-Null Wait-PSFMessage -ErrorAction SilentlyContinue } function Write-KritLog { <# .SYNOPSIS Structured logger. Uses PSFramework's Write-PSFMessage when available, else writes to the host + a plain log file under %LOCALAPPDATA%\Kritical\Logs. .EXAMPLE Write-KritLog -Level Info -Message 'Hardening pass starting' -Tag 'krit-harden','phase-1' #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Message, [ValidateSet('Debug','Verbose','Info','Warning','Error','Critical')] [string] $Level = 'Info', [string[]] $Tag, [hashtable] $Data, [string] $LogDir = (Join-Path $env:LOCALAPPDATA 'Kritical/Logs') ) $usePsf = Get-Module -Name PSFramework -ErrorAction SilentlyContinue if ($usePsf) { $psfLevel = switch ($Level) { 'Debug' {'Debug'} 'Verbose' {'Verbose'} 'Info' {'Host'} 'Warning' {'Warning'} 'Error' {'SomewhatVerbose'} 'Critical' {'Critical'} default {'Host'} } try { $params = @{ Message = $Message; Level = $psfLevel } if ($Tag) { $params.Tag = $Tag } if ($Data) { $params.Data = $Data } Write-PSFMessage @params return } catch { } } # Plain fallback New-Item -ItemType Directory -Path $LogDir -Force -ErrorAction SilentlyContinue | Out-Null $ts = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $entry = [ordered]@{ ts=$ts; level=$Level; msg=$Message } if ($Tag) { $entry.tag = $Tag } if ($Data) { $entry.data = $Data } $line = ($entry | ConvertTo-Json -Compress) $file = Join-Path $LogDir ("krit-{0:yyyyMMdd}.jsonl" -f (Get-Date)) Add-Content -LiteralPath $file -Value $line -Encoding UTF8 $color = switch ($Level) { 'Error' {'Red'} 'Critical' {'Red'} 'Warning' {'Yellow'} 'Debug' {'DarkGray'} default {'Gray'} } Write-Host ("[$Level] $Message") -ForegroundColor $color } |