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

如何为Azure Function日志自动添加Correlation ID(避免IHttpContextAccessor)

为Azure Functions自动在日志中添加Correlation ID

场景描述

所有Azure Function均为HttpTrigger类型,调用方会在请求中携带correlation-id请求头。不想在每个自定义服务类中注入IHttpContextAccessor手动获取并拼接该ID到日志,希望直接调用日志方法时自动带上Correlation ID:

原写法(需手动拼接):

logger.LogInformation($"{correlationId}: Saving some entity to DB");
// 输出:7eec51f0-4046-4398-b044-45ecf905cd81: Saving some entity to DB

期望写法(自动注入ID):

customLogger.LogInformation("Saving some entity to DB");
// 输出:7eec51f0-4046-4398-b044-45ecf905cd81: Saving some entity to DB

之前尝试自定义日志提供程序时遇到与默认.NET ILogger<>兼容的问题,以下是可行的解决方案:


解决方案:包装默认ILogger实现

无需从零实现完整的日志提供程序,只需包装默认的ILogger<T>,在日志输出前自动注入Correlation ID。

1. 实现日志包装器类

根据Azure Functions的运行模式(In-Process/隔离模式)选择对应实现:

针对In-Process模式

public class CorrelationIdLogger<T> : ILogger<T>
{
    private readonly ILogger<T> _innerLogger;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CorrelationIdLogger(ILogger<T> innerLogger, IHttpContextAccessor httpContextAccessor)
    {
        _innerLogger = innerLogger;
        _httpContextAccessor = httpContextAccessor;
    }

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

    public bool IsEnabled(LogLevel logLevel) => _innerLogger.IsEnabled(logLevel);

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        // 从请求头获取Correlation ID,无值时用默认标识
        var correlationId = _httpContextAccessor.HttpContext?.Request.Headers["correlation-id"].FirstOrDefault() ?? "no-correlation-id";
        // 拼接日志消息
        var formattedMessage = $"{correlationId}: {formatter(state, exception)}";
        // 委托给默认日志器输出
        _innerLogger.Log(logLevel, eventId, state, exception, (s, e) => formattedMessage);
    }
}

针对隔离(Isolated Worker)模式

隔离模式下无法直接使用IHttpContextAccessor,需改用IFunctionContextAccessor获取请求信息:

public class CorrelationIdLogger<T> : ILogger<T>
{
    private readonly ILogger<T> _innerLogger;
    private readonly IFunctionContextAccessor _functionContextAccessor;

    public CorrelationIdLogger(ILogger<T> innerLogger, IFunctionContextAccessor functionContextAccessor)
    {
        _innerLogger = innerLogger;
        _functionContextAccessor = functionContextAccessor;
    }

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

    public bool IsEnabled(LogLevel logLevel) => _innerLogger.IsEnabled(logLevel);

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        var correlationId = "no-correlation-id";
        var functionContext = _functionContextAccessor.FunctionContext;
        
        if (functionContext?.BindingContext.BindingData.TryGetValue("HttpRequest", out var requestObj) == true)
        {
            if (requestObj is HttpRequestData request)
            {
                correlationId = request.Headers["correlation-id"].FirstOrDefault() ?? correlationId;
            }
        }

        var formattedMessage = $"{correlationId}: {formatter(state, exception)}";
        _innerLogger.Log(logLevel, eventId, state, exception, (s, e) => formattedMessage);
    }
}

2. 注册服务

在服务配置中替换默认的ILogger<T>注册,让依赖注入返回包装后的实例:

In-Process模式(Startup.cs)

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // 注册HttpContextAccessor
        builder.Services.AddHttpContextAccessor();
        // 替换默认ILogger<T>为自定义包装器
        builder.Services.AddTransient(typeof(ILogger<>), typeof(CorrelationIdLogger<>));
    }
}

隔离模式(Program.cs)

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        // 注册FunctionContextAccessor
        services.AddSingleton<IFunctionContextAccessor, FunctionContextAccessor>();
        // 替换默认ILogger<T>为自定义包装器
        services.AddTransient(typeof(ILogger<>), typeof(CorrelationIdLogger<>));
    })
    .Build();

host.Run();

使用方式

在自定义服务中直接注入ILogger<T>即可,无需任何额外修改,日志会自动带上Correlation ID:

public class MyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public void SaveEntity()
    {
        // 自动添加Correlation ID到日志
        _logger.LogInformation("Saving some entity to DB");
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:43:20