#0005 增加日志

This commit is contained in:
zhengxuan.zhang
2026-03-13 16:59:31 +08:00
parent 75cb502d0d
commit 029752e231
18 changed files with 997 additions and 105 deletions
+90
View File
@@ -0,0 +1,90 @@
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);
}
}
+150
View File
@@ -0,0 +1,150 @@
using System;
using Serilog;
using Serilog.Core;
namespace XplorePlane.Services
{
/// <summary>
/// 日志服务实现
/// </summary>
public class LoggerService : ILoggerService
{
private readonly ILogger _logger;
private readonly string _moduleName;
/// <summary>
/// 构造函数
/// </summary>
public LoggerService() : this(Log.Logger, null)
{
}
/// <summary>
/// 构造函数(指定 Serilog Logger
/// </summary>
public LoggerService(ILogger logger) : this(logger, null)
{
}
/// <summary>
/// 私有构造函数(用于创建带模块名的实例)
/// </summary>
private LoggerService(ILogger logger, string moduleName)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_moduleName = moduleName;
}
/// <summary>
/// 为指定模块创建日志器(使用泛型自动推断类型名)
/// </summary>
public ILoggerService ForModule<T>()
{
var typeName = typeof(T).FullName ?? typeof(T).Name;
return new LoggerService(_logger, typeName);
}
/// <summary>
/// 为指定模块创建日志器(手动指定模块名)
/// </summary>
public ILoggerService ForModule(string moduleName)
{
if (string.IsNullOrWhiteSpace(moduleName))
throw new ArgumentException("模块名不能为空", nameof(moduleName));
return new LoggerService(_logger, moduleName);
}
/// <summary>
/// 获取带模块名的日志器
/// </summary>
private ILogger GetLogger()
{
if (string.IsNullOrEmpty(_moduleName))
return _logger;
return _logger.ForContext("Module", _moduleName);
}
public void Debug(string message)
{
GetLogger().Debug(FormatMessage(message));
}
public void Debug(string messageTemplate, params object[] propertyValues)
{
GetLogger().Debug(FormatMessage(messageTemplate), propertyValues);
}
public void Info(string message)
{
GetLogger().Information(FormatMessage(message));
}
public void Info(string messageTemplate, params object[] propertyValues)
{
GetLogger().Information(FormatMessage(messageTemplate), propertyValues);
}
public void Warn(string message)
{
GetLogger().Warning(FormatMessage(message));
}
public void Warn(string messageTemplate, params object[] propertyValues)
{
GetLogger().Warning(FormatMessage(messageTemplate), propertyValues);
}
public void Error(string message)
{
GetLogger().Error(FormatMessage(message));
}
public void Error(string messageTemplate, params object[] propertyValues)
{
GetLogger().Error(FormatMessage(messageTemplate), propertyValues);
}
public void Error(Exception exception, string message)
{
GetLogger().Error(exception, FormatMessage(message));
}
public void Error(Exception exception, string messageTemplate, params object[] propertyValues)
{
GetLogger().Error(exception, FormatMessage(messageTemplate), propertyValues);
}
public void Fatal(string message)
{
GetLogger().Fatal(FormatMessage(message));
}
public void Fatal(string messageTemplate, params object[] propertyValues)
{
GetLogger().Fatal(FormatMessage(messageTemplate), propertyValues);
}
public void Fatal(Exception exception, string message)
{
GetLogger().Fatal(exception, FormatMessage(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}";
}
}
}
+139
View File
@@ -0,0 +1,139 @@
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";
}
}
}
@@ -0,0 +1,94 @@
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("✓ 模块名测试完成");
}
}
}
+75
View File
@@ -0,0 +1,75 @@
# Services 目录说明
此目录用于存放应用程序的服务层代码,负责封装硬件库的调用和业务逻辑。
## 目录结构
```
Services/
├── IHardwareService.cs # 硬件服务接口
├── HardwareService.cs # 硬件服务实现
├── IConfigurationService.cs # 配置服务接口
└── ConfigurationService.cs # 配置服务实现
```
## 服务说明
### 1. HardwareService
统一管理所有硬件模块的服务适配器。
**功能**
- 初始化和关闭所有硬件
- 射线源控制(开关、参数设置)
- 探测器控制(图像采集、配置)
- PLC 通讯(变量读写)
**使用示例**
```csharp
// 在 ViewModel 中注入
public class MainViewModel
{
private readonly IHardwareService _hardwareService;
public MainViewModel(IHardwareService hardwareService)
{
_hardwareService = hardwareService;
}
public async Task InitializeAsync()
{
await _hardwareService.InitializeAllAsync();
}
}
```
### 2. ConfigurationService
管理应用程序配置的服务。
**功能**
- 读取 App.config 配置
- 类型安全的配置访问
- 默认值支持
## 依赖注入配置
`App.xaml.cs` 中注册服务:
```csharp
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// 注册硬件服务
containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
// 注册配置服务
containerRegistry.RegisterSingleton<IConfigurationService, ConfigurationService>();
}
```
## 注意事项
1. **服务生命周期**:硬件服务应注册为 Singleton,确保全局唯一
2. **异步操作**:所有硬件操作都应使用异步方法
3. **错误处理**:服务层应捕获并记录所有异常
4. **资源释放**:实现 IDisposable 接口,确保资源正确释放
+133
View File
@@ -0,0 +1,133 @@
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
};
}
}
}