Blazor Server应用中如何在日志中获取用户信息?
在Blazor Server中为ILogger自动添加用户信息的可行方案
核心思路
Blazor Server的每个用户电路对应Scoped生命周期的服务实例,我们可以利用这一特性,通过Scoped服务安全获取用户信息,并自动为日志添加包含用户标识的Scope,无需手动修改每一处日志调用。
分步实现
1. 创建Scoped用户信息服务
该服务负责异步获取当前用户信息,避免在单例服务中直接注入Scoped依赖导致的生命周期冲突:
public class CurrentUserService { private readonly AuthenticationStateProvider _authStateProvider; private AuthenticationState? _cachedAuthState; public CurrentUserService(AuthenticationStateProvider authStateProvider) { _authStateProvider = authStateProvider; } public async Task<string?> GetUserEmailAsync() { _cachedAuthState ??= await _authStateProvider.GetAuthenticationStateAsync(); return _cachedAuthState.User.Identity?.IsAuthenticated == true ? _cachedAuthState.User.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value : null; } }
2. 创建Scoped日志Scope管理器
该服务会在作用域启动时自动创建包含用户信息的日志Scope,作用域销毁时自动释放:
public class LogUserScopeManager : IAsyncDisposable { private readonly ILogger<LogUserScopeManager> _logger; private readonly CurrentUserService _currentUserService; private IDisposable? _activeScope; public LogUserScopeManager(ILogger<LogUserScopeManager> logger, CurrentUserService currentUserService) { _logger = logger; _currentUserService = currentUserService; _ = InitializeUserScopeAsync(); } private async Task InitializeUserScopeAsync() { try { var userEmail = await _currentUserService.GetUserEmailAsync(); if (!string.IsNullOrWhiteSpace(userEmail)) { _activeScope = _logger.BeginScope(new Dictionary<string, object> { ["UserEmail"] = userEmail }); } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to attach user info to log scope"); } } public async ValueTask DisposeAsync() { _activeScope?.Dispose(); GC.SuppressFinalize(this); } }
3. 注册服务到DI容器
在Program.cs中添加Scoped服务注册:
builder.Services.AddScoped<CurrentUserService>(); builder.Services.AddScoped<LogUserScopeManager>();
4. 触发Scope初始化
Blazor的Scoped服务不会自动初始化,需在根组件(如MainLayout.razor)中注入LogUserScopeManager,确保每个用户电路启动时创建日志Scope:
@inject LogUserScopeManager LogUserScopeManager <!-- 原有布局内容 -->
效果验证
所有通过ILogger输出的日志(包括框架日志、第三方组件日志)都会自动携带UserEmail标识,例如在日志输出中会看到:
Information: 00:00:00.000 [UserEmail: user@example.com] 执行用户操作 Error: 00:00:00.000 [UserEmail: user@example.com] 操作触发异常
原方案失败原因说明
- ITelemetryInitializer/Serilog增强器:这类组件默认是单例生命周期,无法直接注入Scoped的
AuthenticationStateProvider,会触发DI生命周期冲突。 - IHttpContextAccessor:Blazor Server中HttpContext仅在初始连接阶段存在,后续SignalR通信时HttpContext为空,导致用户信息获取不稳定。
- 提前调用GetAuthenticationStateAsync:在AuthenticationStateProvider完成初始化前调用该方法,会触发
GetAuthenticationStateAsync was called before SetAuthenticationState错误。
内容的提问来源于stack exchange,提问作者Dalton Keightley
相关产品推荐
相关产品推荐

