Created
March 10, 2023 07:10
-
-
Save swbbl/1591467206b70eda61a9afe17bb37538 to your computer and use it in GitHub Desktop.
New-LogEntry. An easy way to create and write proper log entries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum Severity { | |
INFO | |
WARNING | |
ERROR | |
} | |
class LogEntry { | |
[datetime] $DateTime = [datetime]::UtcNow | |
[Severity] $Severity | |
[string] $Message | |
LogEntry() { | |
$this.Severity = 'INFO' | |
} | |
LogEntry([Management.Automation.ErrorRecord] $ErrorRecord) { | |
$this.Severity = [Severity]::ERROR | |
$messageText = [System.Text.StringBuilder]::new() | |
$messageText.AppendLine('Error:') | |
$messageText.AppendLine("`tMessage:`n`t`t" + ($ErrorRecord.ToString() -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tPSMessageDetails:`n`t`t" + ($ErrorRecord.PSMessageDetails -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tPositionMessage:`n`t`t" + ($ErrorRecord.InvocationInfo.PositionMessage -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tCategoryInfo:`n`t`t" + ($ErrorRecord.CategoryInfo -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tFullyQualifiedErrorId:`n`t`t" + ($ErrorRecord.FullyQualifiedErrorId -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tErrorDetails:`n`t`t" + ($ErrorRecord.ErrorDetails -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tScriptStackTrace:`n`t`t" + ($ErrorRecord.ScriptStackTrace -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tTargetObject:`n`t`t" + ($ErrorRecord.TargetObject -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tPipelineIterationInfo:`n`t`t" + ($ErrorRecord.PipelineIterationInfo -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`t" + ('#' * 75)) | |
$exception = $ErrorRecord.Exception | |
while ($null -ne $exception) { | |
$messageText.AppendLine("`tException:`n`t`t" + ($exception.GetType().FullName -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tMessage:`n`t`t" + ($exception.Message -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tSource:`n`t`t" + ($exception.Source -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tHResult:`n`t`t" + ($exception.HResult -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`tStackTrace:`n`t`t" + ($exception.StackTrace -replace '\r?\n', "`n`t`t")) | |
$messageText.AppendLine("`t" + ('-' * 75)) | |
$exception = $exception.InnerException | |
} | |
$this.Message = $messageText.ToString() | |
} | |
LogEntry($Severity, $Message) { | |
$this.Severity = $Severity | |
$this.Message = $Message | |
} | |
[void] ToCsvFile ([string] $FilePath) { | |
$value = if ((Test-Path -LiteralPath $FilePath) -and (Get-Content -LiteralPath $FilePath -TotalCount 1)) { | |
$this.ToCsv($true) | |
} else { | |
$this.ToCsv($false) | |
} | |
Add-Content -LiteralPath $FilePath -Value $value -Encoding UTF8 -Force | |
} | |
[string[]] ToCsv () { | |
return $this.ToCsv($false) | |
} | |
[string[]] ToCsv ([bool] $ExcludeHeader) { | |
return $this | | |
Select-Object @{n='DateTime'; e= { $_.DateTime.ToString('o') } }, * -ErrorAction Ignore | | |
ConvertTo-Csv -Delimiter ',' -NoTypeInformation | | |
Select-Object -Skip ([int]$ExcludeHeader) | |
} | |
[LogEntry] AddPrefix ([string] $Prefix) { | |
if (-not [string]::IsNullOrWhiteSpace($Prefix)) { | |
$this.Message = $Prefix + "`n" + $this.Message | |
} | |
return $this | |
} | |
[LogEntry] AddSuffix ([string] $Suffix) { | |
if (-not [string]::IsNullOrWhiteSpace($Suffix)) { | |
$this.Message = $this.Message + "`n" + $Suffix | |
} | |
return $this | |
} | |
} | |
function New-LogEntry { | |
<# | |
.SYNOPSIS | |
Creates a new log entry. | |
.DESCRIPTION | |
Creates a new log entry with the following format: | |
DateTime|Severity|Message | |
The returned [LogEntry] class has further methods like converting to CSV (.ToCsv) or | |
saving it to CSV file (.ToCsvFile(<filePath) → also done when the parameter "-FilePath" has been provided) which will format the message depending on the type. | |
- An [ErrorRecord] will be formatted in a proper and useful way and it will iterate through each (inner)exception | |
- An [Object] will be piped to Format-List * | |
- A [String] will be written as is | |
If the message is of type [array] it will be enumerated. | |
If the environment variable $env:PSLogEntryFilePath is set (either local in the script or global in the OS), the parameter -FilePath is set to it by default. | |
.EXAMPLE | |
# Return a [LogEntry] (if $env:PSLogEntryFilePath is not set) | |
Get-Item C:\ | New-LogEntry | |
.EXAMPLE | |
# Return a CSV-formatted [LogEntry] (if $env:PSLogEntryFilePath is not set) | |
(Get-Item C:\ | New-LogEntry).ToCsv() | |
.EXAMPLE | |
# Write to CSV file (if $env:PSLogEntryFilePath is not set) | |
Get-Item C:\ | New-LogEntry -FilePath C:\temp\log.csv | |
.EXAMPLE | |
# Write to CSV file with $env:PSLogEntryFilePath | |
$env:PSLogEntryFilePath = 'C:\temp\envLogFile.csv' | |
Get-Item C:\ | New-LogEntry | |
.EXAMPLE | |
# Catch and trap all errors with $env:PSLogEntryFilePath | |
$env:PSLogEntryFilePath = 'C:\temp\envLogFile.csv' | |
trap { | |
New-LogEntry -Message $_ | |
} | |
Get-ChildItem 'C:\System Volume Information\' | |
try { | |
1 / 0 | |
} catch { | |
New-LogEntry -Message $_ | |
Write-Error "Error occured. See logfile: $env:PSLogEntryFilePath" | |
} | |
#> | |
param( | |
# The Message of the log entry. | |
# The content will be formatted depending on the type. | |
# - An [ErrorRecord] will be formatted in a proper and useful way and it will iterate through each (inner)exception | |
# - An [Object] will be piped to Format-List * | |
# - A [String] will be written as is | |
[Parameter(Mandatory, ValueFromPipeline)] | |
[psobject[]] $Message, | |
# The [Severity] of the log entry (INFO, WARNING, ERROR) | |
[Parameter()] | |
[Severity] $Severity = 'INFO', | |
# The LogFile Path. | |
# If set, the log entry will be written to the logfile and won't be returned to the caller. | |
# Set the environment variable "PSLogEntryFilePath" with the desired logfile path as global default setting ($env:PSLogEntryFilePath = '<LogFilePath>'). | |
[Parameter()] | |
[string] $FilePath = $env:PSLogEntryFilePath, | |
# An additional prefix for the message. | |
[Parameter()] | |
[string] $Prefix, | |
# An additional suffix for the message. | |
[Parameter()] | |
[string] $Suffix, | |
# If $true/set, an [ErrorRecord] will not be parsed and handled like an object ($_ | Format-List *). | |
# To handle an [ErrorRecord] like a string, cast it. | |
[Parameter()] | |
[switch] $NoErrorRecordParsing | |
) | |
foreach ($messageItem in $Message) { | |
$logEntry = if (-not $NoErrorRecordParsing -and $messageItem -is [Management.Automation.ErrorRecord]) { | |
[LogEntry]::new($messageItem).AddPrefix($Prefix).AddSuffix($Suffix) | |
} else { | |
[LogEntry]::new($Severity, ($messageItem | fl * | Out-String) -replace '^[^\w]+|[^\w]+$').AddPrefix($Prefix).AddSuffix($Suffix) | |
} | |
if ($FilePath) { | |
$logEntry.ToCsvFile($FilePath) | |
} else { | |
$logEntry | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment