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
相关产品推荐
相关产品推荐

