Согласно документации NLog:
Большинство приложений будут использовать по одному регистратору для каждого класса, где имя регистратора совпадает с именем класса.
Таким же образом работает log4net. Почему это хорошая практика?
Согласно документации NLog:
Большинство приложений будут использовать по одному регистратору для каждого класса, где имя регистратора совпадает с именем класса.
Таким же образом работает log4net. Почему это хорошая практика?
Ответы:
С 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 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}
макета.
Я вижу несколько причин для этого выбора.
В большинстве случаев имя класса дает хорошее имя для регистратора. При сканировании файлов журнала вы можете увидеть сообщение журнала и напрямую связать его со строкой кода.
Хорошим примером, когда это не лучший подход, являются журналы SQL Hibernate. Существует общий регистратор с именем "Hibernate.SQL" или что-то в этом роде, где несколько разных классов записывают необработанный SQL в одну категорию регистратора.
С точки зрения разработки проще всего, если вам не нужно каждый раз создавать объект регистратора. С другой стороны, если вы этого не сделаете, а создадите его динамически, используя отражение, это снизит производительность. Чтобы решить эту проблему, вы можете использовать следующий код, который динамически асинхронно создает регистратор:
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);
}
}
}
На ум сразу приходят две причины:
Вероятно, потому что вы хотите иметь возможность регистрировать методы, которые видны только классу, без нарушения инкапсуляции, это также упрощает использование класса в другом приложении без нарушения функциональности ведения журнала.
Если вы используете NLOG, вы можете указать callite в конфигурации, это будет записывать имя класса и метод, где находился оператор регистрации.
<property name="CallSite" value="${callsite}" />
Затем вы можете использовать константу для имени вашего регистратора или имени сборки.
Отказ от ответственности: я не знаю, как NLOG собирает эту информацию, я предполагаю, что это отражение, поэтому вам, возможно, придется учитывать производительность. Есть несколько проблем с методами Async, если вы не используете NLOG v4.4 или новее.