Public/Write-Log.ps1
|
function Write-Log { <# .SYNOPSIS Log a message to console and/or a log file with levels and auto-rotation. .PARAMETER LogLocation Optional. Where log files will be stored. Defaults to caller script directory or current location. .PARAMETER Message The message to log. .PARAMETER LogFileName Optional. The log file name (default: 'activity.log'). .PARAMETER Level The log level (INFO, SUCCESS, WARNING, DEBUG, ERROR). Default is INFO. .PARAMETER WriteToFile If specified, the message will be written to the log file. .PARAMETER WriteOnlyToFile If specified, the message will be written only to the log file and not output to the console. .PARAMETER MaxFileSizeKB Optional. Max file size (in KB) before rotation; default 512. .PARAMETER AsJson Optional. Emit JSON Lines to file/console: {"timestamp":"...","level":"...","message":"..."}. .PARAMETER PassThru Optional. Return a PSCustomObject with Timestamp, Level, Message, Path. .PARAMETER Color If specified, colorize console output based on log level. .EXAMPLE Write-Log -Message "Hello, world!" -Level INFO -WriteToFile Writes to console and to '.\activity.log' under the caller script folder or current directory. #> [CmdletBinding()] param ( [string] $LogLocation, # optional [Parameter(Mandatory)][string] $Message, [ValidatePattern('\S')] [string] $LogFileName = 'activity.log', # default here (really optional) [ValidateSet('INFO', 'SUCCESS', 'WARNING', 'DEBUG', 'ERROR')] [string] $Level = 'INFO', [switch] $WriteToFile, [switch] $WriteOnlyToFile, [int] $MaxFileSizeKB = 512, [switch] $AsJson, [switch] $PassThru, [switch] $Color ) # Sanitize/validate file name (avoid illegal characters across platforms) $invalid = [IO.Path]::GetInvalidFileNameChars() if ($LogFileName.IndexOfAny($invalid) -ge 0) { throw "LogFileName contains invalid characters." } # Resolve (and ensure) the log directory even when not provided try { $resolvedLogDir = Resolve-LogLocation -LogLocation $LogLocation -EnsureExists } catch { Write-Error $_ return } $logFilePath = Join-Path -Path $resolvedLogDir -ChildPath $LogFileName # Rotate if size >= threshold if ($WriteToFile -and (Test-Path -LiteralPath $logFilePath)) { try { $fileInfo = Get-Item -LiteralPath $logFilePath -ErrorAction Stop $threshold = [math]::Max(1, $MaxFileSizeKB) * 1KB if ($fileInfo.Length -ge $threshold) { $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $archiveName = "$LogFileName.$timestamp.bak" $archivePath = Join-Path $resolvedLogDir $archiveName try { Move-Item -LiteralPath $logFilePath -Destination $archivePath -Force -ErrorAction Stop } catch { Write-Error "Unable to rotate/archive log file: $_" } } } catch { Write-Error "Unable to inspect log file: $_" } } # Format message $now = Get-Date $formattedMessage = if ($AsJson) { @{ timestamp = $now.ToString('o'); level = $Level; message = $Message } | ConvertTo-Json -Compress } else { "$($now.ToString('yyyy-MM-dd HH:mm:ss')) [$Level] $Message" } # Console output (unless WriteOnlyToFile) if (-not $WriteOnlyToFile) { if ($Color) { $fg = Get-LevelColor -Level $Level Write-Host $formattedMessage -ForegroundColor $fg } else { Write-Host $formattedMessage } } # File output if ($WriteToFile) { try { Add-LineToFile -Path $logFilePath -Line $formattedMessage } catch { Write-Error "Failed to write to log file: $_" throw } } if ($PassThru) { return [pscustomobject]@{ Timestamp = $now Level = $Level Message = $Message Path = if ($WriteToFile) { $logFilePath } else { $null } } } } |