#0006 更新日志系统,增加 debug文件夹

This commit is contained in:
zhengxuan.zhang
2026-03-13 17:17:47 +08:00
parent 029752e231
commit 59fc0d64d7
32 changed files with 645 additions and 565 deletions
-90
View File
@@ -1,90 +0,0 @@
using System;
namespace XplorePlane.Services
{
/// <summary>
/// 日志服务接口
/// </summary>
public interface ILoggerService
{
/// <summary>
/// 为指定模块创建日志器(使用泛型自动推断类型名)
/// </summary>
ILoggerService ForModule<T>();
/// <summary>
/// 为指定模块创建日志器(手动指定模块名)
/// </summary>
ILoggerService ForModule(string moduleName);
/// <summary>
/// 记录调试信息
/// </summary>
void Debug(string message);
/// <summary>
/// 记录调试信息(带参数)
/// </summary>
void Debug(string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录一般信息
/// </summary>
void Info(string message);
/// <summary>
/// 记录一般信息(带参数)
/// </summary>
void Info(string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录警告信息
/// </summary>
void Warn(string message);
/// <summary>
/// 记录警告信息(带参数)
/// </summary>
void Warn(string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录错误信息
/// </summary>
void Error(string message);
/// <summary>
/// 记录错误信息(带参数)
/// </summary>
void Error(string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录错误信息(带异常)
/// </summary>
void Error(Exception exception, string message);
/// <summary>
/// 记录错误信息(带异常和参数)
/// </summary>
void Error(Exception exception, string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录致命错误
/// </summary>
void Fatal(string message);
/// <summary>
/// 记录致命错误(带参数)
/// </summary>
void Fatal(string messageTemplate, params object[] propertyValues);
/// <summary>
/// 记录致命错误(带异常)
/// </summary>
void Fatal(Exception exception, string message);
/// <summary>
/// 记录致命错误(带异常和参数)
/// </summary>
void Fatal(Exception exception, string messageTemplate, params object[] propertyValues);
}
}
@@ -1,35 +1,36 @@
using System;
using Serilog;
using Serilog.Core;
using XplorePlane.Common.Logging.Interfaces;
namespace XplorePlane.Services
{
/// <summary>
/// 日志服务实现
/// 日志服务适配器
/// 将 Serilog ILogger 适配为 XplorePlane.Common.ILoggerService 接口
/// </summary>
public class LoggerService : ILoggerService
public class LoggerServiceAdapter : ILoggerService
{
private readonly ILogger _logger;
private readonly string _moduleName;
/// <summary>
/// 构造函数
/// 构造函数(使用全局 Logger
/// </summary>
public LoggerService() : this(Log.Logger, null)
public LoggerServiceAdapter() : this(Log.Logger, null)
{
}
/// <summary>
/// 构造函数(指定 Serilog Logger
/// </summary>
public LoggerService(ILogger logger) : this(logger, null)
public LoggerServiceAdapter(ILogger logger) : this(logger, null)
{
}
/// <summary>
/// 私有构造函数(用于创建带模块名的实例)
/// </summary>
private LoggerService(ILogger logger, string moduleName)
private LoggerServiceAdapter(ILogger logger, string moduleName)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_moduleName = moduleName;
@@ -41,7 +42,8 @@ namespace XplorePlane.Services
public ILoggerService ForModule<T>()
{
var typeName = typeof(T).FullName ?? typeof(T).Name;
return new LoggerService(_logger, typeName);
var contextLogger = _logger.ForContext("SourceContext", typeName);
return new LoggerServiceAdapter(contextLogger, typeName);
}
/// <summary>
@@ -52,99 +54,86 @@ namespace XplorePlane.Services
if (string.IsNullOrWhiteSpace(moduleName))
throw new ArgumentException("模块名不能为空", nameof(moduleName));
return new LoggerService(_logger, moduleName);
var contextLogger = _logger.ForContext("SourceContext", moduleName);
return new LoggerServiceAdapter(contextLogger, moduleName);
}
/// <summary>
/// 获取带模块名的日志器
/// 获取当前日志器
/// </summary>
private ILogger GetLogger()
{
if (string.IsNullOrEmpty(_moduleName))
return _logger;
return _logger.ForContext("Module", _moduleName);
return _logger;
}
public void Debug(string message)
{
GetLogger().Debug(FormatMessage(message));
GetLogger().Debug(message);
}
public void Debug(string messageTemplate, params object[] propertyValues)
{
GetLogger().Debug(FormatMessage(messageTemplate), propertyValues);
GetLogger().Debug(messageTemplate, propertyValues);
}
public void Info(string message)
{
GetLogger().Information(FormatMessage(message));
GetLogger().Information(message);
}
public void Info(string messageTemplate, params object[] propertyValues)
{
GetLogger().Information(FormatMessage(messageTemplate), propertyValues);
GetLogger().Information(messageTemplate, propertyValues);
}
public void Warn(string message)
{
GetLogger().Warning(FormatMessage(message));
GetLogger().Warning(message);
}
public void Warn(string messageTemplate, params object[] propertyValues)
{
GetLogger().Warning(FormatMessage(messageTemplate), propertyValues);
GetLogger().Warning(messageTemplate, propertyValues);
}
public void Error(string message)
{
GetLogger().Error(FormatMessage(message));
GetLogger().Error(message);
}
public void Error(string messageTemplate, params object[] propertyValues)
{
GetLogger().Error(FormatMessage(messageTemplate), propertyValues);
GetLogger().Error(messageTemplate, propertyValues);
}
public void Error(Exception exception, string message)
{
GetLogger().Error(exception, FormatMessage(message));
GetLogger().Error(exception, message);
}
public void Error(Exception exception, string messageTemplate, params object[] propertyValues)
{
GetLogger().Error(exception, FormatMessage(messageTemplate), propertyValues);
GetLogger().Error(exception, messageTemplate, propertyValues);
}
public void Fatal(string message)
{
GetLogger().Fatal(FormatMessage(message));
GetLogger().Fatal(message);
}
public void Fatal(string messageTemplate, params object[] propertyValues)
{
GetLogger().Fatal(FormatMessage(messageTemplate), propertyValues);
GetLogger().Fatal(messageTemplate, propertyValues);
}
public void Fatal(Exception exception, string message)
{
GetLogger().Fatal(exception, FormatMessage(message));
GetLogger().Fatal(exception, message);
}
public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues)
{
GetLogger().Fatal(exception, FormatMessage(messageTemplate), propertyValues);
}
/// <summary>
/// 格式化消息(添加模块名前缀)
/// </summary>
private string FormatMessage(string message)
{
if (string.IsNullOrEmpty(_moduleName))
return message;
return $"[{_moduleName}] {message}";
GetLogger().Fatal(exception, messageTemplate, propertyValues);
}
}
}
-139
View File
@@ -1,139 +0,0 @@
using System;
using System.Threading.Tasks;
namespace XplorePlane.Services
{
/// <summary>
/// 日志服务使用示例
/// </summary>
public class LoggingExample
{
private readonly ILoggerService _logger;
public LoggingExample(ILoggerService logger)
{
// 推荐方式:使用泛型自动推断类型名
_logger = logger?.ForModule<LoggingExample>() ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 基本日志示例
/// </summary>
public void BasicLoggingExample()
{
// 调试信息
_logger.Debug("这是调试信息");
// 一般信息
_logger.Info("应用程序启动成功");
// 警告信息
_logger.Warn("连接不稳定,正在重试...");
// 错误信息
_logger.Error("操作失败");
// 致命错误
_logger.Fatal("系统崩溃");
}
/// <summary>
/// 结构化日志示例
/// </summary>
public void StructuredLoggingExample()
{
var userId = 12345;
var action = "登录";
var voltage = 150.5f;
var current = 500;
// 使用占位符(推荐)
_logger.Info("用户 {UserId} 执行了操作 {Action}", userId, action);
_logger.Info("设置电压为 {Voltage} kV,电流为 {Current} μA", voltage, current);
// 不推荐:字符串拼接
// _logger.Info($"用户 {userId} 执行了操作 {action}");
}
/// <summary>
/// 异常日志示例
/// </summary>
public async Task ExceptionLoggingExample()
{
try
{
// 模拟操作
await Task.Delay(100);
throw new InvalidOperationException("模拟异常");
}
catch (Exception ex)
{
// 记录异常(带上下文信息)
_logger.Error(ex, "操作失败:参数={Parameter}", "test");
// 或者简单记录
_logger.Error(ex, "操作失败");
// 致命错误
_logger.Fatal(ex, "系统发生致命错误");
}
}
/// <summary>
/// 不同模块名示例
/// </summary>
public void DifferentModuleNameExample(ILoggerService logger)
{
// 方式 1:使用泛型(推荐)
var logger1 = logger.ForModule<LoggingExample>();
logger1.Info("使用泛型推断的模块名");
// 输出: [XplorePlane.Services.LoggingExample] 使用泛型推断的模块名
// 方式 2:手动指定模块名
var logger2 = logger.ForModule("CustomModule");
logger2.Info("使用自定义模块名");
// 输出: [CustomModule] 使用自定义模块名
// 方式 3:不指定模块名
logger.Info("没有模块名");
// 输出: 没有模块名
}
/// <summary>
/// 实际业务场景示例
/// </summary>
public async Task<bool> BusinessScenarioExample()
{
_logger.Info("开始初始化硬件...");
try
{
// 步骤 1
_logger.Debug("步骤 1: 检查硬件连接");
await Task.Delay(100);
// 步骤 2
_logger.Debug("步骤 2: 加载配置");
var config = LoadConfig();
_logger.Info("配置加载成功:{ConfigName}", config);
// 步骤 3
_logger.Debug("步骤 3: 建立连接");
await Task.Delay(100);
_logger.Info("硬件初始化成功");
return true;
}
catch (Exception ex)
{
_logger.Error(ex, "硬件初始化失败");
return false;
}
}
private string LoadConfig()
{
return "DefaultConfig";
}
}
}
@@ -1,94 +0,0 @@
using System;
using System.Threading.Tasks;
namespace XplorePlane.Services
{
/// <summary>
/// 日志系统验证类 - 用于测试日志功能是否正常工作
/// </summary>
public class LoggingVerification
{
private readonly ILoggerService _logger;
public LoggingVerification(ILoggerService logger)
{
_logger = logger?.ForModule<LoggingVerification>() ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 运行完整的日志验证测试
/// </summary>
public async Task RunVerificationAsync()
{
_logger.Info("========================================");
_logger.Info("开始日志系统验证测试");
_logger.Info("========================================");
// 测试 1: 基本日志级别
TestBasicLogLevels();
// 测试 2: 结构化日志
TestStructuredLogging();
// 测试 3: 异常日志
await TestExceptionLoggingAsync();
// 测试 4: 模块名
TestModuleNames();
_logger.Info("========================================");
_logger.Info("日志系统验证测试完成");
_logger.Info("========================================");
}
private void TestBasicLogLevels()
{
_logger.Info("测试 1: 基本日志级别");
_logger.Debug("这是 Debug 级别日志");
_logger.Info("这是 Info 级别日志");
_logger.Warn("这是 Warn 级别日志");
_logger.Error("这是 Error 级别日志");
_logger.Info("✓ 基本日志级别测试完成");
}
private void TestStructuredLogging()
{
_logger.Info("测试 2: 结构化日志");
var userId = 12345;
var userName = "张三";
var voltage = 150.5f;
var current = 500;
var timestamp = DateTime.Now;
_logger.Info("用户登录: UserId={UserId}, UserName={UserName}", userId, userName);
_logger.Info("设置参数: Voltage={Voltage}kV, Current={Current}μA", voltage, current);
_logger.Info("操作时间: {Timestamp:yyyy-MM-dd HH:mm:ss}", timestamp);
_logger.Info("✓ 结构化日志测试完成");
}
private async Task TestExceptionLoggingAsync()
{
_logger.Info("测试 3: 异常日志");
try
{
await Task.Delay(10);
throw new InvalidOperationException("这是一个测试异常");
}
catch (Exception ex)
{
_logger.Error(ex, "捕获到异常: {Message}", ex.Message);
_logger.Info("✓ 异常日志测试完成");
}
}
private void TestModuleNames()
{
_logger.Info("测试 4: 模块名功能");
_logger.Info("当前模块名应该是: XplorePlane.Services.LoggingVerification");
_logger.Info("✓ 模块名测试完成");
}
}
}
+99 -50
View File
@@ -1,75 +1,124 @@
# Services 目录说明
# XplorePlane 服务层
此目录用于存放应用程序的服务层代码,负责封装硬件库的调用和业务逻辑。
## 概述
## 目录结构
本目录包含 XplorePlane 应用程序的服务层实现。服务层负责封装业务逻辑和硬件交互。
```
Services/
├── IHardwareService.cs # 硬件服务接口
├── HardwareService.cs # 硬件服务实现
├── IConfigurationService.cs # 配置服务接口
└── ConfigurationService.cs # 配置服务实现
```
## 日志服务
## 服务说明
XplorePlane 使用 **XplorePlane.Common.dll** 库中封装的日志功能,无需在本项目中重复实现。
### 1. HardwareService
### 使用方式
统一管理所有硬件模块的服务适配器。
#### 1. 通过依赖注入使用 Serilog ILogger
**功能**
- 初始化和关闭所有硬件
- 射线源控制(开关、参数设置)
- 探测器控制(图像采集、配置)
- PLC 通讯(变量读写)
**使用示例**
```csharp
// 在 ViewModel 中注入
public class MainViewModel
using Serilog;
public class MyViewModel
{
private readonly IHardwareService _hardwareService;
private readonly ILogger _logger;
public MainViewModel(IHardwareService hardwareService)
public MyViewModel(ILogger logger)
{
_hardwareService = hardwareService;
_logger = logger;
}
public async Task InitializeAsync()
public void DoSomething()
{
await _hardwareService.InitializeAllAsync();
_logger.Information("执行某个操作");
_logger.Warning("警告信息");
_logger.Error("错误信息");
}
}
```
### 2. ConfigurationService
管理应用程序配置的服务。
**功能**
- 读取 App.config 配置
- 类型安全的配置访问
- 默认值支持
## 依赖注入配置
`App.xaml.cs` 中注册服务:
#### 2. 使用静态 Log 类
```csharp
protected override void RegisterTypes(IContainerRegistry containerRegistry)
using Serilog;
public class MyService
{
// 注册硬件服务
containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
// 注册配置服务
containerRegistry.RegisterSingleton<IConfigurationService, ConfigurationService>();
public void DoWork()
{
Log.Information("开始工作");
Log.Debug("调试信息: {Value}", someValue);
Log.Error(exception, "发生错误");
}
}
```
## 注意事项
#### 3. 为特定模块创建日志器
1. **服务生命周期**:硬件服务应注册为 Singleton,确保全局唯一
2. **异步操作**:所有硬件操作都应使用异步方法
3. **错误处理**:服务层应捕获并记录所有异常
4. **资源释放**:实现 IDisposable 接口,确保资源正确释放
```csharp
using Serilog;
public class RaySourceService
{
private readonly ILogger _logger;
public RaySourceService()
{
// 为当前类创建带上下文的日志器
_logger = Log.ForContext<RaySourceService>();
}
public void Initialize()
{
_logger.Information("初始化射线源");
}
}
```
### 日志配置
日志配置通过 `App.config` 文件进行管理:
```xml
<appSettings>
<!-- 日志配置 -->
<add key="Serilog:LogPath" value="logs"/>
<add key="Serilog:MinimumLevel" value="Information"/>
<add key="Serilog:EnableConsole" value="true"/>
<add key="Serilog:RollingInterval" value="Day"/>
<add key="Serilog:FileSizeLimitMB" value="100"/>
<add key="Serilog:RetainedFileCountLimit" value="30"/>
</appSettings>
```
### 日志级别
- **Verbose**: 最详细的日志,用于开发调试
- **Debug**: 调试信息
- **Information**: 一般信息(默认)
- **Warning**: 警告信息
- **Error**: 错误信息
- **Fatal**: 致命错误
### 日志输出
日志会输出到以下位置:
- 文件: `logs/xploreplane-{Date}.log`
- 控制台: 开发时可见
- 调试输出: Debug 模式下可见
## 硬件服务
硬件服务将在后续实现,用于封装与硬件库的交互。
### 计划实现的服务
- `IHardwareService`: 硬件服务总接口
- `RaySourceService`: 射线源控制服务
- `DetectorService`: 探测器控制服务
- `PLCService`: PLC 通讯服务
## 依赖库
- **XplorePlane.Common.dll**: 通用基础设施库(包含日志功能)
- **XplorePlane.Hardware.RaySource.dll**: 射线源控制库
- **XplorePlane.Hardware.Detector.dll**: 探测器控制库
- **XplorePlane.Hardware.PLC.dll**: PLC 通讯库
所有 DLL 文件位于 `Libs/Hardware/` 目录下。
-133
View File
@@ -1,133 +0,0 @@
using System;
using System.Configuration;
using System.IO;
using Serilog;
using Serilog.Events;
namespace XplorePlane.Services
{
/// <summary>
/// Serilog 配置类
/// </summary>
public static class SerilogConfig
{
/// <summary>
/// 配置 Serilog
/// </summary>
public static void Configure()
{
// 读取配置
var logPath = GetConfigValue("Serilog:LogPath", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"));
var minimumLevel = GetLogLevel(GetConfigValue("Serilog:MinimumLevel", "Information"));
var enableConsole = GetConfigValue("Serilog:EnableConsole", "true").ToLower() == "true";
var rollingInterval = GetRollingInterval(GetConfigValue("Serilog:RollingInterval", "Day"));
var fileSizeLimitMB = int.Parse(GetConfigValue("Serilog:FileSizeLimitMB", "100"));
var retainedFileCountLimit = int.Parse(GetConfigValue("Serilog:RetainedFileCountLimit", "30"));
// 确保日志目录存在
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
// 配置 Serilog
var loggerConfig = new LoggerConfiguration()
.MinimumLevel.Is(minimumLevel)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", "XplorePlane")
.Enrich.WithProperty("MachineName", Environment.MachineName)
.WriteTo.File(
path: Path.Combine(logPath, "xploreplane-.log"),
rollingInterval: rollingInterval,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
fileSizeLimitBytes: fileSizeLimitMB * 1024 * 1024,
retainedFileCountLimit: retainedFileCountLimit,
shared: true
);
// 添加控制台输出
if (enableConsole)
{
loggerConfig.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
);
}
// 添加调试输出(仅在 Debug 模式)
#if DEBUG
loggerConfig.WriteTo.Debug(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
);
#endif
// 创建全局 Logger
Log.Logger = loggerConfig.CreateLogger();
Log.Information("========================================");
Log.Information("XplorePlane 应用程序启动");
Log.Information("日志路径: {LogPath}", logPath);
Log.Information("日志级别: {MinimumLevel}", minimumLevel);
Log.Information("========================================");
}
/// <summary>
/// 关闭并刷新日志
/// </summary>
public static void CloseAndFlush()
{
Log.Information("========================================");
Log.Information("XplorePlane 应用程序退出");
Log.Information("========================================");
Log.CloseAndFlush();
}
/// <summary>
/// 获取配置值
/// </summary>
private static string GetConfigValue(string key, string defaultValue)
{
try
{
return ConfigurationManager.AppSettings[key] ?? defaultValue;
}
catch
{
return defaultValue;
}
}
/// <summary>
/// 获取日志级别
/// </summary>
private static LogEventLevel GetLogLevel(string level)
{
return level.ToLower() switch
{
"verbose" => LogEventLevel.Verbose,
"debug" => LogEventLevel.Debug,
"information" => LogEventLevel.Information,
"warning" => LogEventLevel.Warning,
"error" => LogEventLevel.Error,
"fatal" => LogEventLevel.Fatal,
_ => LogEventLevel.Information
};
}
/// <summary>
/// 获取滚动间隔
/// </summary>
private static RollingInterval GetRollingInterval(string interval)
{
return interval.ToLower() switch
{
"infinite" => RollingInterval.Infinite,
"year" => RollingInterval.Year,
"month" => RollingInterval.Month,
"day" => RollingInterval.Day,
"hour" => RollingInterval.Hour,
"minute" => RollingInterval.Minute,
_ => RollingInterval.Day
};
}
}
}