mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
rft: 日志汇报打包 debug 截图,支持分卷,修改存储路径
This commit is contained in:
@@ -76,4 +76,14 @@ public static class PathsHelper
|
||||
/// named "data". This property returns the resolved path, creating the directory if it does not exist is the
|
||||
/// responsibility of the caller.</remarks>
|
||||
public static string DataDir => _data ??= Path.Combine(BaseDir, "data");
|
||||
|
||||
private static string? _reports;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the application's reports directory.
|
||||
/// </summary>
|
||||
/// <remarks>The reports directory is located within the application's base directory under a folder
|
||||
/// named "_reports". This property returns the resolved path, creating the directory if it does not exist is the
|
||||
/// responsibility of the caller.</remarks>
|
||||
public static string ReportsDir => _reports ??= Path.Combine(BaseDir, "reports");
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Data;
|
||||
using JetBrains.Annotations;
|
||||
using MaaWpfGui.Configuration.Factory;
|
||||
using MaaWpfGui.Constants;
|
||||
using MaaWpfGui.Helper;
|
||||
using MaaWpfGui.Main;
|
||||
using Serilog;
|
||||
using Stylet;
|
||||
|
||||
@@ -38,110 +38,187 @@ public class IssueReportUserControlModel : PropertyChangedBase
|
||||
Instance = new();
|
||||
}
|
||||
|
||||
private static readonly string[] _payloadFileNames = [
|
||||
Bootstrapper.UiLogFile,
|
||||
Bootstrapper.UiLogBakFile,
|
||||
Bootstrapper.CoreLogFile,
|
||||
Bootstrapper.CoreLogBakFile,
|
||||
ConfigurationHelper.ConfigFile,
|
||||
ConfigFactory.ConfigFile];
|
||||
|
||||
public static IssueReportUserControlModel Instance { get; }
|
||||
|
||||
public void OpenDebugFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(PathsHelper.DebugDir))
|
||||
if (!Directory.Exists(PathsHelper.ReportsDir))
|
||||
{
|
||||
Directory.CreateDirectory(PathsHelper.DebugDir);
|
||||
Directory.CreateDirectory(PathsHelper.ReportsDir);
|
||||
}
|
||||
|
||||
Process.Start("explorer.exe", PathsHelper.DebugDir);
|
||||
Process.Start("explorer.exe", PathsHelper.ReportsDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ToastNotification.ShowDirect($"Failed to open debug folder\n{ex.Message}");
|
||||
Log.Error(ex, "Failed to open debug folder");
|
||||
ToastNotification.ShowDirect($"Failed to open reports folder\n{ex.Message}");
|
||||
Log.Error(ex, "Failed to open reports folder");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成日志压缩包
|
||||
/// </summary>
|
||||
public void GenerateSupportPayload()
|
||||
{
|
||||
try
|
||||
{
|
||||
var reportFileName = $"report_{DateTimeOffset.Now:MM-dd_HH-mm-ss}.zip";
|
||||
string zipPath = Path.Combine(PathsHelper.DebugDir, reportFileName);
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
string debugTempPath = Path.Combine(tempPath, "debug");
|
||||
string resourceTempPath = Path.Combine(tempPath, "resource");
|
||||
if (File.Exists(zipPath))
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
const int PartSize = 10 * 1024 * 1024; // 10 MB
|
||||
string reportNameBase = $"report_{DateTimeOffset.Now:MM-dd_HH-mm-ss}";
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), $"maa-report-{Guid.NewGuid()}");
|
||||
Directory.CreateDirectory(tempPath);
|
||||
Directory.CreateDirectory(debugTempPath);
|
||||
Directory.CreateDirectory(resourceTempPath);
|
||||
|
||||
// 复制 _payloadFileNames 中的文件到 tempPath/debug
|
||||
foreach (var file in _payloadFileNames)
|
||||
if (!Directory.Exists(PathsHelper.ReportsDir))
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string relativePath = Path.GetRelativePath(PathsHelper.BaseDir, file);
|
||||
string dest = Path.Combine(tempPath, relativePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
|
||||
File.Copy(file, dest, overwrite: true);
|
||||
Directory.CreateDirectory(PathsHelper.ReportsDir);
|
||||
}
|
||||
|
||||
// 遍历 resource 文件夹下带 _custom 后缀的文件,复制到 tempPath/resource
|
||||
const string ResourceDir = "resource";
|
||||
if (Directory.Exists(ResourceDir))
|
||||
// 复制文件
|
||||
CopyDirectoryIfExists(PathsHelper.DebugDir, Path.Combine(tempPath, "debug"),
|
||||
f => { return !Path.GetFileName(f).StartsWith("report", StringComparison.OrdinalIgnoreCase); });
|
||||
CopyDirectoryIfExists(PathsHelper.ResourceDir, Path.Combine(tempPath, "resource"),
|
||||
f => { return Path.GetFileName(f).Contains("_custom.", StringComparison.OrdinalIgnoreCase); });
|
||||
CopyDirectoryIfExists(PathsHelper.ConfigDir, Path.Combine(tempPath, "config"));
|
||||
CopyDirectoryIfExists(PathsHelper.CacheDir, Path.Combine(tempPath, "cache"));
|
||||
|
||||
string partsFolder = Path.Combine(PathsHelper.ReportsDir, $"{reportNameBase}_parts");
|
||||
if (!Directory.Exists(partsFolder))
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(ResourceDir, "*_custom.*", SearchOption.AllDirectories))
|
||||
Directory.CreateDirectory(partsFolder);
|
||||
}
|
||||
|
||||
// ====== part01:config + resource + cache + debug 根目录文件 ======
|
||||
List<string> part01Files = [];
|
||||
|
||||
string[] categories = ["config", "resource", "cache"];
|
||||
foreach (string category in categories)
|
||||
{
|
||||
string categoryPath = Path.Combine(tempPath, category);
|
||||
if (Directory.Exists(categoryPath))
|
||||
{
|
||||
string relativePath = Path.GetRelativePath(ResourceDir, file);
|
||||
string dest = Path.Combine(resourceTempPath, relativePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
|
||||
File.Copy(file, dest, overwrite: true);
|
||||
part01Files.AddRange(Directory.EnumerateFiles(categoryPath, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历 cache 文件夹下的文件,复制到 tempPath/cache
|
||||
string cacheResourceDir = PathsHelper.CacheDir;
|
||||
if (Directory.Exists(cacheResourceDir))
|
||||
string debugPath = Path.Combine(tempPath, "debug");
|
||||
if (Directory.Exists(debugPath))
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(cacheResourceDir, "*", SearchOption.AllDirectories))
|
||||
// 只取 debug 根目录文件
|
||||
var debugRootFiles = Directory.EnumerateFiles(debugPath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
part01Files.AddRange(debugRootFiles);
|
||||
}
|
||||
|
||||
if (part01Files.Count > 0)
|
||||
{
|
||||
string part01Path = Path.Combine(partsFolder, "part01.zip");
|
||||
using var fs = new FileStream(part01Path, FileMode.Create);
|
||||
using var archive = new ZipArchive(fs, ZipArchiveMode.Create);
|
||||
foreach (var file in part01Files)
|
||||
{
|
||||
string relativePath = Path.GetRelativePath(cacheResourceDir, file);
|
||||
string dest = Path.Combine(tempPath, "cache", relativePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
|
||||
File.Copy(file, dest, overwrite: true);
|
||||
string entryName = Path.GetRelativePath(tempPath, file).Replace("\\", "/");
|
||||
var entry = archive.CreateEntry(entryName, CompressionLevel.SmallestSize);
|
||||
|
||||
using var entryStream = entry.Open();
|
||||
using var fileStream = File.OpenRead(file);
|
||||
fileStream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
|
||||
using (FileStream zipToOpen = new FileStream(zipPath, FileMode.Create))
|
||||
// ====== part02+:debug 子目录文件按 PartSize 分卷 ======
|
||||
var debugSubFiles = Directory.EnumerateFiles(debugPath, "*", SearchOption.AllDirectories)
|
||||
.Where(f => Path.GetDirectoryName(f) != debugPath).ToList();
|
||||
|
||||
int partNumber = 2;
|
||||
while (debugSubFiles.Count > 0)
|
||||
{
|
||||
using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Create);
|
||||
foreach (var file in Directory.EnumerateFiles(tempPath, "*", SearchOption.AllDirectories))
|
||||
string partFileName = $"part{partNumber:D2}.zip";
|
||||
string partPath = Path.Combine(partsFolder, partFileName);
|
||||
|
||||
using (var fs = new FileStream(partPath, FileMode.Create))
|
||||
{
|
||||
string entryName = Path.GetRelativePath(tempPath, file);
|
||||
archive.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
|
||||
using var archive = new ZipArchive(fs, ZipArchiveMode.Create);
|
||||
long currentSize = 0;
|
||||
List<string> processedFiles = [];
|
||||
|
||||
foreach (var file in debugSubFiles.ToList())
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
|
||||
if (currentSize + fileInfo.Length > PartSize && currentSize > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string entryName = Path.GetRelativePath(tempPath, file).Replace("\\", "/");
|
||||
var entry = archive.CreateEntry(entryName, CompressionLevel.SmallestSize);
|
||||
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
using var fileStream = File.OpenRead(file);
|
||||
fileStream.CopyTo(entryStream);
|
||||
}
|
||||
|
||||
currentSize += fileInfo.Length;
|
||||
processedFiles.Add(file);
|
||||
}
|
||||
|
||||
debugSubFiles.RemoveAll(f => processedFiles.Contains(f));
|
||||
}
|
||||
|
||||
partNumber++;
|
||||
}
|
||||
|
||||
// ====== 生成完整压缩包 ======
|
||||
string fullZipPath = Path.Combine(PathsHelper.ReportsDir, $"{reportNameBase}.zip");
|
||||
ZipFile.CreateFromDirectory(tempPath, fullZipPath, CompressionLevel.SmallestSize, includeBaseDirectory: false);
|
||||
|
||||
// 清理临时目录
|
||||
Directory.Delete(tempPath, recursive: true);
|
||||
ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadSuccessful")}\n{reportFileName}");
|
||||
|
||||
ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadSuccessful")}\n{fullZipPath}");
|
||||
OpenDebugFolder();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadException")}\n{ex.Message}");
|
||||
Log.Error(ex, "Failed to create log zip");
|
||||
Log.Error(ex, "Failed to create support payload");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 sourceDir 复制文件到 targetDir,支持过滤。
|
||||
/// </summary>
|
||||
private static void CopyDirectoryIfExists(string? sourceDir, string targetDir, Func<string, bool>? filter = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sourceDir) || !Directory.Exists(sourceDir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(sourceDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (filter != null && !filter(file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string relative = Path.GetRelativePath(sourceDir, file);
|
||||
string dest = Path.Combine(targetDir, relative);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
|
||||
try
|
||||
{
|
||||
File.Copy(file, dest, true);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// 某些文件可能被占用,忽略复制失败
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// 也忽略权限问题
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user