如何基于当前DI作用域切换Serilog Sink实现?
解决方案
核心问题在于Serilog的ILogEventSink默认是单例生命周期,无法直接依赖Scoped的IApiClient。要实现需求,需要在日志发送时动态获取当前作用域内的IApiClient实例,具体步骤如下:
1. 调整MySink实现
不再直接注入IApiClient,而是注入IServiceProvider,在Emit方法中获取当前作用域的IApiClient:
public class MySink : ILogEventSink { private readonly IServiceProvider _serviceProvider; public MySink(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void Emit(LogEvent logEvent) { // 获取当前Blazor Circuit对应的Scoped作用域 using var scope = _serviceProvider.CreateScope(); var apiClient = scope.ServiceProvider.GetRequiredService<IApiClient>(); if (apiClient.Authenticated) { apiClient.Log(logEvent); } } }
2. 配置服务与Serilog
在Program.cs中完成依赖注册与Serilog配置:
var builder = WebApplication.CreateBuilder(args); // 注册Scoped生命周期的IApiClient builder.Services.AddScoped<IApiClient, ApiClient>(); // 可选:若要通过HttpContext获取作用域,需注册此服务 builder.Services.AddHttpContextAccessor(); // 配置Serilog,将MySink注册为单例并注入IServiceProvider builder.Host.UseSerilog((context, services, config) => config .WriteTo.Sink(new MySink(services)) .Enrich.FromLogContext()); // 注册Blazor Server基础服务 builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); var app = builder.Build(); // ... 中间件配置与启动逻辑 app.Run();
3. 性能优化(可选)
若日志量较大,可避免每次创建新作用域,直接通过IHttpContextAccessor获取当前Circuit的作用域:
public void Emit(LogEvent logEvent) { var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>(); var httpContext = httpContextAccessor.HttpContext; if (httpContext == null) return; var apiClient = httpContext.RequestServices.GetRequiredService<IApiClient>(); if (apiClient.Authenticated) { apiClient.Log(logEvent); } }
4. 保留原有ILogger使用方式
你依然可以通过@inject ILogger<MyComponent>或构造函数注入的方式使用Microsoft.Extensions.Logging.ILogger,所有日志会自动流经Serilog管道,最终由MySink按当前用户的IApiClient发送到对应API端点。
内容的提问来源于stack exchange,提问作者Adam A
相关产品推荐
相关产品推荐

