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

如何在Serilog Enricher中使用ProtectedSessionStorage?遇依赖注入问题

问题分析与解决方案

错误原因

你遇到的错误源于服务生命周期不匹配:

  • ProtectedSessionStorage是Scoped服务,其作用域绑定到单个Blazor组件会话或请求;
  • 而Serilog的ILogEventEnricher通常在应用启动时就完成初始化(属于根服务容器范畴),根容器无法解析Scoped服务——因为Scoped服务仅存在于特定作用域内,根容器没有对应的上下文环境。

能否访问会话存储?

可以,但不能直接通过构造函数注入的方式在Enricher中使用ProtectedSessionStorage,需要通过间接方式实现。

可行解决方案

方案1:使用Serilog LogContext中转用户信息

这是最稳妥的实现方式,利用LogContext在Blazor组件上下文内将用户信息注入日志上下文,再通过Enricher提取:

  1. 修改Enricher,从LogContext获取用户信息:
public class UserEnricher : ILogEventEnricher
{
    private const string UsernamePropertyName = "Username";

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        if (LogContext.TryGetProperty(UsernamePropertyName, out var existingProperty))
        {
            logEvent.AddPropertyIfAbsent(existingProperty);
        }
    }
}
  1. 在Blazor全局布局组件中(确保所有页面生效),获取会话存储的用户信息并推入LogContext:
@inject ProtectedSessionStorage SessionStorage
@using Serilog.Context

@code {
    protected override async Task OnInitializedAsync()
    {
        var userNameResult = await SessionStorage.GetItemAsync<string>("UsernameKey");
        var userName = userNameResult.Success ? userNameResult.Value : "Anonymous";
        
        // 推入LogContext后,组件及子组件的日志都会携带该属性
        LogContext.PushProperty("Username", userName);
        
        await base.OnInitializedAsync();
    }
}
  1. 确保Serilog配置中启用LogContext增强:
Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext() // 必须添加此行
    .Enrich.With<UserEnricher>()
    // 其他日志配置...
    .CreateLogger();

方案2:手动创建作用域(不推荐)

如果一定要在Enricher中直接访问会话存储,可通过注入IServiceProvider手动创建作用域,但需注意仅在存在Blazor组件上下文时有效(后台任务等场景无法使用):

public class UserEnricher : ILogEventEnricher
{
    private const string UsernamePropertyName = "Username";
    private readonly IServiceProvider _serviceProvider;

    public UserEnricher(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        // 仅在有活跃Blazor作用域时可用,否则会抛出异常
        using var scope = _serviceProvider.CreateScope();
        var sessionStorage = scope.ServiceProvider.GetRequiredService<ProtectedSessionStorage>();
        
        var userNameResult = await sessionStorage.GetItemAsync<string>("UsernameKey");
        var userName = userNameResult.Success ? userNameResult.Value : "Anonymous";
        
        var userNameProperty = propertyFactory.CreateProperty(UsernamePropertyName, userName);
        logEvent.AddPropertyIfAbsent(userNameProperty);
    }
}

这种方式存在明显局限性:若在无Blazor作用域的场景下(如后台定时任务日志),会因无法获取有效ProtectedSessionStorage而报错,因此方案1更可靠。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:17:04