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

Serilog通过EnrichDiagnosticContext添加的属性未在SignalR Hub中被记录怎么办

问题原因

UseSerilogRequestLogging 仅作用于单次短生命周期的常规HTTP请求管道,而SignalR的核心交互基于长连接:只有初始握手请求会触发该中间件的属性注入,后续所有Hub方法调用、连接断开事件都运行在长连接专属上下文内,不会重新走常规HTTP请求管道,因此DiagnosticContext内的属性不会被自动携带到Hub的执行上下文。

前置配置确认

首先确保你的Serilog初始化配置中已经开启了日志上下文继承:

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext() // 必须配置,否则自定义属性不会随日志上下文传递
    // 其余你的Elasticsearch Sink等配置
    .CreateLogger();

方案1:全局Hub过滤器(推荐)

通过实现IHubFilter可以一次性为所有Hub的所有调用、连接/断开事件注入自定义属性,无需修改业务代码:

  1. 实现Hub过滤器
public class SerilogEnrichHubFilter : IHubFilter
{
    // 处理Hub方法调用的日志属性注入
    public async ValueTask<object> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
    {
        var httpContext = invocationContext.Context.GetHttpContext();
        if (httpContext == null) return await next(invocationContext);
        
        using var scope = LogContext.PushProperty("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress)
                                    .PushProperty("UserName", httpContext.User?.Identity?.Name ?? "(anonymous)");
        return await next(invocationContext);
    }

    // 处理连接建立事件的日志属性注入
    public async Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
    {
        var httpContext = context.Context.GetHttpContext();
        if (httpContext == null)
        {
            await next(context);
            return;
        }

        using var scope = LogContext.PushProperty("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress)
                                    .PushProperty("UserName", httpContext.User?.Identity?.Name ?? "(anonymous)");
        await next(context);
    }

    // 处理连接断开事件的日志属性注入
    public async Task OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
    {
        var httpContext = context.Context.GetHttpContext();
        if (httpContext == null)
        {
            await next(context, exception);
            return;
        }

        using var scope = LogContext.PushProperty("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress)
                                    .PushProperty("UserName", httpContext.User?.Identity?.Name ?? "(anonymous)");
        await next(context, exception);
    }
}
  1. 注册过滤器到SignalR配置
// Startup.cs ConfigureServices 或 Program.cs 服务注册位置
services.AddSignalR(options =>
{
    options.AddFilter<SerilogEnrichHubFilter>();
});

方案2:单Hub/单方法手动注入

如果只需要为部分Hub的特定方法加属性,可以直接在方法内手动获取上下文注入:

public class CustomHub : Hub
{
    private readonly ILogger<CustomHub> _logger;

    public CustomHub(ILogger<CustomHub> logger)
    {
        _logger = logger;
    }

    public async Task CustomOperation()
    {
        var httpContext = Context.GetHttpContext();
        using var scope = LogContext.PushProperty("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress)
                                    .PushProperty("UserName", httpContext.User?.Identity?.Name ?? "(anonymous)");
        
        // 这里输出的日志就会携带自定义属性
        _logger.LogInformation("执行自定义操作");
        // 其余业务逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 18:30:05