using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using XP.Common.Dump.Configs;
using XP.Common.Dump.Interfaces;
using XP.Common.Dump.Native;
using XP.Common.Logging.Interfaces;
namespace XP.Common.Dump.Implementations
{
///
/// Dump 文件管理服务实现,负责 Dump 生成、崩溃事件订阅、定时调度和清理
/// Dump file management service implementation, handles dump generation, crash event subscription, scheduled tasks and cleanup
///
public class DumpService : IDumpService
{
private readonly DumpConfig _config;
private readonly ILoggerService _logger;
private readonly DumpCleaner _cleaner;
private Timer? _scheduledTimer;
private Timer? _cleanupTimer;
private bool _isStarted;
private bool _disposed;
///
/// 默认存储路径 | Default storage path
///
private const string DefaultStoragePath = @"D:\XplorePlane\Dump";
public DumpService(DumpConfig config, ILoggerService logger)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
_cleaner = new DumpCleaner(config, logger);
}
///
/// 生成 Dump 文件名(纯函数)| Generate dump file name (pure function)
/// 格式:XplorePlane_{yyyyMMdd_HHmm}_{TriggerType}.dmp
///
/// 触发类型 | Trigger type
/// 时间戳 | Timestamp
/// 文件名 | File name
internal static string GenerateFileName(DumpTriggerType triggerType, DateTime timestamp)
{
return $"XplorePlane_{timestamp:yyyyMMdd_HHmm}_{triggerType}.dmp";
}
///
/// 验证 Mini Dump 文件大小是否在限制范围内 | Validate Mini Dump file size is within limit
///
/// 文件大小(字节)| File size in bytes
/// 大小限制(MB)| Size limit in MB
/// true 表示在限制内 | true if within limit
internal static bool ValidateFileSize(long fileSizeBytes, long sizeLimitMB)
{
return fileSizeBytes <= sizeLimitMB * 1024 * 1024;
}
///
/// 写入 Dump 文件的核心方法 | Core method to write dump file
///
/// 触发类型 | Trigger type
/// 是否为 Mini Dump | Whether it is a Mini Dump
/// 生成的文件完整路径,失败返回 null | Full path of generated file, null on failure
private string? WriteDump(DumpTriggerType triggerType, bool isMiniDump)
{
try
{
// 非手动触发请求 Full Dump 时,降级为 Mini Dump | Downgrade to Mini Dump for non-manual Full Dump requests
if (!isMiniDump && triggerType != DumpTriggerType.Manual)
{
_logger.Warn("非手动触发不允许生成 Full Dump,已降级为 Mini Dump:{TriggerType} | Non-manual trigger cannot generate Full Dump, downgraded to Mini Dump: {TriggerType}", triggerType);
isMiniDump = true;
}
var fileName = GenerateFileName(triggerType, DateTime.Now);
var filePath = Path.Combine(_config.StoragePath, fileName);
// 确定 Dump 类型标志 | Determine dump type flags
uint dumpType = isMiniDump
? (uint)(MiniDumpType.MiniDumpWithDataSegs | MiniDumpType.MiniDumpWithHandleData | MiniDumpType.MiniDumpWithThreadInfo)
: (uint)MiniDumpType.MiniDumpWithFullMemory;
var process = Process.GetCurrentProcess();
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
bool success = NativeMethods.MiniDumpWriteDump(
process.Handle,
(uint)process.Id,
fileStream.SafeFileHandle,
dumpType,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
var errorCode = Marshal.GetLastWin32Error();
_logger.Error(null!, "MiniDumpWriteDump 调用失败,错误码:{ErrorCode} | MiniDumpWriteDump failed, error code: {ErrorCode}", errorCode);
// 清理失败的文件 | Clean up failed file
try { File.Delete(filePath); } catch { }
return null;
}
}
// Mini Dump 文件大小检查 | Mini Dump file size check
if (isMiniDump)
{
var fileInfo = new FileInfo(filePath);
if (!ValidateFileSize(fileInfo.Length, _config.MiniDumpSizeLimitMB))
{
_logger.Warn("Mini Dump 文件超过大小限制({SizeMB}MB),已删除:{FilePath} | Mini Dump file exceeds size limit ({SizeMB}MB), deleted: {FilePath}",
_config.MiniDumpSizeLimitMB, filePath);
File.Delete(filePath);
return null;
}
}
var dumpTypeStr = isMiniDump ? "Mini" : "Full";
_logger.Info("Dump 文件已生成:{FilePath},触发类型:{TriggerType},Dump 类型:{DumpType} | Dump file generated: {FilePath}, trigger: {TriggerType}, type: {DumpType}",
filePath, triggerType, dumpTypeStr);
return filePath;
}
catch (IOException ex)
{
_logger.Error(ex, "Dump 文件写入失败:{Message} | Dump file write failed: {Message}", ex.Message);
return null;
}
catch (UnauthorizedAccessException ex)
{
_logger.Error(ex, "Dump 文件写入权限不足:{Message} | Dump file write permission denied: {Message}", ex.Message);
return null;
}
catch (Exception ex)
{
_logger.Error(ex, "Dump 文件生成异常:{Message} | Dump file generation error: {Message}", ex.Message);
return null;
}
}
#region 手动触发方法 | Manual trigger methods
///
public string? CreateMiniDump()
{
_logger.Info("手动触发 Mini Dump 生成 | Manually triggering Mini Dump generation");
var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: true);
if (filePath != null)
{
_logger.Info("手动 Mini Dump 已生成:{FilePath} | Manual Mini Dump generated: {FilePath}", filePath);
}
return filePath;
}
///
public string? CreateFullDump()
{
_logger.Info("手动触发 Full Dump 生成 | Manually triggering Full Dump generation");
var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: false);
if (filePath != null)
{
_logger.Info("手动 Full Dump 已生成:{FilePath} | Manual Full Dump generated: {FilePath}", filePath);
}
return filePath;
}
#endregion
#region 生命周期管理 | Lifecycle management
///
public void Start()
{
if (_isStarted) return;
_logger.Info("Dump 服务启动中 | Dump service starting");
// 确保存储目录存在 | Ensure storage directory exists
EnsureStorageDirectory();
// 订阅崩溃事件 | Subscribe to crash events
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
// 启动定时 Dump 计时器 | Start scheduled dump timer
if (_config.EnableScheduledDump)
{
var intervalMs = _config.ScheduledIntervalMinutes * 60 * 1000;
_scheduledTimer = new Timer(OnScheduledDumpCallback, null, intervalMs, intervalMs);
_logger.Info("定时 Dump 已启用,间隔:{Interval} 分钟 | Scheduled dump enabled, interval: {Interval} minutes", _config.ScheduledIntervalMinutes);
}
// 启动每日清理计时器(每24小时执行一次)| Start daily cleanup timer (every 24 hours)
var dailyMs = 24 * 60 * 60 * 1000;
_cleanupTimer = new Timer(OnCleanupCallback, null, dailyMs, dailyMs);
// 立即执行一次清理 | Execute cleanup immediately
_cleaner.CleanExpiredFiles();
_isStarted = true;
_logger.Info("Dump 服务已启动 | Dump service started");
}
///
public void Stop()
{
if (!_isStarted) return;
_logger.Info("Dump 服务停止中 | Dump service stopping");
// 取消事件订阅 | Unsubscribe from events
AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException;
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
// 停止所有计时器 | Stop all timers
_scheduledTimer?.Change(Timeout.Infinite, Timeout.Infinite);
_cleanupTimer?.Change(Timeout.Infinite, Timeout.Infinite);
_isStarted = false;
_logger.Info("Dump 服务已停止 | Dump service stopped");
}
///
public void Dispose()
{
if (_disposed) return;
Stop();
_scheduledTimer?.Dispose();
_scheduledTimer = null;
_cleanupTimer?.Dispose();
_cleanupTimer = null;
_disposed = true;
}
#endregion
#region 内部方法 | Internal methods
///
/// 确保存储目录存在,不存在则创建,创建失败回退默认路径
/// Ensure storage directory exists, create if not, fallback to default on failure
///
private void EnsureStorageDirectory()
{
try
{
if (!Directory.Exists(_config.StoragePath))
{
Directory.CreateDirectory(_config.StoragePath);
_logger.Info("已创建存储目录:{Path} | Created storage directory: {Path}", _config.StoragePath);
}
}
catch (Exception ex)
{
_logger.Error(ex, "存储目录创建失败:{Path},回退到默认路径 | Storage directory creation failed: {Path}, falling back to default", _config.StoragePath);
_config.StoragePath = DefaultStoragePath;
try
{
if (!Directory.Exists(DefaultStoragePath))
{
Directory.CreateDirectory(DefaultStoragePath);
_logger.Info("已创建默认存储目录:{Path} | Created default storage directory: {Path}", DefaultStoragePath);
}
}
catch (Exception fallbackEx)
{
_logger.Fatal(fallbackEx, "默认存储目录创建也失败:{Path} | Default storage directory creation also failed: {Path}", DefaultStoragePath);
}
}
}
///
/// 未处理异常回调 | Unhandled exception callback
///
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
_logger.Info("检测到未处理异常,正在生成崩溃 Dump | Unhandled exception detected, generating crash dump");
WriteDump(DumpTriggerType.Crash, isMiniDump: true);
}
///
/// 未观察到的 Task 异常回调 | Unobserved task exception callback
///
private void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
_logger.Info("检测到未观察的 Task 异常,正在生成崩溃 Dump | Unobserved task exception detected, generating crash dump");
WriteDump(DumpTriggerType.Crash, isMiniDump: true);
}
///
/// 定时 Dump 回调 | Scheduled dump callback
///
private void OnScheduledDumpCallback(object? state)
{
_logger.Debug("定时 Dump 触发 | Scheduled dump triggered");
WriteDump(DumpTriggerType.Scheduled, isMiniDump: true);
}
///
/// 清理回调 | Cleanup callback
///
private void OnCleanupCallback(object? state)
{
_logger.Debug("定时清理触发 | Scheduled cleanup triggered");
_cleaner.CleanExpiredFiles();
}
#endregion
}
}