Skip to content

Commit a765308

Browse files
committed
[vscode] Add mergeVscodeLogs.ps1
1 parent 437a2e0 commit a765308

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

VSCode/mergeVscodeLogs.ps1

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Copyright (c) 2023 Matthias Wolf, Mawosoft.
2+
3+
<#
4+
.SYNOPSIS
5+
Merge multiple VSCode logs into a single log ordered by timestamps.
6+
#>
7+
8+
#Requires -Version 7.3
9+
10+
using namespace System
11+
using namespace System.Collections.Generic
12+
using namespace System.IO
13+
using namespace System.Text.RegularExpressions
14+
15+
16+
[CmdletBinding()]
17+
param(
18+
# Log file directory
19+
[Parameter(Mandatory)]
20+
[ValidateNotNullOrEmpty()]
21+
[string]$Path,
22+
23+
# Log file names without extension. Wildcards are allowed.
24+
# Order determines how entries with identical timestamp are sorted.
25+
[Parameter(Mandatory)]
26+
[ValidateNotNullOrEmpty()]
27+
[SupportsWildcards()]
28+
[string[]]$LogName,
29+
30+
# One or more window numbers. Default is 1.
31+
[ValidateNotNullOrEmpty()]
32+
[int[]]$Window = @(1),
33+
34+
# Destination file. If omitted, output is sent to the pipe.
35+
[ValidateNotNullOrEmpty()]
36+
[string]$Destination,
37+
38+
# Convert multi-line log entries to single line.
39+
[switch]$SingleLine
40+
)
41+
42+
[List[FileInfo]]$candidateFiles = @()
43+
[List[psobject]]$logFiles = @()
44+
45+
$candidateFiles.AddRange([FileInfo[]](Get-ChildItem $Path -File))
46+
47+
foreach ($windowNumber in $Window) {
48+
[string]$windowFolder = Join-Path $Path "window$windowNumber"
49+
if (Test-Path $windowFolder -PathType Container) {
50+
$candidateFiles.AddRange([FileInfo[]](Get-ChildItem $windowFolder -File -Recurse))
51+
}
52+
}
53+
54+
foreach ($logPattern in $LogName) {
55+
for ([int]$i = 0; $i -lt $candidateFiles.Count; $i++) {
56+
[FileInfo]$fi = $candidateFiles[$i]
57+
if ($fi -and $fi.BaseName -like $logPattern -and $fi.Extension -eq '.log') {
58+
[string]$name = $fi.BaseName
59+
[DirectoryInfo]$parent = $fi.Directory
60+
while ($logFiles.Exists({ param($lf) $lf.Name -ceq $name }) -and $parent) {
61+
$name = $parent.Name + '/' + $name
62+
$parent = $parent.Parent
63+
}
64+
$logfiles.Add([PSCustomObject]@{
65+
Name = $name
66+
Path = $fi.FullName
67+
})
68+
$candidateFiles[$i] = $null
69+
}
70+
}
71+
}
72+
73+
if ($logFiles.Count -lt 2) {
74+
throw 'At least two logfiles are required for merging.'
75+
}
76+
77+
# Log entries from all files
78+
[List[psobject]]$logEntries = @()
79+
80+
foreach ($logfile in $logFiles) {
81+
[string]$content = Get-Content $logFile.Path -Raw
82+
if (-not $content) { continue } # Can be empty
83+
[string]$name = $logFile.Name
84+
85+
[Match]$m = [regex]::Match($content, '^(?<stamp>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d) \[[A-Za-z]+\]')
86+
if (-not $m.Success) {
87+
Write-Warning "Skipping log with non-standard format $($logFile.Path)"
88+
continue
89+
}
90+
91+
[string]$stamp = $m.Groups['stamp'].Value
92+
[int]$startPos = 0
93+
$m = [regex]::Match($content, '(?<eol>\r?\n)(?<stamp>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d) \[[A-Za-z]+\]')
94+
for (; ; ) {
95+
[string]$entry = $m.Success ? $content.Substring($startPos, $m.Index - $startPos) : $content.Substring($startPos).TrimEnd("`r`n")
96+
if ($SingleLine) {
97+
$entry = [regex]::Replace($entry, '\r?\n', '')
98+
}
99+
$logEntries.Add([PSCustomObject]@{
100+
Name = $name
101+
Stamp = $stamp
102+
Order = $i
103+
Entry = $entry
104+
})
105+
if (-not $m.Success) { break }
106+
$stamp = $m.Groups['stamp'].Value
107+
$startPos = $m.Index + $m.Groups['eol'].Length
108+
$m = $m.NextMatch()
109+
}
110+
}
111+
112+
$logEntries.Sort({
113+
param($x, $y)
114+
[int]$r = $x.Stamp.CompareTo($y.Stamp)
115+
if ($r -ne 0) { return $r }
116+
return $x.Order.CompareTo($y.Order)
117+
})
118+
119+
[int]$logNamePadding = ($logEntries.Name | Measure-Object -Property Length -Maximum).Maximum + 1
120+
121+
[List[string]]$lines = $logEntries.ConvertAll[string]({
122+
param($e)
123+
return $e.Name.PadRight($logNamePadding) + $e.Entry
124+
})
125+
126+
if ($Destination) {
127+
$lines | Set-Content $Destination
128+
}
129+
else {
130+
$PSCmdlet.WriteObject($lines, $true)
131+
}

0 commit comments

Comments
 (0)