diff --git a/tools/Collect-YALootBoxesLogs.ps1 b/tools/Collect-YALootBoxesLogs.ps1 new file mode 100644 index 0000000..3425564 --- /dev/null +++ b/tools/Collect-YALootBoxesLogs.ps1 @@ -0,0 +1,162 @@ +<# +.SYNOPSIS + Collects DayZ server + client logs (and the mod's live config) for + debugging the YALootBoxes mod, then bundles them into one timestamped + .zip that is easy to hand off. + +.DESCRIPTION + Copies recent .RPT / .ADM / .log / .mdmp / crash logs from the server + profile folder and the client log folder, plus the auto-generated + profiles\YALootBoxes\*.json config. Produces: + - server\ , client\ : the raw collected files + - server\YALootBoxes_config\*.json: the mod's live config + - YALootBoxes_filtered.txt : every "[YALootBoxes]" line, by file + - summary.txt : machine info + ERROR/WARN counts + file list + ...all inside YALootBoxes_logs_.zip on your Desktop. + + Handles files locked by a running server (shared-read copy). + +.EXAMPLE + powershell -ExecutionPolicy Bypass -File .\Collect-YALootBoxesLogs.ps1 + +.EXAMPLE + # widen the window to the last 48 hours + powershell -ExecutionPolicy Bypass -File .\Collect-YALootBoxesLogs.ps1 -SinceHours 48 +#> +[CmdletBinding()] +param( + [string]$ServerProfile = 'C:\Program Files (x86)\Steam\steamapps\common\DayZServer\profiles', + [string]$ClientLogs = 'C:\Users\drasi\AppData\Local\DayZ', + [string]$OutDir = "$env:USERPROFILE\Desktop", + [int] $SinceHours = 12, + [string]$Tag = '[YALootBoxes]' +) + +$ErrorActionPreference = 'Stop' +$stamp = Get-Date -Format 'yyyyMMdd_HHmmss' +$stageRoot = Join-Path $env:TEMP "YALB_logs_$stamp" +$stageServer = Join-Path $stageRoot 'server' +$stageClient = Join-Path $stageRoot 'client' +New-Item -ItemType Directory -Force -Path $stageServer, $stageClient | Out-Null + +$patterns = '*.RPT', '*.log', '*.ADM', '*.mdmp', 'crash_*.log', 'error.log', 'script*.log' +$cutoff = (Get-Date).AddHours(-$SinceHours) + +# Copy a file even if a running server holds it open (shared read). +function Copy-Locked { + param([string]$Src, [string]$Dst) + try { + $in = [System.IO.File]::Open($Src, 'Open', 'Read', 'ReadWrite') + try { + $out = [System.IO.File]::Create($Dst) + try { $in.CopyTo($out) } finally { $out.Dispose() } + } finally { $in.Dispose() } + return $true + } catch { + Write-Warning "Could not copy '$Src': $($_.Exception.Message)" + return $false + } +} + +function Collect { + param([string]$Source, [string]$Stage, [string]$Label) + if (-not (Test-Path $Source)) { + Write-Warning "$Label path not found: $Source" + return @() + } + $files = Get-ChildItem -Path $Source -Recurse -File -Include $patterns -ErrorAction SilentlyContinue | + Where-Object { $_.LastWriteTime -ge $cutoff } + + # Always include the newest .RPT even if it predates the cutoff window. + if (-not ($files | Where-Object { $_.Extension -eq '.RPT' })) { + $newestRpt = Get-ChildItem -Path $Source -Recurse -File -Filter '*.RPT' -ErrorAction SilentlyContinue | + Sort-Object LastWriteTime -Descending | Select-Object -First 1 + if ($newestRpt) { $files = @($files) + $newestRpt } + } + + $collected = @() + foreach ($f in ($files | Sort-Object FullName -Unique)) { + $dst = Join-Path $Stage $f.Name + if (Test-Path $dst) { + $dst = Join-Path $Stage ('{0}_{1}{2}' -f $f.BaseName, $f.LastWriteTime.ToString('HHmmss'), $f.Extension) + } + if (Copy-Locked $f.FullName $dst) { + $collected += [pscustomobject]@{ + Source = $Label; Name = $f.Name; Size = $f.Length; Modified = $f.LastWriteTime + } + } + } + return $collected +} + +# --- collect server + client logs --- +$serverFiles = @(Collect -Source $ServerProfile -Stage $stageServer -Label 'server') +$clientFiles = @(Collect -Source $ClientLogs -Stage $stageClient -Label 'client') + +# --- grab the mod's live generated config ($profile:YALootBoxes\*.json) --- +$modCfg = Join-Path $ServerProfile 'YALootBoxes' +if (Test-Path $modCfg) { + $cfgStage = Join-Path $stageServer 'YALootBoxes_config' + New-Item -ItemType Directory -Force -Path $cfgStage | Out-Null + Get-ChildItem $modCfg -Filter '*.json' -File -ErrorAction SilentlyContinue | ForEach-Object { + Copy-Locked $_.FullName (Join-Path $cfgStage $_.Name) | Out-Null + } +} else { + Write-Warning "Mod config folder not found (mod may not have run yet): $modCfg" +} + +$all = $serverFiles + $clientFiles + +# --- extract only the mod-tagged lines into one file --- +$filterPath = Join-Path $stageRoot 'YALootBoxes_filtered.txt' +$sb = New-Object System.Text.StringBuilder +$textFiles = Get-ChildItem -Path $stageServer, $stageClient -Recurse -File | + Where-Object { $_.Extension -in '.RPT', '.log', '.ADM' } +foreach ($tf in $textFiles) { + $hits = Select-String -Path $tf.FullName -SimpleMatch -Pattern $Tag -ErrorAction SilentlyContinue + if ($hits) { + [void]$sb.AppendLine("===== $($tf.Name) ($($hits.Count) line(s)) =====") + foreach ($h in $hits) { [void]$sb.AppendLine(('{0,6}: {1}' -f $h.LineNumber, $h.Line.Trim())) } + [void]$sb.AppendLine('') + } +} +if ($sb.Length -eq 0) { [void]$sb.AppendLine("No '$Tag' lines found in the collected logs.") } +Set-Content -Path $filterPath -Value $sb.ToString() -Encoding UTF8 + +# --- summary --- +$errCount = (Select-String -Path $filterPath -SimpleMatch -Pattern '[ERROR]' -ErrorAction SilentlyContinue).Count +$warnCount = (Select-String -Path $filterPath -SimpleMatch -Pattern '[WARN]' -ErrorAction SilentlyContinue).Count + +$lines = @( + 'YALootBoxes log bundle' + "Generated: $(Get-Date -Format 'u')" + "Machine: $env:COMPUTERNAME User: $env:USERNAME" + "Window: last $SinceHours hour(s) Tag: $Tag" + "Server src: $ServerProfile" + "Client src: $ClientLogs" + '' + "Mod-tagged ERROR lines: $errCount" + "Mod-tagged WARN lines: $warnCount" + '' + 'Files collected:' +) +if ($all) { + foreach ($f in $all) { + $lines += (' [{0}] {1} ({2:N0} bytes, {3:yyyy-MM-dd HH:mm})' -f $f.Source, $f.Name, $f.Size, $f.Modified) + } +} else { + $lines += ' (none - check the paths or widen -SinceHours)' +} +Set-Content -Path (Join-Path $stageRoot 'summary.txt') -Value $lines -Encoding UTF8 + +# --- zip + cleanup --- +$zip = Join-Path $OutDir "YALootBoxes_logs_$stamp.zip" +if (Test-Path $zip) { Remove-Item $zip -Force } +Compress-Archive -Path (Join-Path $stageRoot '*') -DestinationPath $zip +Remove-Item $stageRoot -Recurse -Force + +Write-Host '' +Write-Host "Collected $($all.Count) log file(s)." -ForegroundColor Green +Write-Host "Mod-tagged lines -> ERROR: $errCount WARN: $warnCount" +Write-Host "Bundle: $zip" -ForegroundColor Cyan +Write-Host 'Send me that .zip (summary.txt + YALootBoxes_filtered.txt inside are the key files).'