如何在Serilog Enricher中使用ProtectedSessionStorage?遇依赖注入问题
问题分析与解决方案
错误原因
你遇到的错误源于服务生命周期不匹配:
ProtectedSessionStorage是Scoped服务,其作用域绑定到单个Blazor组件会话或请求;- 而Serilog的
ILogEventEnricher通常在应用启动时就完成初始化(属于根服务容器范畴),根容器无法解析Scoped服务——因为Scoped服务仅存在于特定作用域内,根容器没有对应的上下文环境。
能否访问会话存储?
可以,但不能直接通过构造函数注入的方式在Enricher中使用ProtectedSessionStorage,需要通过间接方式实现。
可行解决方案
方案1:使用Serilog LogContext中转用户信息
这是最稳妥的实现方式,利用LogContext在Blazor组件上下文内将用户信息注入日志上下文,再通过Enricher提取:
- 修改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); } } }
- 在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(); } }
- 确保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
相关产品推荐
相关产品推荐

