You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

扩展Serilog日志记录器以自动包含用户UserId

解决方案

方法一:Serilog自定义Enricher(推荐)

Serilog支持通过Enricher为所有日志事件自动添加属性,这是最简洁的方案,无需修改现有ILogger调用代码,完全符合结构化日志最佳实践。

1. 实现ILogEventEnricher

创建自定义Enricher,从IUserContextService获取UserId并添加到日志事件:

public class UserIdEnricher : ILogEventEnricher
{
    private readonly IUserContextService _userContextService;

    public UserIdEnricher(IUserContextService userContextService)
    {
        _userContextService = userContextService;
    }

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var userId = _userContextService.GetUserDetails().Id;
        if (!string.IsNullOrEmpty(userId))
        {
            // 仅当UserId存在时添加属性
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserId", userId));
        }
    }
}

2. 注册Enricher到Serilog配置

在Program.cs中配置Serilog时,注入自定义Enricher:

var builder = WebApplication.CreateBuilder(args);

// 先注册基础服务
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IUserContextService, UserContextService>();

// 配置Serilog
builder.Host.UseSerilog((context, services, config) => config
    .ReadFrom.Configuration(context.Configuration)
    .Enrich.FromLogContext()
    // 从DI容器获取自定义Enricher实例
    .Enrich.With(services.GetRequiredService<UserIdEnricher>())
    // 控制台输出模板中包含Properties,方便查看UserId
    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"));

// 后续API配置...
var app = builder.Build();
app.Run();

配置完成后,所有日志会自动带上UserId属性(用户已登录时生效),无需手动在日志消息中传入{userId},日志输出的属性集合中会包含该字段(如控制台输出的{Properties:j}部分会显示"UserId":"xxx")。

方法二:封装ILogger扩展方法

若不想修改Serilog全局配置,可封装扩展方法,让日志调用时自动附加UserId:

1. 编写扩展方法

public static class LoggerUserIdExtensions
{
    public static void LogWarningWithUserId(this ILogger logger, IUserContextService userContextService, string message, params object[] args)
    {
        var userId = userContextService.GetUserDetails().Id;
        // 将UserId作为结构化日志属性传入
        logger.LogWarning("{Message}, UserId: {UserId}", message, userId, args);
    }

    // 同理扩展其他日志级别
    public static void LogInformationWithUserId(this ILogger logger, IUserContextService userContextService, string message, params object[] args)
    {
        var userId = userContextService.GetUserDetails().Id;
        logger.LogInformation("{Message}, UserId: {UserId}", message, userId, args);
    }
}

2. 使用扩展方法

public void DoSomething()
{
    // 直接调用扩展方法,无需手动获取UserId
    _logger.LogWarningWithUserId(_userContextService, "my warning message");
}

方法三:Serilog LogContext(特定场景使用)

如果仅需要在局部代码块中添加UserId,可使用LogContext.PushProperty临时注入属性:

using (LogContext.PushProperty("UserId", _userContextService.GetUserDetails().Id))
{
    _logger.LogWarning("my warning message");
    // 该代码块内的所有日志都会自动带上UserId属性
}

注意:需确保Serilog配置中启用了.Enrich.FromLogContext()才会生效。


总结

优先选择方法一(自定义Enricher),它无需修改现有业务代码,全局统一添加UserId属性,便于日志分析工具提取字段,是最符合Serilog设计理念的方案。

内容的提问来源于stack exchange,提问作者James Andrew Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 10:33:21