Verwendung von log4net mit WebApi - Beibehaltung der gleichen Korrelations-ID in der gesamten Instanz

Ich habe eine LoggingHandler-Klasse, die ich als Message-Handler verwende, um Anfragen und Antworten (mit log4net) auf mein WebApi zu protokollieren

public class LoggingMessageHandler : DelegatingHandler
{
    public ILogManager LogManager { get; set; }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var controllerType = GlobalConfiguration.Configuration.Services
            .GetHttpControllerSelector()
            .SelectController(request)
            .ControllerType;

        var requestInfo = string.Format("{0} {1}", request.Method, request.RequestUri);
        var correlationId = request.GetCorrelationId().ToString();

        var requestMessage = await request.Content.ReadAsStringAsync();

        await LogIncommingMessageAsync(controllerType, correlationId, requestInfo, requestMessage.Replace("\r\n", ""));

        var response = await base.SendAsync(request, cancellationToken);

        string responseMessage;

        if (response.IsSuccessStatusCode)
            responseMessage = await response.Content.ReadAsStringAsync();
        else
            responseMessage = response.ReasonPhrase;

        await OutgoingMessageAsync(controllerType, correlationId, requestInfo, responseMessage);

        return response;
    }

    protected async Task LogIncommingMessageAsync(Type type, string correlationId, string requestInfo, string body)
    {
        var logger = LogManager.GetLogger(type);
        await Task.Run(() => logger.Debug("{0} {1} - API Request: \r\n{2}", correlationId, requestInfo, body));
    }

    protected async Task OutgoingMessageAsync(Type type, string correlationId, string requestInfo, string body)
    {
        var logger = LogManager.GetLogger(type);
        await Task.Run(() => logger.Debug("{0} {1} - API Response: \r\n{2}", correlationId, requestInfo, body));
    }
}

ILogManager wird über Autofac injiziert (siehe unser Autofac-Modul weiter unten)

Mein Problem ist mit einer Korrelations-ID;

Wie kann dies in unsere App weitergegeben werden?

In einer anderen Assembly, die einen Teil unserer Geschäftslogik enthält, möchten wir beispielsweise einen Aufruf von LogManager.GetLogger verwenden, um ein Protokoll für einen anderen Typ abzurufen. Da es jedoch von der Anforderung stammt, verwenden Sie dieselbe Korrelations-ID erneut können unsere Logmeldungen zusammenbinden?

Aus Gründen der Übersichtlichkeit sieht unser Autofac-Modul folgendermaßen aus:

public class LoggingAutofacModule : Autofac.Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<LogManager>().As<ILogManager>().InstancePerApiRequest();
    }

    protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
    {
        registration.Preparing += OnComponentPreparing;

        var implementationType = registration.Activator.LimitType;
        var injectors = BuildInjectors(implementationType).ToArray();

        if (!injectors.Any())
            return;

        registration.Activated += (s, e) =>
        {
            foreach (var injector in injectors)
                injector(e.Context, e.Instance);
        };
    }

    private static void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        var t = e.Component.Activator.LimitType;
        e.Parameters =
            e.Parameters.Union(new[] { new ResolvedParameter((p, i) => p.ParameterType == typeof(ILogger), (p, i) => e.Context.Resolve<ILogManager>().GetLogger(t)) });
    }

    private static IEnumerable<Action<IComponentContext, object>> BuildInjectors(Type componentType)
    {
        var properties =
            componentType.GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
                         .Where(p => p.PropertyType == typeof(ILogger) && !p.GetIndexParameters().Any())
                         .Where(p =>
                         {
                             var accessors = p.GetAccessors(false);
                             return accessors.Length != -1 || accessors[0].ReturnType == typeof(void);
                         });

        foreach (var propertyInfo in properties)
        {
            var propInfo = propertyInfo;
            yield return (context, instance) => propInfo.SetValue(instance, context.Resolve<ILogManager>().GetLogger(componentType), null);
        }
    }
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage