2010-06-29 2 views
77

Согласно документации NLog по:Почему регистраторы рекомендуют использовать логгер для каждого класса?

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

Это то же самое, что и log4net. Почему это хорошая практика?

+1

hmm. Кажется, здесь есть два вопроса: у одного есть фактический объект журнала для каждого класса, а у одного - имя журнала, то же самое, что и класс. –

ответ

49

С помощью log4net, используя один регистратор для каждого класса, упрощается сбор источника сообщения журнала (т. Е. Запись класса в журнал). Если у вас нет одного регистратора для каждого класса, но вместо него есть один регистратор для всего приложения, вам нужно прибегнуть к дополнительным трюкам для определения того, откуда берутся сообщения журнала.

Сравните следующие:

Вход в класс

using System.Reflection; 
private static readonly ILog _logger = 
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);  

public void SomeMethod() 
{ 
    _logger.DebugFormat("File not found: {0}", _filename); 
} 

Один регистратор в приложение (или аналогичный)

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller 

-- or -- 

Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller 

Используя второй пример, регистратору необходимо будет построить стек проследите, чтобы кто-то вызывал его, или ваш код всегда должен был пройти в вызывающем. С помощью стиля logger-per-class вы все равно это делаете, но вы можете делать это один раз за класс, а не один раз за звонок и устранять серьезную проблему с производительностью.

+0

Спасибо, что помогает прояснить ситуацию. Мы просто вручную помещали имя и метод класса в сообщение (т. Е. «ImageCreator.CreateThumbnail()»), но лучше, если регистратор может его обработать. –

+1

Просто FYI, это стало «лучшей» практикой иметь регистратор на экземпляр, а не на класс (т. Е. Статический), поскольку это упрощает сбор информации, такой как информация о потоке. Очевидно, что это вопрос вкуса, никакого «жесткого и быстрого правила», но я хотел просто выбросить это. –

+6

@ может, вы можете объяснить это немного больше? При регистрации с использованием Logger для каждого класса я всегда регистрирую идентификатор потока, чтобы регистратор мог получать текущую информацию о потоке. Любая другая информация о потоке будет доступна и для регистратора. –

0

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

+0

Это затрудняет использование этого класса в другом приложении. Вам нужна ссылка на регистрационную библиотеку, нравится вам это или нет. –

1

Две причины сразу приходят на ум:

  1. Имея отдельный журнал для каждого класса позволяет легко сгруппировать все сообщения журнала/ошибки, относящиеся к данному классу.
  2. Наличие журнала в классе позволяет регистрировать внутренние данные, которые могут быть недоступны вне класса (например, личное состояние, информация о реализации класса и т. Д.).
+1

У вас есть регистратор в классе, независимо от того, установлен ли он на уровне класса или глобально. Глобальные регистраторы не являются «вне» класса с точки зрения видимости. Вы все еще ссылаетесь на глобальный регистратор из * внутри * рассматриваемого класса, чтобы у вас была полная видимость. – Robert

5

Я вижу несколько причин для этого выбора.

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

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

Хорошим примером того, что это не лучший подход, является SQL-журналы Hibernate. Существует общий логгер с именем «Hibernate.SQL» или что-то в этом роде, где несколько разных классов записывают исходный SQL-код в одну категорию регистратора.

0

Легко настраивает приложения по пространству имен или классу.

11

Преимущество использования «logger per file» в NLog: у вас есть возможность управлять/фильтровать журналы по пространству имен и имени класса. Пример:

<logger name="A.NameSpace.MyClass"  minlevel="Debug" writeTo="ImportantLogs" /> 
<logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" /> 
<logger name="StupidLibrary.*"   minlevel="Error" writeTo="StupidLibraryLogs" /> 

<!-- Hide other messages from StupidLibrary --> 
<logger name="StupidLibrary.*" final="true" /> 

<!-- Log all but hidden messages --> 
<logger name="*" writeTo="AllLogs" /> 

NLogger имеет очень полезный фрагмент кода для этого. nlogger фрагмент кода создаст следующий код:

private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); 

Так всего несколько нажатий клавиш, и у вас есть регистратор на класс. Он будет использовать пространство имен и имя класса в качестве имени регистратора. Чтобы задать другое имя вашего класса регистратор, вы можете использовать это:

private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName"); 

И, как сказал @JeremyWiebe, вы не должны использовать уловки, чтобы получить имя класса, который пытается войти сообщение : Имя регистратора (обычно это имя класса) можно легко записать в файл (или другую цель), используя ${logger} в макете.

0

В случае с NLog также есть преимущество в производительности. Большинство пользователей используют

Logger logger = LogManager.GetCurrentClassLogger() 

Глядя на текущий класс от трассировки стека занять некоторое (но не много) производительности.

0

С точки зрения разработки, это проще всего, если вам не нужно создавать объект журнала каждый раз. С другой стороны, если вы этого не сделаете, но вы создаете его динамически с помощью отражения, это замедлит работу. Чтобы решить эту проблему, вы можете использовать следующий код, который создает регистратор динамически асинхронно:

using NLog; 
using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace WinForms 
{ 
    class log 
    { 

     public static async void Log(int severity, string message) 
     { 
      await Task.Run(() => LogIt(severity, message)); 
     } 

     private static void LogIt(int severity, string message) 
     { 
      StackTrace st = new StackTrace(); 
      StackFrame x = st.GetFrame(2);  //the third one goes back to the original caller 
      Type t = x.GetMethod().DeclaringType; 
      Logger theLogger = LogManager.GetLogger(t.FullName); 

      //https://github.com/NLog/NLog/wiki/Log-levels 
      string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" }; 
      int level = Math.Min(levels.Length, severity); 
      theLogger.Log(LogLevel.FromOrdinal(level), message); 

     } 
    } 
} 
0

Если вы используете NLog вы можете указать callsite в конфигурации, это будет записывать имя класса и метод, где протоколирование заявление был расположен.

<property name="CallSite" value="${callsite}" /> 

Затем вы можете использовать константу для имени вашего регистратора или имени сборки.

Отказ от ответственности: Я не знаю, как NLOG собирает эту информацию, моя догадка будет отражением, поэтому вам может потребоваться рассмотреть производительность. Есть несколько проблем с методами Async, если вы не используете NLOG v4.4 или новее.

Смежные вопросы