Public/Read-ChocoLog.ps1
<# .SYNOPSIS Parses a chocolatey into an object that is easier to search and filter. .DESCRIPTION Reads chocolatey log(s) and creates a new set of custom objects. It highlights details that make it easier to search and filter logs. .NOTES Works for Windows PowerShell and PowerShell Core. This works on Linux. .LINK Specify a URI to a help page, this will show when Get-Help -Online is used. .EXAMPLE Read-ChocoLog This will read the latest Chocolatey.log on the machine. .PARAMETER Path The log you want to parse. This will default to the latest local log. .PARAMETER FileLimit How many files should we parse a given folder path? .PARAMETER Filter The filter passed to Get Child Item. Default to 'chocolatey*.log.' .PARAMETER PatternLayout The log4net pattern layour used to parse the log. Should contain capture groups for time, session, level, and message. #> function Read-ChocoLog { [OutputType([System.Collections.ArrayList])] param ( [ValidateScript({ if (-Not ($_ | Test-Path) ) { throw "File or folder does not exist" } return $true })] [string[]] $Path = 'C:\ProgramData\chocolatey\logs\', [int] $FileLimit = 1, [String] $Filter = 'chocolatey*.log', [string] $PatternLayout = '%date %thread [%-5level] - %message' ) $files = Get-Item -Path $Path if ($files.PSIsContainer) { $files = Get-ChildItem -Path $Path -Filter $Filter | Sort-Object -Property LastWriteTime | Select-Object -Last $FileLimit } [System.Collections.ArrayList]$parsed = @() $RegularExpression = Convert-PatternLayout $PatternLayout $files | ForEach-Object -Process { $file = $_ $raw = [System.IO.File]::ReadAllLines($file.FullName) # Iterate over each line foreach ($line in $raw) { # Write-Debug $line $m = $RegularExpression.match($line) if ($m.Success) { # If it matches the regex, tag it if ($m.Groups['thread'].Value -ne $currentSession.thread) { if ($currentSession) { $currentSession.endTime = $currentSession.logs[-1].time # This updates fields like: cli, environment, and configuration $currentSession.ParseSpecialLogs() $parsed.Add($currentSession) > $null } # This is a different session $currentSession = [ChocoLog]::new( $m.Groups['thread'].Value, ($m.Groups['date'].Value -replace ',', '.'), $file ) } $currentSession.logs.Add( [Log4NetLogLine]::new( [Datetime]($m.Groups['date'].Value -replace ',', '.'), $m.Groups['thread'].Value, $m.Groups['level'].Value, $m.Groups['message'].Value )) > $null } else { # if it doesn't match regex, append to the previous if ($currentSession) { $currentSession.logs[-1].AppendMessage($line) } else { # This might happen if the log starts on what should have been a # multiline entry... Not very likely Write-Warning "No currentSession. File: $File; Line: $Line" } } } } # Write out the last log line! if (-Not $parsed.Contains($currentSession)) { $parsed.Add($currentSession) > $null } # Return the whole parsed object $parsed } |