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

Согласно документации NLog:

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

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

Ответ 1

С помощью 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 должен будет построить трассировку стека, чтобы узнать, кто ее вызывается, или ваш код всегда должен проходить в вызывающем. С помощью стиля logger-per-class вы все равно это делаете, но вы можете делать это один раз за класс, а не один раз за звонок, и устранять серьезную проблему с производительностью.

Ответ 2

Преимущество использования "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} в макете.

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

Две причины немедленно spring:

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

Logger logger = LogManager.GetCurrentClassLogger()

Поиск текущего класса из трассировки стека требует некоторой (но не очень высокой) производительности.

Ответ 9

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

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

        }
    }
}

Ответ 10

Если вы используете NLOG, вы можете указать callsite в config, это будет записывать имя и метод класса, в которых был указан оператор ведения журнала.

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

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

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