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

如何通过依赖注入在自定义ILogger中使用ISupportExternalScope与中间件

自定义日志组件集成外部作用域的DI实现方案

1. 获取IExternalScopeProvider的正确方式

不需要手动注册IExternalScopeProvider,.NET日志框架已默认将LoggerExternalScopeProvider作为单例服务注册。你可以直接在中间件或其他需要的类中通过构造函数注入该服务。

示例中间件代码:

public class LogContextMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IExternalScopeProvider _scopeProvider;

    public LogContextMiddleware(RequestDelegate next, IExternalScopeProvider scopeProvider)
    {
        _next = next;
        _scopeProvider = scopeProvider;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 从HttpContext提取需注入日志的字段,如TraceId、请求路径等
        var scopeData = new Dictionary<string, object>
        {
            ["TraceId"] = context.TraceIdentifier,
            ["RequestPath"] = context.Request.Path
        };

        // 推入作用域,using块结束后自动释放
        using (_scopeProvider.Push(scopeData))
        {
            await _next(context);
        }
    }
}

2. 让自定义LoggerProvider支持外部作用域

你的MyLoggerProvider需要实现ISupportExternalScope接口,框架会自动将全局的IExternalScopeProvider实例注入进来,无需手动实例化。

示例代码:

public class MyLoggerProvider : ILoggerProvider, ISupportExternalScope
{
    private IExternalScopeProvider _scopeProvider;

    // 实现ISupportExternalScope的SetScopeProvider方法,框架会自动调用此方法注入作用域提供者
    public void SetScopeProvider(IExternalScopeProvider scopeProvider)
    {
        _scopeProvider = scopeProvider;
    }

    public ILogger CreateLogger(string categoryName)
    {
        // 将作用域提供者传递给Logger实例,用于后续读取作用域数据
        return new MyLogger(categoryName, _scopeProvider);
    }

    public void Dispose()
    {
        // 按需清理资源
    }
}

// 自定义Logger接收作用域提供者,用于收集日志作用域数据
public class MyLogger : ILogger
{
    private readonly string _categoryName;
    private readonly IExternalScopeProvider _scopeProvider;

    public MyLogger(string categoryName, IExternalScopeProvider scopeProvider)
    {
        _categoryName = categoryName;
        _scopeProvider = scopeProvider;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        // 委托给外部作用域提供者处理作用域创建
        return _scopeProvider?.Push(state) ?? NullScope.Instance;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        // 按需实现日志级别过滤逻辑
        return true;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        // 收集所有作用域中的自定义字段
        var scopeData = new Dictionary<string, object>();
        _scopeProvider?.ForEachScope((scope, target) =>
        {
            if (scope is Dictionary<string, object> data)
            {
                foreach (var kvp in data)
                {
                    target[kvp.Key] = kvp.Value;
                }
            }
        }, scopeData);

        // 将日志内容与作用域数据序列化为JSON输出
        var logEntry = new
        {
            Category = _categoryName,
            LogLevel = logLevel.ToString(),
            Message = formatter(state, exception),
            Exception = exception?.ToString(),
            Scope = scopeData
        };

        Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(logEntry));
    }
}

3. 注册自定义组件到DI容器

在Program.cs中注册日志提供者和中间件即可,框架会自动识别ISupportExternalScope并完成注入:

var builder = WebApplication.CreateBuilder(args);

// 清空默认日志提供者,添加自定义实现
builder.Logging.ClearProviders();
builder.Logging.AddProvider(new MyLoggerProvider());

// 注册中间件
builder.Services.AddTransient<LogContextMiddleware>();

var app = builder.Build();

// 启用日志上下文中间件
app.UseMiddleware<LogContextMiddleware>();

app.Run();

关键说明

  • IExternalScopeProvider默认是单例注册,无需改为Scoped,其Push方法线程安全,请求级别的作用域会通过using块自动隔离。
  • 实现ISupportExternalScope后,框架初始化日志提供者时会自动调用SetScopeProvider,无需手动处理注入逻辑。
  • 中间件中推入的作用域数据,会在整个请求生命周期内的所有日志中被捕获,直到using块结束自动释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:52:35