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

如何实现ILogger的BeginScope并在Log函数中提取传入值

实现ILogger的BeginScope并在Log中获取Scope值

需求:实现ILogger接口的BeginScope方法,让Log函数能获取到BeginScope传入的值(如示例中的"app1")。现有MyLogger类框架及调用示例如下:

public sealed class MyLogger : ILogger
{
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        // 需要实现这里的逻辑
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        // 要在这里拿到"app1"这类Scope值
    }
}

调用示例:

using (logger.BeginScope("app1"))
    logger.LogInformation("Hello");

实现思路

使用**AsyncLocal<Stack>**存储Scope状态,它能在异步/多线程上下文间正确传递Scope信息,同时支持嵌套Scope的场景。

1. 实现BeginScope

  • 初始化AsyncLocal栈(首次调用时创建),每次调用BeginScope时将传入的state压入栈。
  • 返回自定义的Disposable对象,在Dispose时弹出栈顶的state,保证Scope生命周期结束后清理状态。

2. 在Log中获取Scope值

从AsyncLocal的栈中读取当前栈顶的元素,即为最近一次BeginScope传入的值。

完整代码实现

public sealed class MyLogger : ILogger
{
    // 用AsyncLocal存储Scope栈,支持异步上下文传递
    private readonly AsyncLocal<Stack<object>> _scopeStack = new AsyncLocal<Stack<object>>();

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        // 初始化栈(如果还未创建)
        _scopeStack.Value ??= new Stack<object>();

        // 将当前Scope状态压入栈
        _scopeStack.Value.Push(state);

        // 返回自定义清理器,退出Scope时弹出栈顶元素
        return new ScopeDisposer(() =>
        {
            if (_scopeStack.Value != null && _scopeStack.Value.Count > 0)
            {
                _scopeStack.Value.Pop();
                // 栈空后置null节省资源
                if (_scopeStack.Value.Count == 0)
                {
                    _scopeStack.Value = null;
                }
            }
        });
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        // 获取当前生效的Scope值
        var currentScope = _scopeStack.Value?.Count > 0 ? _scopeStack.Value.Peek() : null;
        string scopeContent = currentScope?.ToString() ?? "无Scope";

        // 组合Scope与日志内容输出
        string logContent = formatter(state, exception);
        Console.WriteLine($"[{scopeContent}] {logContent}");
    }

    // 自定义Scope清理器,实现IDisposable接口
    private sealed class ScopeDisposer : IDisposable
    {
        private readonly Action _cleanupAction;
        private bool _disposed;

        public ScopeDisposer(Action cleanupAction)
        {
            _cleanupAction = cleanupAction;
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _cleanupAction();
                _disposed = true;
            }
        }
    }
}

调用验证

var logger = new MyLogger();
using (logger.BeginScope("app1"))
{
    logger.LogInformation("Hello"); // 输出:[app1] Hello

    // 嵌套Scope测试
    using (logger.BeginScope("moduleA"))
    {
        logger.LogInformation("Processing data"); // 输出:[moduleA] Processing data
    }

    logger.LogInformation("Operation completed"); // 输出:[app1] Operation completed
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:13:30