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

使用Serilog日志时,无法向PostgreSQL自定义列写入用户名

问题分析与修复方案

核心问题点

  1. 中间件顺序错误:UseSerilogRequestLogging需要在推送LogContext属性的中间件之后,否则无法捕获到user_name属性。
  2. 用户名判断逻辑bug:原代码中context.User?.Identity?.IsAuthenticated != null || true的条件永远为true,会导致未认证时也尝试取Identity.Name(此时为null),逻辑完全错误。
  3. 未启用LogContext enrichment:Serilog默认不会从LogContext提取属性,需要在配置中显式开启。
  4. 自定义列Writer取值逻辑错误:直接调用value?.ToString()会返回带引号的序列化字符串(如"admin"),且解构FirstOrDefault的写法有误。

修复后的代码

1. 修正Serilog配置(添加LogContext enrichment)

Logger log = new LoggerConfiguration()
    .Enrich.FromLogContext() // 关键:启用从LogContext提取属性
    .WriteTo.PostgreSQL(builder.Configuration.GetConnectionString("ConnectionString"),
        "Logs",
        needAutoCreateTable: true,
        columnOptions: new Dictionary<string, ColumnWriterBase>
        {
            {"message", new RenderedMessageColumnWriter(NpgsqlDbType.Text)},
            {"message_template", new MessageTemplateColumnWriter(NpgsqlDbType.Text)},
            {"level", new LevelColumnWriter(true , NpgsqlDbType.Varchar)},
            {"time_stamp", new TimestampColumnWriter(NpgsqlDbType.Timestamp)},
            {"exception", new ExceptionColumnWriter(NpgsqlDbType.Text)},
            {"log_event", new LogEventSerializedColumnWriter(NpgsqlDbType.Json)},
            {"user_name", new UsernameColumnWriter()}
        })
    .WriteTo.Debug()
    .WriteTo.Console()
    .MinimumLevel.Information()
    .CreateLogger();

builder.Host.UseSerilog(log);

2. 修正中间件顺序与逻辑

app.UseHttpLogging();

app.UseAuthentication();
app.UseAuthorization();

// 先推送LogContext属性,再执行SerilogRequestLogging
app.Use(async (context, next) => {
    var username = context.User?.Identity?.IsAuthenticated == true 
        ? context.User.Identity.Name 
        : null;
    using (LogContext.PushProperty("user_name", username)) // 使用using确保属性在请求结束后释放
    {
        await next();
    }
});

app.UseSerilogRequestLogging(); // 移到自定义中间件之后

3. 修正UsernameColumnWriter取值逻辑

public class UsernameColumnWriter : ColumnWriterBase 
{
    public UsernameColumnWriter() : base(NpgsqlDbType.Varchar) { }

    public override object GetValue(LogEvent logEvent, IFormatProvider formatProvider = null) 
    {
        // 直接从Properties字典取值,比FirstOrDefault更高效
        if (logEvent.Properties.TryGetValue("user_name", out var value))
        {
            // 处理字符串类型的属性值,去掉引号
            if (value is ScalarValue scalar && scalar.Value is string username)
            {
                return username;
            }
            // 其他类型直接返回原始值的字符串形式(可选)
            return value?.ToString(null, formatProvider);
        }
        return null;
    }
}

额外说明

  • 使用using (LogContext.PushProperty(...))可以确保在请求结束后自动清除属性,避免上下文污染。
  • 直接从logEvent.Properties字典取值比FirstOrDefault更高效,因为字典是键值对结构。
  • 提取ScalarValue的Value属性可以得到原始字符串,避免序列化后的引号问题。

内容的提问来源于stack exchange,提问作者caner0zkan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:15:24