Dlaczego loggery zalecają używanie loggera dla każdej klasy?

88

Zgodnie z dokumentacją NLog:

Większość aplikacji będzie używać jednego programu rejestrującego na klasę, gdzie nazwa programu rejestrującego jest taka sama, jak nazwa klasy.

To jest ten sam sposób, w jaki działa log4net. Dlaczego jest to dobra praktyka?

Daniel T.
źródło
1
hmm. wydaje się, że są tu dwa problemy - jeden z nich to faktyczny obiekt dziennika dla każdej klasy, a drugi to nazwa dziennika taka sama jak klasa.
Peter Recore

Odpowiedzi:

59

W log4net użycie jednego programu rejestrującego na klasę ułatwia przechwytywanie źródła komunikatu dziennika (tj. Klasy zapisującej w dzienniku). Jeśli nie masz jednego rejestratora na klasę, ale zamiast tego masz jeden rejestrator dla całej aplikacji, musisz skorzystać z większej liczby sztuczek refleksji, aby wiedzieć, skąd pochodzą komunikaty dziennika.

Porównaj następujące:

Zaloguj się na zajęcia

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

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

Jeden rejestrator na aplikację (lub podobny)

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

-- or --

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

Korzystając z drugiego przykładu, Logger musiałby zbudować ślad stosu, aby zobaczyć, kto go wywołuje, lub twój kod zawsze musiałby przekazać wywołujący. W przypadku stylu rejestratora według klasy nadal to robisz, ale możesz to zrobić raz na klasę zamiast raz na wywołanie i wyeliminować poważny problem z wydajnością.

Jeremy Wiebe
źródło
Dzięki, to pomaga wyjaśnić sprawy. Po prostu ręcznie wprowadzaliśmy nazwę klasy i metodę do wiadomości (tj. „Wywołano ImageCreator.CreateThumbnail ()”), ale lepiej, jeśli rejestrator sobie z tym poradzi.
Daniel T.
1
Po prostu do Twojej wiadomości, „lepszą” praktyką stało się posiadanie Loggera na instancję, a nie na klasę (tj. Statyczną), ponieważ ułatwia to przechwytywanie informacji, takich jak informacje o wątkach. Oczywiście to kwestia gustu, nie ma „twardej i szybkiej zasady”, ale chciałem to po prostu wyrzucić.
Will Hartung
7
@will, czy możesz to trochę więcej wyjaśnić? Podczas rejestrowania przy użyciu programu Logger per Class, zawsze rejestruję identyfikator wątku, aby rejestrator mógł uzyskać bieżące informacje o wątku. Wszelkie inne informacje o wątku będą również dostępne dla rejestrującego.
Jeremy Wiebe
@Jeremy Wiebe: Czy to jedyny powód? Funkcjonalnie nie ma problemów, jeśli używam jednej zmiennej globalnej typu logger dla całej aplikacji?
giorgim
1
@Giorgi Nie, nie sądzę. Wiele z tych informacji można obecnie uzyskać dzięki atrybutom CallerInformation, które sprawiają, że jeden rejestrator na klasę jest nieco mniej istotny - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe
15

Zaleta używania "logger per file" w NLog: masz możliwość zarządzania / filtrowania logów według przestrzeni nazw i nazwy klasy. Przykład:

<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 ma do tego bardzo przydatny fragment kodu. nloggerFragment stworzy następujący kod:

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

Więc tylko kilka naciśnięć klawiszy i masz rejestrator na klasę. Użyje przestrzeni nazw i nazwy klasy jako nazwy rejestrującego. Aby ustawić inną nazwę dla swojego rejestratora klasy, możesz użyć tego:

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

I, jak powiedział @JeremyWiebe, nie musisz używać sztuczek, aby uzyskać nazwę klasy, która próbuje zarejestrować wiadomość: Nazwa loggera (która zwykle jest nazwą klasy) może być łatwo zalogowana do pliku (lub inny cel) przy użyciu ${logger}w układzie.

CoperNick
źródło
5

Widzę kilka powodów takiego wyboru.

  • Zawsze będziesz wiedzieć, skąd pochodzi konkretna instrukcja dziennika, jeśli umieścisz nazwę programu rejestrującego w formacie wyjściowym dziennika.
  • Możesz kontrolować, jakie instrukcje dziennika są wyświetlane na dokładnym poziomie, włączając lub wyłączając niektóre programy rejestrujące lub ustawiając ich poziom.
Peter Recore
źródło
4

W przypadku NLog istnieje również korzyść wydajnościowa. Większość użytkowników będzie używać

Logger logger = LogManager.GetCurrentClassLogger()

Wyszukiwanie bieżącej klasy ze śledzenia stosu wymaga pewnej (ale nie dużej) wydajności.

juliański
źródło
3

W większości przypadków nazwa klasy stanowi dobrą nazwę dla programu rejestrującego. Podczas skanowania plików dziennika można zobaczyć komunikat dziennika i powiązać go bezpośrednio z wierszem kodu.

Dobrym przykładem, w którym nie jest to najlepsze podejście, są dzienniki SQL Hibernate. Istnieje współużytkowany program rejestrujący o nazwie „Hibernate.SQL” lub coś podobnego, w którym wiele różnych klas wypisuje surowy kod SQL do jednej kategorii rejestratora.

Eric Hauser
źródło
2

Z punktu widzenia programisty najłatwiej jest, jeśli nie musisz za każdym razem tworzyć obiektu rejestrującego. Z drugiej strony, jeśli tego nie zrobisz, ale raczej utworzysz ją dynamicznie za pomocą odbicia, spowolni to wydajność. Aby rozwiązać ten problem, możesz użyć następującego kodu, który tworzy rejestrator dynamicznie asynchronicznie:

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

        }
    }
}
as9876
źródło
1

Natychmiast przychodzą na myśl dwa powody:

  1. Posiadanie oddzielnego dziennika dla każdej klasy ułatwia grupowanie wszystkich komunikatów / błędów dotyczących danej klasy.
  2. Posiadanie dziennika w klasie pozwala na rejestrowanie wewnętrznych szczegółów, które mogą być niedostępne poza klasą (np. Stan prywatny, informacje dotyczące implementacji klasy itp.).
Dan Tao
źródło
2
Masz rejestrator w klasie, niezależnie od tego, czy jest zdefiniowany na poziomie klasy, czy globalnie. Globalne rejestratory nie są „poza” klasą z punktu widzenia widoczności. Jesteś jeszcze przedstawieniu globalny rejestrator od wewnątrz danej klasy, więc masz pełną widoczność.
Robert
0

Prawdopodobnie dlatego, że chcesz mieć możliwość rejestrowania metod, które są widoczne tylko dla klasy bez przerywania hermetyzacji, ułatwia to również korzystanie z klasy w innej aplikacji bez przerywania funkcji rejestrowania.

Rodrick Chapman
źródło
1
Utrudnia użycie tej klasy w innej aplikacji. Musisz odwołać się do biblioteki rejestrowania, czy chcesz, czy nie.
Piotr Perak
0

Ułatwia konfigurowanie programów dopisujących według przestrzeni nazw lub klas.

dotjoe
źródło
0

Jeśli używasz NLOG, możesz określić callsite w konfiguracji, to zapisze nazwę klasy i metodę, w której znajduje się instrukcja logowania.

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

Następnie możesz użyć stałej dla nazwy programu rejestrującego lub nazwy zestawu.

Zastrzeżenie: nie wiem, w jaki sposób NLOG zbiera te informacje, przypuszczam, że będzie to refleksja, więc być może będziesz musiał rozważyć wydajność. Istnieje kilka problemów z metodami Async, jeśli nie używasz NLOG w wersji 4.4 lub nowszej.

user6271979
źródło