Last active
August 21, 2023 18:11
-
-
Save mavaddat/2174b546cab2be61c4936985a63f529a to your computer and use it in GitHub Desktop.
PowerShell Core function to search Winget (aka Windows Package Manager) logs
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
#Requires -Version 6.0 | |
<# | |
.SYNOPSIS | |
Retrieves recent winget logs and searches them for a pattern, if specified. | |
.DESCRIPTION | |
This script parses the Windows Package Manager winget command-line tool log files and allows searching by specifying a pattern or date boundaries. | |
.PARAMETER Search | |
The pattern to search for in the log files. If not specified, all log entries are returned. | |
.PARAMETER AfterDate | |
The date after which to search for log entries. If not specified, all log entries are returned. | |
.PARAMETER BeforeDate | |
The date before which to search for log entries. If not specified, all log entries are returned. | |
.PARAMETER ErrorsOnly | |
If specified, only log entries with a source of FAIL are returned. | |
.PARAMETER HighlightPaths | |
If specified, paths in the log entries are highlighted. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -HighlightPaths | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours and highlights the paths in the log entries. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -ErrorsOnly | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours and only returns log entries with a source of FAIL. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -BeforeDate (Get-Date).AddHours(-1) | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours, but only returns log entries from the last 23 hours. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -BeforeDate (Get-Date).AddHours(-1) -HighlightPaths | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours, but only returns log entries from the last 23 hours and highlights the paths in the log entries. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -BeforeDate (Get-Date).AddHours(-1) -ErrorsOnly | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours, but only returns log entries from the last 23 hours and only returns log entries with a source of FAIL. | |
.EXAMPLE | |
PS C:\> Get-WingetLogs -Search 'winget install' -AfterDate (Get-Date).AddDays(-1) -BeforeDate (Get-Date).AddHours(-1) -ErrorsOnly -HighlightPaths | |
# This example retrieves all winget log entries containing the text 'winget install' from the last 24 hours, but only returns log entries from the last 23 hours, only returns log entries with a source of FAIL, and highlights the paths in the log entries. | |
.NOTES | |
Author: Mavaddat Javid | |
Date: 2023-06-03 | |
Version: 1.0 | |
#> | |
function Get-WingetLogs { | |
[CmdletBinding()] | |
param ( | |
# Parameter help description | |
[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] | |
[Alias('Filter', 'Pattern')] | |
[string] | |
$Search, | |
[Alias('After')] | |
[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] | |
[datetime] | |
$AfterDate = ([datetime]::MinValue), | |
[Alias('Before')] | |
[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] | |
[datetime] | |
$BeforeDate = ([datetime]::MaxValue), | |
[switch] | |
$ErrorsOnly, | |
[switch] | |
$HighlightPaths, | |
[Alias('Symbols','Icons')] | |
[switch] | |
$Emojis | |
) | |
begin { | |
class WingetLogs { | |
[datetime]$TimeStamp | |
[string]$Source | |
[string]$Message | |
WingetLogs([psobject]$IncomingObj) { | |
$this.TimeStamp = $IncomingObj.TimeStamp | |
$this.Source = $IncomingObj.Source | |
$this.Message = $IncomingObj.Message | |
} | |
} | |
[array]$WingetLogFiles = Get-ChildItem -Path ("$env:LOCALAPPDATA\Packages\Microsoft.DesktopAppInstaller*\LocalState\DiagOutputDir" | Resolve-Path ) -File | |
Write-Verbose -Message "Found $($WingetLogFiles.Count) Winget log files" | |
} | |
process { | |
$WingetLogs = $WingetLogFiles | ForEach-Object -Begin { | |
$numRecords = 0 | |
Write-Verbose -Message "Searching for Winget log files in $($WingetLogFiles[0].DirectoryName)" | |
} -Process { | |
Write-Verbose -Message "Processing Winget log file $($_.FullName)" | |
Write-Progress -Activity "Parsing $($_.Name)" -Status "File $($WingetLogFiles.IndexOf($_)) out of $($WingetLogFiles.Count)" -PercentComplete (($WingetLogFiles.IndexOf($_) / $WingetLogFiles.Count) * 100) | |
$_ | Get-Content | Select-String -Pattern '^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[([^\]]+)\] (.+)$' | ForEach-Object { | |
$numRecords++ | |
$source = $_.Matches.Groups[2].Value.Trim() | |
$message = ($source -eq 'FAIL') ? ($_.Matches.Groups[3].Value -replace '^(.+)$', "`e[31m`$1`e[0m") : ($_.Matches.Groups[3].Value) | |
if ($Emojis) { | |
$source = switch ($source) { | |
CLI { "🖥️"; break } | |
CORE { "🧰"; break } | |
FAIL { "❌"; break } | |
REPO { "📦"; break } | |
SQL { "🗄️"; break } | |
default { $source } | |
} | |
} | |
else { | |
$source = switch ($source) { | |
CLI { "`e[32m$source`e[0m"; break } | |
CORE { "`e[33m$source`e[0m"; break } | |
FAIL { "`e[31m$source`e[0m"; break } | |
REPO { "`e[34m$source`e[0m"; break } | |
SQL { "`e[35m$source`e[0m"; break } | |
default { $source } | |
} | |
} | |
if ($HighlightPaths) { | |
$pathsInMsg = $message | Select-String -Pattern '(?:([''"])[A-Z]:\\[^:!''"]+\1)|(?:[A-Z]:\\[^\s:!]+)' -AllMatches | ForEach-Object { $_.Matches.Value } | |
# Surround the paths with ANSI escape codes to highlight them | |
$pathsInMsg | ForEach-Object { | |
# Replace the $Search with the ANSI escape codes | |
$pathHighlighted = $_ -replace "($Search)", "`e[7m`$1`e[0m`e[36m" # Highlight the search term in the path | |
$message = $message -replace "$Search(.*)(?=$([regex]::Escape($_)))", "`e[7m$Search`e[0m`$1" # Move the path highlight start before the search highlight start | |
$message = $message -replace "(?<=$([regex]::Escape($_)))(.*)$Search", "`$1`e[7m$Search`e[0m" # Move the path highlight end after the search highlight end | |
$message = $message -replace "($([regex]::Escape($_)))", "`e[36m$pathHighlighted`e[0m" # Replace the path with the highlighted path | |
} | |
} | |
elseif (-not [string]::IsNullOrWhiteSpace($Search)) { | |
$message = $message -replace "($Search)", "`e[7m`$1`e[0m" | |
} | |
# add background color to the filter text | |
$message = $message -replace "((`e\[\d+m).*)?($Search)", "`$1`e[7m`$3`e[0m`$2" | |
[WingetLogs]::new(@{ | |
TimeStamp = [datetime]::Parse(($_.Matches.Groups[1].Value)) | |
Source = $source | |
Message = $message | |
}) | |
} | Where-Object -FilterScript { $_.TimeStamp -ge $AfterDate -and $_.TimeStamp -le $BeforeDate -and $_.Message -match $Search } | |
} -End { | |
Write-Verbose -Message "Found $numRecords records in $($WingetLogFiles[0].DirectoryName)" | |
} | |
} | |
end { | |
if ($ErrorsOnly) { | |
return $WingetLogs | Where-Object -FilterScript { $_.Source -eq 'FAIL' } | |
} | |
else { | |
return $WingetLogs | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment