2014-10-02 31 views
7

Log.Info'u kullandığımız bir kayıt sistemimiz var ve bir ILogger'a yazıyor.ThreadLocal ve bekleyin

Artık arka planda çalışan birden çok çalışanımız var ve bunların kendi günlüklerine yazılmasını istiyoruz. Yani her işçiye İşçi başı verilir. Bu görev yürütüldüğünde kaydedilen her şey kendi logger'ına iletilmelidir.

Burada bir yöntem oluşturmayı düşünüyoruz Log.SetLoggerForCurrentThread, ThreadLocal ile uygulayarak. yürütme kodu şöyle görünecektir:

public class Worker 
{ 
    ILogger _Logger; 

    public void ExecuteTask() 
    { 
     Log.Info("This goes to the 'Global' logger"); 

     using (Log.SetLoggerForCurrentThread(_Logger)) 
     { 
      Log.Info("This goes to local logger"); 
      DoWork(); 
     } 
    } 

    private async void DoWork() 
    { 
     Log.Info("Starting..."); 

     // SomeMethod does some logging, 
     // that also needs to be forwared to the local logger 
     var value = await SomeDeepDomainClass.SomeMethod(); 

     // if we use ThreadLocal, and this thread has been reused, 
     // it could be a completely different logger that is now attached. 
     Log.Info("Ended..."); 
    } 
} 

Sorular

  • biz bekliyor kullanmak, iplik olabilir teori süreci başka işçinin çalışma, böylece yerel kaydediciler kadar karıştırma içinde.
  • Benzer bir şey yapmak için en iyi desen nedir? Hangi depolama yöntemini kullanabilirim?
  • CultureInfo bunu nasıl yapıyor?

Arka plan bilgileri

bu İşçi çoğu artık ve onlar da bir konsol uygulamasından (bir kez) tetiklenir daha bir Azure WorkerRole örneği içinde çalışacak, ancak.

+0

'CultureInfo' her yönetilen iplik başında kurulduğundan - o Task'' bağımsız bu. Bir 'Task'den oturum açmak istiyorsanız 'Log.SetLoggerForCurrentThread' açık kullanımını öneriyorum. Bunu açıkça düşündüğünüzü ve telafi ettiğinizi gösterir. –

+0

Bir cevap üzerinde çalışmaya başlamadan önce, log4net'in ücretsiz olduğunu ve sorunlarınızın çoğunu zaten çözdüğünü bilmelisiniz. –

+0

Merhaba @GuillaumeCR, haklı olabilirsiniz, ancak benzer modeller için genel bir programlama sorusu olarak görün. –

cevap

6

İleti dizilerindeki verileri iletmek için CallContext'i kullanabilirsiniz. Örnek için bu makaleye bakın: Bazı arka plan bilgileri için

http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html

, gördüğüm bu makaleye: Bence

http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx

+5

Cevabınızın anlamlı bir kod örneği göndermelisiniz. Ölebilecek dış bağlantılar üzerinde röle yapmayın. –

+0

harika bir yazı! Teşekkürler! :) –

6

, en iyi çözüm argümanları olarak logger örneklerini geçmek ya olduğu (veya üye değişkenleri) veya enjekte edin (ör. iç içe kapsamlar kullanarak). Ancak, await ile uyumlu bir şekilde kayıt örneğini saklamak ve saklamak istiyorsanız, mantıksal çağrı bağlamını kullanmanız gerekir. Bu yaklaşımın sınırlamalarını işaret eden bir blog post describing this approach var:

  1. Yalnızca .NET 4.5 çerçevesi üzerinde çalışır.
  2. "Üstyazı" semantiklerini kullanmalısınız. Bu genellikle sadece değişmez verilerin saklanması anlamına gelir. Bu düşünceyle

, burada ihtiyaçlarınız için çalışmalıdır bazı kod:

public static class LocalLogger 
{ 
    private static readonly string name = Guid.NewGuid().ToString("N"); 

    // Static Log methods should read this. 
    public static ILogger CurrentLogger 
    { 
    public get 
    { 
     var ret = CallContext.LogicalGetData(name) as ILogger; 
     return ret == null ? Logger.GlobalLogger : ret; 
    } 

    private set 
    { 
     CallContext.LogicalSetData(name, value); 
    } 
    } 

    // Client code uses this. 
    public static IDisposable UseLogger(ILogger logger) 
    { 
    var oldLogger = CurrentLogger; 
    CurrentLogger = logger; 
    if (oldLogger == GlobalLogger) 
     return NoopDisposable.Instance; 
    return new SetWhenDisposed(oldLogger); 
    } 

    private sealed class NoopDisposable : IDisposable 
    { 
    public void Dispose() { } 
    public static readonly Instance = new NoopDisposable(); 
    } 

    private sealed class SetWhenDisposed : IDisposable 
    { 
    private readonly ILogger _oldLogger; 
    private bool _disposed; 

    public SetWhenDisposed(ILogger oldLogger) 
    { 
     _oldLogger = oldLogger; 
    } 

    public void Dispose() 
    { 
     if (_disposed) 
     return; 
     CurrentLogger = _oldLogger; 
     _disposed = true; 
    } 
    } 
}