Private/ODBLogProcessor.ps1
|
class ODBLogProcessor { # ── Properties ────────────────────────────────────────────────────────────── [System.IO.FileInfo] $SourceFile # validated FileInfo for the input .cab/.zip [string] $DateFilter # e.g. "2023-10-31" [string] $Platform # "Windows" | "Mac" [string] $WorkingDir # parent directory of the source file [string] $ExtractedDir # full path to the extracted subfolder # ── Constructor ────────────────────────────────────────────────────────────── ODBLogProcessor([string]$Path, [string]$Date, [string]$Platform) { try { $this.SourceFile = Get-Item -LiteralPath $Path -ErrorAction Stop } catch { throw "Source file not found at '$Path': $_" } $this.DateFilter = $Date $this.Platform = $Platform $this.WorkingDir = $this.SourceFile.DirectoryName $this.ExtractedDir = Join-Path $this.WorkingDir $this.SourceFile.BaseName } # ── Methods ────────────────────────────────────────────────────────────────── # Step 1 — Extract the source file. # Windows .cab → extrac32.exe (native Cabinet tool) # Mac .zip → Expand-Archive (PowerShell built-in) [void] ExtractSource() { if ($this.Platform -eq 'Windows') { $proc = Start-Process -FilePath 'extrac32.exe' ` -ArgumentList "`"$($this.SourceFile.FullName)`"", '/E', '/L', "`"$($this.ExtractedDir)`"" ` -Wait ` -PassThru if ($proc.ExitCode -ne 0) { throw "extrac32.exe exited with code $($proc.ExitCode). Extraction may have failed." } } else { Expand-Archive -Path $this.SourceFile.FullName -DestinationPath $this.ExtractedDir -Force } } # Step 2 — Collect ODL log files (files to KEEP). # Windows adds *.odlsent; Mac does not (Mac ODB does not generate them). [System.IO.FileInfo[]] CollectOdlFiles() { $extensions = @('*.odl', '*.odlgz', '*.etlgz') if ($this.Platform -eq 'Windows') { $extensions += '*.odlsent' } $files = [System.Collections.Generic.List[System.IO.FileInfo]]::new() foreach ($ext in $extensions) { foreach ($f in (Get-ChildItem -Path $this.ExtractedDir -Recurse -Filter $ext)) { $files.Add($f) } } return $files.ToArray() } # Step 3 — Collect the four excluded file categories. # Returns a hashtable keyed by the zip-suffix label used in CompressExcluded. [hashtable] CollectExcludedFiles() { return @{ 'EventLogsFiles' = @(Get-ChildItem -Path $this.ExtractedDir -Recurse -Filter '*.evtx') 'DbFiles' = @(Get-ChildItem -Path $this.ExtractedDir -Recurse -Filter '*.db') 'DbWalFiles' = @(Get-ChildItem -Path $this.ExtractedDir -Recurse -Filter '*.db-wal') 'SetupLogFiles' = @(Get-ChildItem -Path $this.ExtractedDir -Recurse -Filter '*.log') } } # Step 4 — Rename each excluded file with its immediate parent folder as a prefix # to avoid collisions when files from different subdirs are compressed together. # Returns a hashtable of the same keys, now holding [string[]] of new absolute paths. [hashtable] RenameExcludedFiles([hashtable]$ExcludedGroups) { $renamed = @{} foreach ($key in $ExcludedGroups.Keys) { $group = $ExcludedGroups[$key] if ($group.Count -gt 0) { $group | Rename-Item -NewName { $parentLeaf = Split-Path $_.DirectoryName -Leaf "${parentLeaf}_$($_.Name)" } $renamed[$key] = $group | ForEach-Object { $parentLeaf = Split-Path $_.DirectoryName -Leaf Join-Path $_.DirectoryName "${parentLeaf}_$($_.Name)" } } else { $renamed[$key] = @() } } return $renamed } # Step 5 — Compress each excluded category into its own .zip side archive. [void] CompressExcluded([hashtable]$RenamedGroups) { foreach ($key in $RenamedGroups.Keys) { $paths = $RenamedGroups[$key] if ($paths.Count -gt 0) { $destination = Join-Path $this.WorkingDir "$($this.SourceFile.BaseName)-${key}.zip" $paths | Compress-Archive -DestinationPath $destination -CompressionLevel Optimal } } } # Step 6a — Delete excluded files after they have been archived. # Windows suppresses deletion errors (mirrors original script behaviour); # Mac lets errors surface. Both use explicit -ErrorAction on Remove-Item # because class methods cannot assign to scope-based preference variables. [void] DeleteExcluded([hashtable]$RenamedGroups) { $errorAction = if ($this.Platform -eq 'Windows') { 'SilentlyContinue' } else { 'Continue' } foreach ($paths in $RenamedGroups.Values) { if ($paths.Count -gt 0) { $paths | Remove-Item -Force -ErrorAction $errorAction } } } # Step 6b — Delete ODL files whose BaseName does not match the date filter. [void] DeleteNonMatchingOdl([System.IO.FileInfo[]]$OdlFiles) { $toDelete = $OdlFiles | Where-Object { $_.BaseName -notmatch $this.DateFilter } if ($toDelete) { $toDelete | Remove-Item -Force } } # Step 8 — Remove the extracted working directory and its contents. [void] Cleanup() { if (Test-Path -LiteralPath $this.ExtractedDir) { Remove-Item -LiteralPath $this.ExtractedDir -Recurse -Force -ErrorAction SilentlyContinue } } # Step 7 — Compress remaining logs into the final Parsed archive. # Windows → {basename}-Parsed.cab via makecab.exe (preserves folder structure via DDF). # Mac → {basename}-Parsed.zip via Compress-Archive. [string] CompressParsed() { if ($this.Platform -eq 'Windows') { $destination = Join-Path $this.WorkingDir "$($this.SourceFile.BaseName)-Parsed.cab" return New-CabinetFile -SourceDirectory $this.ExtractedDir -Destination $destination } else { $destination = Join-Path $this.WorkingDir "$($this.SourceFile.BaseName)-Parsed.zip" Compress-Archive -Path "$($this.ExtractedDir)\*" -DestinationPath $destination -CompressionLevel Optimal return $destination } } } |