2016-08-19 102 views
6

我使用NLOG 4.3.5和.NET Framework 4.6.1NLOG MappedDiagnosticsLogicalContext不能在異步工作/等待與ConfigureAwait(假)

當我開始一個服務器端的操作我打電話:

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

這通過映射並出現在我的日誌文件中。一切都很好....或者是嗎? 在審查我的日誌文件時,我注意到這個操作ID值似乎沒有按照我預期的那樣工作。

實施例:

  1. 在螺紋19的操作開始,並設置背景。

  2. 它在所有等待調用

  3. 它執行

    var tasks = items.Select(item => Task.Run(() => { /* do stuff */} 
    await Task.WhenAll(tasks).ConfigureAwait(false) 
    
  4. 一個用於這些任務的線程是線程31使用.ConfigureAwait(假)(記住這一點後面再說)
  5. 同時,在線程36中,調用不同的服務器方法並開始新的操作。幾個日誌消息寫入它的唯一操作ID
  6. 此操作使用ConfigureAwait執行2個不同的等待調用(假)
  7. 接下來的日誌語句發生在線程31上。從此,它記錄創建的操作ID線程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