2016-08-19 5 views
6

Я использую NLog 4.3.5 и рамки .Net 4.6.1NLog MappedDiagnosticsLogicalContext не работает в асинхронном/ждут с ConfigureAwait (ложной)

Когда я начинаю работу на стороне сервера, который я называю:

NLog.MappedDiagnosticsLogicalContext.Set("OperationId", Guid.NewGuid()); 

Это отображается и отображается в моих файлах журнала. Все хорошо .... или не так ли? При просмотре моих файлов журналов я заметил, что это значение идентификатора операции не работает, как я ожидал.

Пример:

  1. В потоке 19 операция начинается и устанавливает контекст.

  2. Он использует .ConfigureAwait (ложь) на все ждут звонков

  3. Он выполняет

    var tasks = items.Select(item => Task.Run(() => { /* do stuff */} 
    await Task.WhenAll(tasks).ConfigureAwait(false) 
    
  4. Один из потоков, используемых для выполнения этих задач является поточно 31 (имейте это в виду, для последующего использования)
  5. Между тем, в потоке 36 вызывается другой серверный метод и начинается новая операция. Несколько сообщений журнала написаны с уникальным идентификатором операции
  6. Эта операция выполняет 2 разных вызова ожидания с помощью ConfigureAwait (false)
  7. Следующий оператор журнала встречается в потоке 31. С этого момента он регистрирует идентификатор операции, который был создан для операция, которая началась в потоке 19!

Я не ожидал, что это произойдет, и я не знаю, как это произошло. Но, просматривая историю журнала, я вижу, что такого рода вещи случались раньше.

Я думал, что контекст логического вызова должен был переноситься. Могу ли я использовать ConfigureAwait (false), который вызывает такое поведение? Это единственное, что я могу придумать ....

ответ

1

Вы можете обойти это следующим образом:

public static class LogicalThreadContext 
{ 
    private const string KeyPrefix = "NLog.LogicalThreadContext"; 

    private static string GetCallContextKey(string key) 
    { 
     return string.Format("{0}.{1}", KeyPrefix, key); 
    } 

    private static string GetCallContextValue(string key) 
    { 
     return CallContext.LogicalGetData(GetCallContextKey(key)) as string ?? string.Empty; 
    } 

    private static void SetCallContextValue(string key, string value) 
    { 
     CallContext.LogicalSetData(GetCallContextKey(key), value);   
    } 

    public static string Get(string item) 
    { 
     return GetCallContextValue(item); 
    } 

    public static string Get(string item, IFormatProvider formatProvider) 
    { 
     if ((formatProvider == null) && (LogManager.Configuration != null)) 
     { 
      formatProvider = LogManager.Configuration.DefaultCultureInfo; 
     } 

     return string.Format(formatProvider, "{0}", GetCallContextValue(item)); 
    } 

    public static void Set(string item, string value) 
    { 
     SetCallContextValue(item, value); 
    } 
} 

[LayoutRenderer("mdlc2")] 
public class LogicalThreadContextLayoutRenderer : LayoutRenderer 
{ 
    [DefaultParameter] 
    public bool Name {get;set;} 

    protected override void Append(StringBuilder builder, LogEventInfo logEvent) 
    { 
     builder.Append(LogicalThreadContext.Get(Name, null)); 
    } 
} 

//or application_start for ASP.NET 4 
static void Main(string[] args) 
{ 
    //layout renderer 
    ConfigurationItemFactory.Default.LayoutRenderers 
      .RegisterDefinition("mdlc2", typeof(LogicalThreadContextLayoutRenderer)); 
} 

Использование в конфигурационном файле:

${mdlc2:OperationId} 
+0

Да, это решение было в ссылке, которую я написал выше. – Keith

+0

Отчасти это связано со ссылкой. Я обновил и расширил ваши требования. – Julian