Skip to content

日志(基于Serilog的高性能日志记录库)

一个基于Serilog的高性能日志记录库,为GameFrameX框架提供统一的日志记录接口和丰富的日志输出功能。

特性

  • 多级别日志支持 - 支持Verbose、Debug、Info、Warn、Error、Fatal六个日志级别
  • 多输出目标 - 支持文件、控制台、Grafana Loki等多种输出方式
  • 灵活配置 - 通过LogOptions类提供丰富的配置选项
  • 文件滚动 - 支持按时间间隔和文件大小进行日志文件滚动
  • 标签支持 - 支持为日志添加标签,便于分类和过滤
  • 异常记录 - 专门的异常记录方法,包含完整的堆栈跟踪信息
  • 控制台输出 - 支持同时输出到日志文件和控制台
  • 高性能 - 基于Serilog构建,提供高性能的日志记录能力

安装

bash
dotnet add package GameFrameX.Foundation.Logger

快速开始

1. 基本使用

csharp
using GameFrameX.Foundation.Logger;

// 使用默认配置初始化日志系统
var logger = LogHandler.Create(LogOptions.Default);

// 记录不同级别的日志
LogHelper.Info("应用程序启动");
LogHelper.Warn("这是一个警告消息");
LogHelper.Error("发生了一个错误");

2. 自定义配置

csharp
using GameFrameX.Foundation.Logger;

// 创建自定义日志配置
var logOptions = new LogOptions("mylogs")
{
    LogType = "WebApi",
    LogTagName = "Production",
    LogEventLevel = LogEventLevel.Information,
    IsConsole = true,
    RollingInterval = RollingInterval.Hour,
    FileSizeLimitBytes = 50 * 1024 * 1024, // 50MB
    RetainedFileCountLimit = 7 // 保留7个文件
};

// 初始化日志系统
var logger = LogHandler.Create(logOptions);

// 使用日志
LogHelper.Info("服务器", "服务器启动在端口 {Port}", 8080);
LogHelper.InfoConsole("同时输出到文件和控制台的消息");

详细用法

日志级别

支持六个标准的日志级别:

csharp
// Verbose - 最详细的日志信息
LogHelper.Verbose("详细的调试信息");

// Debug - 调试信息
LogHelper.Debug("调试信息: 变量值 = {Value}", someValue);

// Information - 一般信息
LogHelper.Info("用户 {UserId} 登录成功", userId);

// Warning - 警告信息
LogHelper.Warn("磁盘空间不足,剩余: {FreeSpace}MB", freeSpace);

// Error - 错误信息
LogHelper.Error("数据库连接失败: {Error}", errorMessage);

// Fatal - 致命错误
LogHelper.Fatal("应用程序即将崩溃: {Reason}", reason);

异常记录

专门的异常记录方法,自动包含堆栈跟踪:

csharp
try
{
    // 可能抛出异常的代码
    DoSomething();
}
catch (Exception ex)
{
    // 记录异常
    LogHelper.Error(ex);
    
    // 带标签的异常记录
    LogHelper.Error("数据库", ex);
    
    // 自定义异常消息
    LogHelper.Error("处理用户请求时发生错误: {Message}", ex.Message);
}

标签支持

为日志添加标签,便于分类和过滤:

csharp
// 带标签的日志记录
LogHelper.Info("用户管理", "用户 {UserId} 创建成功", userId);
LogHelper.Warn("安全", "检测到可疑登录尝试,IP: {IP}", ipAddress);
LogHelper.Error("支付", "支付处理失败,订单号: {OrderId}", orderId);

// 带标签的控制台输出
LogHelper.InfoConsole("启动", "服务器启动完成,监听端口: {Port}", port);

控制台输出

支持同时输出到日志文件和控制台:

csharp
// 仅输出到日志文件
LogHelper.Info("这条消息只会写入日志文件");

// 同时输出到日志文件和控制台
LogHelper.InfoConsole("这条消息会同时显示在控制台和日志文件中");

// 错误消息的控制台输出(红色显示)
LogHelper.ErrorConsole("这是一个错误消息,控制台中会以红色显示");

// 仅输出到控制台(不写入日志文件)
LogHelper.Console("这条消息只会显示在控制台");

配置

LogOptions 配置类

csharp
var logOptions = new LogOptions("logs") // 日志目录名
{
    // 基本配置
    LogType = "WebServer",              // 服务器类型标识
    LogTagName = "Production",          // 日志标签名
    LogEventLevel = LogEventLevel.Info, // 最低日志级别
    
    // 输出配置
    IsConsole = true,                   // 是否输出到控制台
    
    // 文件配置
    RollingInterval = RollingInterval.Day,    // 滚动间隔(天)
    IsFileSizeLimit = true,                   // 是否限制文件大小
    FileSizeLimitBytes = 100 * 1024 * 1024,   // 文件大小限制(100MB)
    RetainedFileCountLimit = 31,              // 保留文件数量(31个)
    
    // Grafana Loki 配置
    IsGrafanaLoki = false,                    // 是否启用Loki
    GrafanaLokiUrl = "http://localhost:3100", // Loki服务地址
    GrafanaLokiLabels = new Dictionary<string, string>
    {
        ["app"] = "myapp",
        ["env"] = "production"
    },
    GrafanaLokiUsername = "admin",            // Loki用户名
    GrafanaLokiPassword = "password"          // Loki密码
};

滚动间隔选项

csharp
// 支持的滚动间隔
RollingInterval.Infinite    // 不滚动
RollingInterval.Year        // 按年滚动
RollingInterval.Month       // 按月滚动
RollingInterval.Day         // 按天滚动(默认)
RollingInterval.Hour        // 按小时滚动
RollingInterval.Minute      // 按分钟滚动

日志级别配置

csharp
// 支持的日志级别
LogEventLevel.Verbose       // 最详细
LogEventLevel.Debug         // 调试(默认)
LogEventLevel.Information   // 信息
LogEventLevel.Warning       // 警告
LogEventLevel.Error         // 错误
LogEventLevel.Fatal         // 致命错误

高级用法

Grafana Loki 集成

支持将日志发送到Grafana Loki进行集中化日志管理:

csharp
var logOptions = new LogOptions()
{
    IsGrafanaLoki = true,
    GrafanaLokiUrl = "http://loki.example.com:3100",
    GrafanaLokiLabels = new Dictionary<string, string>
    {
        ["service"] = "user-service",
        ["environment"] = "production",
        ["version"] = "1.0.0"
    },
    GrafanaLokiUsername = "your-username",
    GrafanaLokiPassword = "your-password"
};

var logger = LogHandler.Create(logOptions);

自定义日志配置

支持通过回调函数进行更高级的自定义配置:

csharp
var logger = LogHandler.Create(logOptions, true, config =>
{
    // 添加自定义的Sink
    config.WriteTo.Email(
        fromEmail: "noreply@example.com",
        toEmail: "admin@example.com",
        outputTemplate: "{Timestamp} [{Level}] {Message}{NewLine}{Exception}",
        restrictedToMinimumLevel: LogEventLevel.Error
    );
    
    // 添加自定义的Enricher
    config.Enrich.WithProperty("MachineName", Environment.MachineName);
    config.Enrich.WithProperty("ProcessId", Environment.ProcessId);
});

使用自定义Logger实例

csharp
// 创建多个Logger实例
var webLogger = LogHandler.Create(webLogOptions, false);
var dbLogger = LogHandler.Create(dbLogOptions, false);

// 使用特定的Logger实例
LogHelper.Info(webLogger, "Web请求处理完成");
LogHelper.Error(dbLogger, "数据库连接异常", exception);

性能优化

异步日志刷新

csharp
// 同步刷新(阻塞)
LogHelper.FlushAndSave();

// 异步刷新(非阻塞)
LogHelper.CloseAndFlushAsync();

条件日志记录

csharp
// 避免不必要的字符串格式化
if (logger.IsEnabled(LogEventLevel.Debug))
{
    LogHelper.Debug("复杂的调试信息: {Data}", ExpensiveOperation());
}

最佳实践

结构化日志

使用结构化的日志消息,便于后续分析:

csharp
// 好的做法 - 结构化日志
LogHelper.Info("用户登录成功,用户ID: {UserId}, IP: {IP}, 耗时: {Duration}ms", 
    userId, ipAddress, duration);

// 避免的做法 - 字符串拼接
LogHelper.Info($"用户登录成功,用户ID: {userId}, IP: {ipAddress}, 耗时: {duration}ms");

合理使用日志级别

csharp
// Debug - 开发调试信息
LogHelper.Debug("进入方法 ProcessOrder,参数: {OrderId}", orderId);

// Info - 重要的业务事件
LogHelper.Info("订单创建成功,订单号: {OrderId}, 用户: {UserId}", orderId, userId);

// Warn - 可恢复的问题
LogHelper.Warn("重试连接数据库,第 {Attempt} 次尝试", attemptCount);

// Error - 需要关注的错误
LogHelper.Error("处理支付失败,订单: {OrderId}, 错误: {Error}", orderId, error);

// Fatal - 导致应用程序终止的严重错误
LogHelper.Fatal("数据库连接池耗尽,应用程序即将关闭");

使用标签分类

csharp
// 按功能模块分类
LogHelper.Info("用户管理", "用户注册成功: {Email}", email);
LogHelper.Info("订单处理", "订单状态更新: {OrderId} -> {Status}", orderId, status);
LogHelper.Info("支付系统", "支付完成: {Amount} 元", amount);

// 按环境分类
LogHelper.Info("生产环境", "服务器启动完成");
LogHelper.Debug("开发环境", "调试信息: {Data}", debugData);

异常处理

csharp
try
{
    await ProcessOrderAsync(orderId);
    LogHelper.Info("订单处理", "订单 {OrderId} 处理完成", orderId);
}
catch (BusinessException ex)
{
    // 业务异常,记录为警告
    LogHelper.Warn("订单处理", "业务规则验证失败: {Message}", ex.Message);
    throw;
}
catch (Exception ex)
{
    // 系统异常,记录为错误
    LogHelper.Error("订单处理", ex);
    throw;
}

配置管理

csharp
// 开发环境配置
var devLogOptions = new LogOptions("logs")
{
    LogEventLevel = LogEventLevel.Debug,
    IsConsole = true,
    RollingInterval = RollingInterval.Hour
};

// 生产环境配置
var prodLogOptions = new LogOptions("logs")
{
    LogEventLevel = LogEventLevel.Information,
    IsConsole = false,
    RollingInterval = RollingInterval.Day,
    IsGrafanaLoki = true,
    GrafanaLokiUrl = "http://loki.prod.com:3100"
};

// 根据环境选择配置
var logOptions = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" 
    ? devLogOptions 
    : prodLogOptions;

API 参考

LogHandler

方法说明
LogHandler.Create(LogOptions)使用指定配置创建 Logger 实例
LogHandler.Create(LogOptions, bool)创建 Logger 实例,第二个参数控制是否设为全局默认
LogHandler.Create(LogOptions, bool, ``Action<LoggerConfiguration>``)创建 Logger 实例并通过回调自定义 Serilog 配置

LogHelper 日志记录方法

方法说明
LogHelper.Verbose(message)记录 Verbose 级别日志
LogHelper.Debug(message)记录 Debug 级别日志
LogHelper.Info(message)记录 Information 级别日志
LogHelper.Warn(message)记录 Warning 级别日志
LogHelper.Error(message)记录 Error 级别日志
LogHelper.Fatal(message)记录 Fatal 级别日志
LogHelper.Info(tag, message, args)带标签的 Info 日志
LogHelper.Warn(tag, message, args)带标签的 Warn 日志
LogHelper.Error(tag, message, args)带标签的 Error 日志
LogHelper.Error(Exception)记录异常(含堆栈跟踪)
LogHelper.Error(tag, Exception)带标签的异常记录
LogHelper.InfoConsole(message)同时输出到控制台的 Info 日志
LogHelper.ErrorConsole(message)同时输出到控制台的 Error 日志(红色)
LogHelper.Console(message)仅输出到控制台
LogHelper.FlushAndSave()同步刷新并保存日志
LogHelper.CloseAndFlushAsync()异步关闭并刷新日志

LogOptions 主要属性

属性类型说明
LogSavePathstring日志目录路径
LogTypestring服务器类型标识
LogTagNamestring日志标签名
LogEventLevelLogEventLevel最低日志级别
IsConsolebool是否输出到控制台
RollingIntervalRollingInterval文件滚动间隔
FileSizeLimitByteslong单个日志文件大小限制
RetainedFileCountLimitint保留的日志文件数量
IsGrafanaLokibool是否启用 Grafana Loki
GrafanaLokiUrlstringLoki 服务地址
GrafanaLokiLabelsDictionary<string, string>Loki 标签
GrafanaLokiUsernamestringLoki 认证用户名
GrafanaLokiPasswordstringLoki 认证密码

依赖项

  • Serilog.AspNetCore (9.0.0) - 核心日志框架
  • Serilog.Sinks.Console (6.0.0) - 控制台输出
  • Serilog.Sinks.File (7.0.0) - 文件输出
  • Serilog.Sinks.Grafana.Loki (8.3.1) - Grafana Loki集成
  • GameFrameX.Foundation.Json - JSON序列化支持


快来请作者喝奶茶.我喝不惯咖啡





最后更新于: