Использование log4net с WebApi — сохранение одного и того же идентификатора корреляции во всем экземпляре

У меня есть класс LoggingHandler, который я использую в качестве обработчика сообщений для регистрации (используя log4net) запросов и ответов на мой WebApi.

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 вводится через Autofac (см. наш модуль Autofac ниже)

Моя проблема связана с идентификатором корреляции;

Как это можно передать дальше в наше приложение?

Например, в другой сборке, содержащей часть нашей бизнес-логики, мы хотим использовать вызов LogManager.GetLogger, чтобы получить журнал для другого типа, но, поскольку он возник по запросу, повторно используем тот же идентификатор корреляции, поэтому мы можно связать наши сообщения журнала вместе?

Для наглядности наш модуль Autofac выглядит так:

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);
        }
    }
}

person Alex    schedule 24.01.2014    source источник
comment
хороший код -> LoggingMessageHandler   -  person ms007    schedule 25.03.2014
comment
он был основан на идее, найденной здесь github.com/seattlekiran/ Мои фрагменты/блоб/мастер/C%23/   -  person Alex    schedule 25.03.2014


Ответы (1)


Самый простой способ — просто добавить его в свойства контекста логического потока log4net:

LogicalThreadContext.Properties["CorrelationId"] = request.GetCorrelationId();

Обратите внимание, что поскольку это контекст логического потока, он будет сохраняться в await точках, даже если для возобновления запроса используется другой поток.

person Stephen Cleary    schedule 24.01.2014
comment
Где мы должны добавить эту строку в веб-API? Нужно ли добавлять эту строку в Global.asax или когда я получаю любой вызов веб-API в методе действия - person Amit Kotha; 10.04.2018
comment
@AmitKotha: у вас нет идентификатора запроса или корреляции в Global.asax. Его можно добавить только в метод действия. - person Stephen Cleary; 10.04.2018