按作用域/操作读取ASP.NET Core日志并在Blazor中展示
问题描述
我拥有多个ASP.NET BackgroundService,每个服务都在各自的作用域/操作(OP1和OP2)中记录日志。
背景服务代码
MyBackgroundService1
public class MyBackgroundService1 : BackgroundService { private readonly ILogger<MyBackgroundService1> _logger; public MyBackgroundService1(ILogger<MyBackgroundService1> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var activity = new Activity("OP1"); activity.Start(); while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Hello from MyBackgroundService1"); await Task.Delay(5000, stoppingToken); } } }
MyBackgroundService2
public class MyBackgroundService2 : BackgroundService { private readonly ILogger<MyBackgroundService2> _logger; public MyBackgroundService2(ILogger<MyBackgroundService2> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var activity = new Activity("OP2"); activity.Start(); while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Hello from MyBackgroundService2"); await Task.Delay(1000, stoppingToken); } } }
现在希望使用Blazor,为每个操作展示对应的日志表格,示例效果如下:
- OP1日志:
- Hello from MyBackgroundService1
- Hello from MyBackgroundService1
- OP2日志:
- Hello from MyBackgroundService2
- Hello from MyBackgroundService2
请问该如何实现?
实现方案
1. 创建自定义日志提供器与日志状态容器
需要一个能捕获日志并按Activity操作分组的容器,同时支持通知Blazor组件更新:
日志状态容器(支持实时通知)
using System.Collections.ObjectModel; using System.ComponentModel; public class LogContainer : INotifyPropertyChanged { private readonly Dictionary<string, ObservableCollection<string>> _operationLogs = new(); public IReadOnlyDictionary<string, ObservableCollection<string>> OperationLogs => _operationLogs; public void AddLog(string operationName, string message) { if (!_operationLogs.TryGetValue(operationName, out var logs)) { logs = new ObservableCollection<string>(); _operationLogs[operationName] = logs; OnPropertyChanged(nameof(OperationLogs)); } logs.Add(message); } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
自定义日志提供器
using Microsoft.Extensions.Logging; public class OperationLogProvider : ILoggerProvider { private readonly LogContainer _logContainer; public OperationLogProvider(LogContainer logContainer) { _logContainer = logContainer; } public ILogger CreateLogger(string categoryName) { return new OperationLogger(_logContainer); } public void Dispose() { } private class OperationLogger : ILogger { private readonly LogContainer _logContainer; public OperationLogger(LogContainer logContainer) { _logContainer = logContainer; } public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default; public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { if (!IsEnabled(logLevel)) return; // 获取当前Activity的操作名称 var operationName = System.Diagnostics.Activity.Current?.Name; if (string.IsNullOrEmpty(operationName)) return; var message = formatter(state, exception); _logContainer.AddLog(operationName, message); } } }
2. 注册服务到DI容器
在Program.cs中注册日志提供器和日志容器:
builder.Services.AddSingleton<LogContainer>(); builder.Services.AddLogging(builder => { builder.AddProvider(new OperationLogProvider(builder.Services.BuildServiceProvider().GetRequiredService<LogContainer>())); }); // 注册后台服务 builder.Services.AddHostedService<MyBackgroundService1>(); builder.Services.AddHostedService<MyBackgroundService2>();
3. 创建Blazor日志展示组件
创建OperationLogs.razor组件展示每个操作的日志:
@inject LogContainer LogContainer @implements IDisposable <h3>按操作分组日志</h3> @foreach (var operation in LogContainer.OperationLogs) { <div class="mb-4"> <h4>@operation.Key 日志:</h4> <table class="table table-striped"> <thead> <tr> <th>日志内容</th> </tr> </thead> <tbody> @foreach (var log in operation.Value) { <tr> <td>@log</td> </tr> } </tbody> </table> </div> } @code { protected override void OnInitialized() { LogContainer.PropertyChanged += LogContainer_PropertyChanged; } private void LogContainer_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(LogContainer.OperationLogs)) { InvokeAsync(StateHasChanged); } } public void Dispose() { LogContainer.PropertyChanged -= LogContainer_PropertyChanged; } }
4. 在Blazor页面中使用组件
在Index.razor或其他页面引用组件:
@page "/" <OperationLogs />
后台服务产生的日志会自动按操作分组,并实时更新到页面的对应表格中。
内容的提问来源于stack exchange,提问作者Rookian
相关产品推荐
相关产品推荐

