# 日志服务使用指南 | Logger Service Usage Guide ## 概述 | Overview XplorePlane 使用 Serilog 作为底层日志框架,通过 `ILoggerService` 接口提供统一的日志服务。 ## 基本用法 | Basic Usage ### 方式 1:自动类型推断(推荐)| Method 1: Auto Type Inference (Recommended) 使用泛型方法 `ForModule()` 自动获取类型的完整名称(命名空间 + 类名): ```csharp public class PlcService { private readonly ILoggerService _logger; public PlcService(ILoggerService logger) { // 自动使用 "XP.Hardware.Plc.Services.PlcService" 作为模块名 // Automatically uses "XP.Hardware.Plc.Services.PlcService" as module name _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); } public void DoSomething() { _logger.Info("执行操作 | Performing operation"); } } ``` ### 方式 2:手动指定模块名 | Method 2: Manual Module Name 如果需要自定义模块名,可以使用字符串参数: ```csharp public class PlcService { private readonly ILoggerService _logger; public PlcService(ILoggerService logger) { // 手动指定简短的模块名 // Manually specify a short module name _logger = logger?.ForModule("PlcService") ?? throw new ArgumentNullException(nameof(logger)); } } ``` ### 方式 3:使用 typeof 获取类型名 | Method 3: Using typeof for Type Name 在静态方法或无法使用泛型的场景: ```csharp public class PlcService { private readonly ILoggerService _logger; public PlcService(ILoggerService logger) { // 使用 typeof 获取类型 // Use typeof to get type _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); } public static void StaticMethod(ILoggerService logger) { // 静态方法中也可以使用泛型 // Can also use generics in static methods var log = logger.ForModule(); log.Info("静态方法日志 | Static method log"); } } ``` ## 日志级别 | Log Levels ```csharp // 调试信息(开发环境)| Debug information (development environment) _logger.Debug("调试信息:变量值={Value} | Debug info: variable value={Value}", someValue); // 一般信息 | General information _logger.Info("操作成功 | Operation successful"); // 警告信息 | Warning information _logger.Warn("连接不稳定 | Connection unstable"); // 错误信息(带异常)| Error information (with exception) _logger.Error(ex, "操作失败:{Message} | Operation failed: {Message}", ex.Message); // 致命错误 | Fatal error _logger.Fatal(ex, "系统崩溃 | System crash"); ``` ## 日志输出格式 | Log Output Format 使用 `ForModule()` 后,日志会自动包含完整的类型信息: ``` 2026-03-12 10:30:15.123 [INF] [XP.Hardware.Plc.Services.PlcService] 正在初始化 PLC 连接... | Initializing PLC connection... 2026-03-12 10:30:16.456 [INF] [XP.Hardware.Plc.Services.PlcService] PLC 连接成功 | PLC connection successful ``` ## 最佳实践 | Best Practices ### 1. 在构造函数中初始化日志器 | Initialize Logger in Constructor ```csharp public class MyService { private readonly ILoggerService _logger; public MyService(ILoggerService logger) { _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); } } ``` ### 2. 使用结构化日志 | Use Structured Logging ```csharp // 好的做法:使用占位符 | Good: use placeholders _logger.Info("用户 {UserId} 执行了操作 {Action} | User {UserId} performed action {Action}", userId, action); // 不好的做法:字符串拼接 | Bad: string concatenation _logger.Info($"用户 {userId} 执行了操作 {action}"); ``` ### 3. 异常日志包含上下文 | Exception Logs Include Context ```csharp try { await _plcClient.ConnectAsync(config); } catch (PlcException ex) { _logger.Error(ex, "PLC 连接失败:地址={Address}, 端口={Port} | PLC connection failed: address={Address}, port={Port}", config.Address, config.Port); throw; } ``` ## 对比:三种方式的输出 | Comparison: Output of Three Methods ```csharp // 方式 1:ForModule() - 完整类型名 // Method 1: ForModule() - Full type name _logger = logger.ForModule(); // 输出 | Output: [XP.Hardware.Plc.Services.PlcService] // 方式 2:ForModule("PlcService") - 自定义名称 // Method 2: ForModule("PlcService") - Custom name _logger = logger.ForModule("PlcService"); // 输出 | Output: [PlcService] // 方式 3:不调用 ForModule - 无模块标记 // Method 3: Don't call ForModule - No module tag _logger = logger; // 输出 | Output: [] (空标记 | empty tag) ``` ## 配置 | Configuration 日志配置在 `App.config` 中设置,通过 `SerilogConfig` 加载: ```xml ```