2014-10-02 2 views
7

Итак, у нас есть система регистрации, в которой мы используем Log.Info, и она записывается в ILogger.ThreadLocal и ждут

Теперь у нас есть несколько рабочих, работающих в фоновом режиме, и мы хотим, чтобы они записывали их в свои журналы. Так что все в комплекте для Рабочего. Все, что регистрируется при выполнении этой задачи, должно быть перенаправлено на собственный журнал.

Мы, где думаем о создании метода Log.SetLoggerForCurrentThread, реализуем его с помощью ThreadLocal. Исполнительный код будет выглядеть примерно так:

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

Вопросы

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

Справочная информация

Большинство из этих работников будет работать внутри экземпляра Azure WorkerRole, но теперь и чем они также вызвали (один раз) из приложения консоли.

+0

'CultureInfo' устанавливается в начале каждой управляемой нити - это зависит от' Task'. Я бы порекомендовал явное использование «Log.SetLoggerForCurrentThread», если вы хотите войти в «Задачу». Это показывает, что вы явно подумали об этом и получили компенсацию. –

+0

Прежде чем начать работу над ответом, вы должны знать, что log4net свободен и решает большинство ваших проблем. –

+0

Привет @GuillaumeCR, возможно, вы правы, но рассматриваете его как общий вопрос программирования для похожих шаблонов. –

ответ

6

Вы можете использовать CallContext для передачи (сериализации) данных по потокам. Смотрите эту статью для примера:

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

Для некоторых справочной информации, обратитесь к этой статье:

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

+5

Вы должны опубликовать содержательный пример кода вашего ответа. Не ретранслируйте внешние ссылки, которые могут умереть. –

+0

отличная статья! Благодаря! :) –

6

На мой взгляд, лучшим решением является либо передать экземпляры регистратора в качестве аргументов (или переменные-члены), или вводить их (например, используя вложенные области).

Однако, если вы хотите сохранить и передать экземпляр журнала неявно способом, совместимым с await, тогда вам нужно будет использовать контекст логического вызова. У меня есть blog post describing this approach, в котором указаны ограничения по этому подходу:

  1. Он работает только на полной платформе .NET 4.5.
  2. Вы должны использовать семантику «перезаписать». Обычно это означает сохранение только неизменяемых данных.

Имея это в виду, вот код, который должен работать для ваших нужд:

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