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

Blazor Server中能否实现将日志输出到浏览器控制台的ILogger?

在Blazor Server中实现输出到浏览器控制台的ILogger

当然可以实现,但你当前的代码存在两个核心问题:

  • 在LoggerProvider中调用BuildServiceProvider()会触发日志系统的初始化,进而导致无限递归循环
  • 直接在Singleton生命周期的LoggerProvider中获取Scoped的IJSRuntime不符合DI的生命周期规则

正确的实现方案

我们可以通过IServiceScopeFactory延迟创建作用域来获取IJSRuntime,避免循环依赖和生命周期冲突。

1. 实现BrowserLogger

public class BrowserLogger : ILogger
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly string _categoryName;

    public BrowserLogger(IServiceScopeFactory scopeFactory, string categoryName)
    {
        _scopeFactory = scopeFactory;
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state) => default!;

    public bool IsEnabled(LogLevel logLevel) => true; // 可根据需求调整过滤逻辑

    public async void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        var message = formatter(state, exception);
        using var scope = _scopeFactory.CreateScope();
        var jsRuntime = scope.ServiceProvider.GetRequiredService<IJSRuntime>();
        
        // 调用JS方法输出到控制台,可根据日志级别调用不同的console方法
        await jsRuntime.InvokeVoidAsync("console.log", $"[{logLevel}] {_categoryName}: {message}");
        if (exception != null)
        {
            await jsRuntime.InvokeVoidAsync("console.error", exception.ToString());
        }
    }
}

2. 实现BrowserLoggerProvider

public class BrowserLoggerProvider : ILoggerProvider
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly Dictionary<string, BrowserLogger> _loggers = new();

    public BrowserLoggerProvider(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public ILogger CreateLogger(string categoryName)
    {
        lock (_loggers)
        {
            if (!_loggers.TryGetValue(categoryName, out var logger))
            {
                logger = new BrowserLogger(_scopeFactory, categoryName);
                _loggers[categoryName] = logger;
            }
            return logger;
        }
    }

    public void Dispose()
    {
        _loggers.Clear();
    }
}

3. 注册日志提供程序

在Program.cs中,我们需要先确保IServiceScopeFactory已注册(Blazor Server默认已注册),然后添加自定义日志提供程序:

builder.Services.AddLogging(builder =>
{
    builder.AddProvider(new BrowserLoggerProvider(builder.Services.BuildServiceProvider().GetRequiredService<IServiceScopeFactory>()));
});

关键注意事项

  • 生命周期匹配:通过IServiceScopeFactory创建临时作用域,确保每次日志输出都能获取到当前请求/连接对应的IJSRuntime实例
  • 后台线程限制:如果在后台线程(非组件上下文、非SignalR连接作用域)中输出日志,IJSRuntime可能无法正常工作,此时可以添加判断逻辑跳过这类日志
  • 日志级别过滤:可以在IsEnabled方法中添加日志级别过滤,避免不必要的JS调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:30:23