Assets/OpsUtils.psm1
|
# OpsUtils.psm1 $script:OpsLogLevels = @{ "DEBUG" = 0 "INFO" = 1 "WARNING" = 2 "ERROR" = 3 } function Write-OpsLog { <# .SYNOPSIS Writes a message to the transcript and to the job's NDJSON history file. .PARAMETER Message The message to log. .PARAMETER Level Severity. Messages below $env:OPS_LOG_LEVEL are dropped. #> param ( [Parameter(Mandatory=$true)] [AllowEmptyString()] [string]$Message, [Parameter(Mandatory=$false)] [ValidateSet("DEBUG", "INFO", "WARNING", "ERROR")] [string]$Level = "INFO" ) # An unrecognised OPS_LOG_LEVEL used to index to $null, which made every # comparison true (PowerShell) or false (Node) instead of falling back. $currentLevel = $env:OPS_LOG_LEVEL if ([string]::IsNullOrWhiteSpace($currentLevel) -or -not $script:OpsLogLevels.ContainsKey($currentLevel.ToUpperInvariant())) { $currentLevel = "INFO" } else { $currentLevel = $currentLevel.ToUpperInvariant() } if ($script:OpsLogLevels[$Level] -lt $script:OpsLogLevels[$currentLevel]) { return } $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMsg = "[$timestamp] [$Level] $Message" Write-Host $logMsg # JSON Logging (newline-delimited JSON, one object per line) if (-not [string]::IsNullOrWhiteSpace($env:OPS_LOG_FILE)) { $jsonLog = [pscustomobject]@{ Timestamp = $timestamp Level = $Level Message = $Message } | ConvertTo-Json -Compress # Add-Content defaults to ASCII on PS 5.1, which mangles non-ASCII # messages into '?'. Retry briefly: concurrent runs of different jobs # share the directory and a reader may hold the file open. $written = $false for ($attempt = 0; $attempt -lt 5 -and -not $written; $attempt++) { try { Add-Content -Path $env:OPS_LOG_FILE -Value $jsonLog -Encoding UTF8 -ErrorAction Stop $written = $true } catch { Start-Sleep -Milliseconds 100 } } if (-not $written) { Write-Warning "Could not append to history log '$env:OPS_LOG_FILE'." } } } Export-ModuleMember -Function Write-OpsLog |