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

Azure Function全局日志自动添加InvocationId的实现咨询

解决方案

要实现自定义日志自动附带InvocationId且无需使用者手动传入,完全可行,推荐以下两种适配所有生命周期服务的方案:

方案一:通过Activity.Current提取InvocationId(推荐,适配所有生命周期)

Azure Functions在每次执行时会自动创建一个跟踪Activity,并将InvocationId作为Tag存入其中。由于Activity.Current基于AsyncLocal实现,能跨依赖注入生命周期传递,不管是Singleton、Scoped还是Transient服务,只要在函数执行的调用链中记录日志,都能正确获取到当前的InvocationId。

代码示例

public class CustomLogger : ILogger
{
    private readonly string _categoryName;

    public CustomLogger(string categoryName)
    {
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;

    public bool IsEnabled(LogLevel logLevel) => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        // 从Activity中提取InvocationId,默认值为"Unknown"
        string invocationId = Activity.Current?.GetTagItem("InvocationId")?.ToString() ?? "Unknown";
        
        // 拼接带InvocationId的日志消息
        string logMessage = $"[{invocationId}] {formatter(state, exception)}";

        // 这里替换为你的实际日志输出逻辑(如写入文件、第三方日志服务等)
        Console.WriteLine(logMessage);
    }
}

方案二:通过ExecutionContext获取InvocationId(需注意Singleton服务场景)

ExecutionContext是Azure Functions中代表当前函数执行上下文的对象,包含InvocationId,但它的生命周期是Scoped。由于你的CustomLogger是Transient,若要获取ExecutionContext,需要注入IServiceProvider,并在日志方法中动态解析。

注意:该方式不适用于Singleton服务,因为Singleton服务的依赖注入根容器无法解析Scoped生命周期的ExecutionContext,会抛出异常。仅当你的Singleton服务不会在函数执行上下文外调用日志时,才可使用。

代码示例

using Microsoft.Azure.WebJobs;

public class CustomLogger : ILogger
{
    private readonly IServiceProvider _serviceProvider;
    private readonly string _categoryName;

    public CustomLogger(IServiceProvider serviceProvider, string categoryName)
    {
        _serviceProvider = serviceProvider;
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;

    public bool IsEnabled(LogLevel logLevel) => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        string invocationId = "Unknown";
        try
        {
            // 动态解析当前Scope的ExecutionContext
            var executionContext = _serviceProvider.GetRequiredService<ExecutionContext>();
            invocationId = executionContext.InvocationId.ToString();
        }
        catch (InvalidOperationException)
        {
            // 若在非函数执行上下文(如Singleton服务初始化时)调用,捕获异常并使用默认值
        }
        
        string logMessage = $"[{invocationId}] {formatter(state, exception)}";
        Console.WriteLine(logMessage);
    }
}

注册自定义日志提供器

无论使用哪种方案,都需要注册自定义的ILoggerProvider来创建CustomLogger实例:

public class CustomLoggerProvider : ILoggerProvider
{
    private readonly IServiceProvider _serviceProvider;

    public CustomLoggerProvider(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public ILogger CreateLogger(string categoryName)
    {
        // 方案一用这个:
        return new CustomLogger(categoryName);
        // 方案二用这个:
        // return new CustomLogger(_serviceProvider, categoryName);
    }

    public void Dispose() { }
}

然后在函数应用的启动配置中注册:

隔离进程模型(Program.cs)

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddSingleton<ILoggerProvider, CustomLoggerProvider>();
        // 注册你的其他服务(Singleton/Scoped/Transient)
    })
    .Build();

host.Run();

进程内模型(Startup.cs)

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<ILoggerProvider, CustomLoggerProvider>();
        // 注册你的其他服务
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:46:25