Eigenverft.Manifested.Drydock.Logging.ps1
function Write-ConsoleLog { <# .SYNOPSIS Minimal console logger for PowerShell 5 (space layout), UTC by default. .DESCRIPTION Emits: "[yyyy-MM-dd HH:mm:ss:fff LEVEL] [file.ps1] [function] message" - Time is UTC by default. Use -LocalTime to log in local time. - Auto-resolves caller script file and function from the call stack. - Plain text, deterministic output (no colors). .PARAMETER Message The message to log. Mandatory and non-empty. .PARAMETER Level Severity tag. One of: TRC, DBG, INF, WRN, ERR, FTL. Defaults to INF. .PARAMETER LocalTime Switch. When present, uses local time instead of UTC. .EXAMPLE function MakeDirs { Write-ConsoleLog -Level INF -Message 'Dirscreated.' } MakeDirs # -> [2025-10-12 04:47:17:265 INF] [out.ps1] [makedirs] Dirscreated. # (timestamp is in UTC) .EXAMPLE Write-ConsoleLog -Message 'customtext.' -Level WRN -LocalTime # -> [2025-10-12 06:47:17:265 WRN] [fileofthefunction] [functionnameinfile] customtext. # (timestamp is in local time) .NOTES Help is inside the function so it travels when copied. #> [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [string]$Message, [Parameter()] [ValidateSet('TRC','DBG','INF','WRN','ERR','FTL')] [string]$Level = 'INF', [Parameter()] [switch]$LocalTime ) # Reviewer: Deterministic timestamp; UTC default unless -LocalTime is set. $now = if ($LocalTime.IsPresent) { Get-Date } else { [DateTime]::UtcNow } $ts = $now.ToString('yyyy-MM-dd HH:mm:ss:fff') # Reviewer: Get first caller that's not this function. $self = $MyInvocation.MyCommand.Name $caller = Get-PSCallStack | Where-Object { $_.FunctionName -ne $self } | Select-Object -First 1 if (-not $caller) { $caller = [pscustomobject]@{ ScriptName = $PSCommandPath FunctionName = '<scriptblock>' Location = $null Command = $null } } # Prefer ScriptName; fall back to Location; else 'console'. $file = if ($caller.ScriptName) { Split-Path -Leaf $caller.ScriptName } elseif ($caller.Location -and $caller.Location -match '^(.*?):\d+') { Split-Path -Leaf $Matches[1] } else { 'console' } $func = if ($caller.FunctionName) { $caller.FunctionName } elseif ($caller.Command) { $caller.Command } else { '<scriptblock>' } $lvl = $Level.ToUpperInvariant() $line = "[{0} {1}] [{2}] [{3}] {4}" -f $ts, $lvl, $file, $func.ToLower(), $Message # Reviewer: Write-Host ensures immediate console rendering in PS5. Write-Host $line } |