fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
8 minut

log4net vs nLog


03.03.2011

Kolejny raz o logowaniu… "bo to naprawdę ważne™" :).

W świecie .NET mamy dwie liczące się biblioteki oferujące logowanie informacji z aplikacji: log4net oraz nLog. Oczywiście znajdą się też inne rozwiązania: od koszmarnych (The Logging Application Block z EntLiba) po głupie (pisanie własnego loggera i jego produkcyjne wykorzystanie).

Z tych dwóch zdecydowanie bardziej popularny jest log4net. I ja także od niego zacząłem. Gdy po raz pierwszy zobaczyłem możliwości tej biblioteki to dosłownie szczena mi opadła wbijając spację w biurko. Wcześniej mieliśmy w projekcie Logging Application Block, a po podmianie konsola naszego CallCenter zaczęła czarodziejsko mienić się wszelkimi kolorami tęczy. Każda informacja była wreszcie prezentowana w taki sposób, w jaki chciałem ją widzieć. A do tego doszły zmiany konfiguracji loggera w locie, bez restartowania aplikacji – wystarczy edytować i zapisać plik XML! Cudo-niewido. Teraz się z tym już obyłem, ale na początku naprawdę gały miałem rozpostarte na oścież i napatrzeć się na logi nie mogłem. Właściwie dopiero wtedy logowanie czegokolwiek nabrało sensu.

W którymś kolejnym projekcie postanowiłem (o czym zresztą pisałem) zobaczyć co oferuje konkurent. I… do log4neta już nie wróciłem. W każdym kolejnym nowym projekcie umieszczam bez zastanowienia nLog.

Temat ten wyskoczył podczas kilku rozmów z fellow-devs i zawsze dostawałem pytanie: dlaczego nLog jest lepszy? Pytanie jak najbardziej na miejscu i tutaj postaram się na nie odpowiedzieć.

Zacznę jednak od stwierdzenia trochę sprzecznego: nLog bynajmniej nie jest lepszy. Po prostu dla mnie osobiście korzysta się z niego wygodniej. Możliwości obu są ogromne i bardzo do siebie zbliżone, więc diabeł tkwi w szczegółach. Jakby nie miał gdzie tkwić. Co zatem powoduje, że wybieram nLog?

Deklaracja loggera

Nie uznaję czegoś takiego jak wstrzykiwanie loggera przez DI czy pisanie własnej abstrakcji nad zewnętrzną biblioteką. Jest to sztuka dla sztuki i nie ma żadnego sensu (jeśli ktoś uważa inaczej to chętnie poznam argumenty). W każdej klasie, która chce coś zalogować, deklaruję statyczne pole loggera i tyle. Loggery mogą mieć swoje nazwy, która to cecha niesie za sobą wiele korzyści. Jeśli nazwiemy logger tak jak typ, który go zawiera, będziemy mogli z poziomu konfiguracji sterować logami na BARDZO szczegółowym poziomie. Np: niech wszystkie logi z klasy A zapisują się do tego pliku, logi powyżej poziomu DEBUG z całej aplikacji zapiszmy do pliku innego, natomiast wszystkie błędy z przestrzeni nazw BBB.CCC dodatkowo wysyłajmy na maila. To jest MOC!

I tutaj natrafiamy na pierwsze udogodnienie w nLogu, ponieważ wystarczy coś takiego:

  1:  private static readonly Logger _log = LogManager.GetCurrentClassLogger();

Z kolei dla log4net instrukcja ta będzie wyglądać tak

  1:  private static readonly ILog _log = LogManager.GetLogger(typeof ([current_class]));

Niby nic wielkiego, bo można samemu dopisać metodę przejeżdżającą się po stosie wywołań tak jak robi to nLog, albo stworzyć odpowiedni snippet w VS czy R# i też nie jest źle. Ale mimo wszystko bardzo mi się to spodobało.

Konfiguracja

W tym punkcie być może wyjdę na kretyna, ale… jakoś nigdy nie ogarnąłem struktury konfiguracji log4neta i używanych tam pojęć. logger, appender, mapping. layout… wiem że nie ma w tym niby nic skomplikowanego, ale z palca nic tam nie napiszę. A nawet z kopiuj/wklej muszę się niekiedy zastanowić o co tam w ogóle chodzi.

Jest to bardzo subiektywne odczucie, ale struktura konfiguracji w nLogu wydaje mi się bardziej naturalna. Po prostu taka jak powinna być. Mamy targets, czyli miejsca w które ma trafić informacja. Oraz rules, czyli zbiór reguł kierujących odpowiednie logi w odpowiednie strony. Tyle.

Nie jest to wielki problem, ale lepiej czuję się w XMLu nLoga niż log4neta (BTW, logger to chyba jedyna rzecz której nie konfiguruję w kodzie).

API logowania

Obie biblioteki umożliwiają logowanie tekstu z formatowaniem, czyli możemy wysłać do loggera:

  1:  ...("{0} texttexttext {1}", user.Id, action.Id);

Tutaj szczegół z diabłem polega na tym, że log4net rozróżnia pomiędzy logowaniem zwykłego stringa a stringa z formatowaniem. Powyższa linijka w log4net wyglądać więc będzie tak:

  1:  _log.DebugFormat("{0} texttexttext {1}", user.Id, action.Id);

Z kolei w nLog niezależnie od tego czy chcemy zapisać czysty string, czy też go sformatować, mamy instrukcję:

  1:  _log.Debug("{0} texttexttext {1}", user.Id, action.Id);

Niby pierdoła, ale po co niepotrzebnie komplikować życie i mnożyć metody? Chcę zalogować to co chcę zalogować, a biblioteka niech zdecyduje jak to zrobić.

Deferred logging

Niekiedy zbudowanie wiadomości do zalogowania może być dość skomplikowane i składać się z pobieraniem informacji z wielu obiektów, łączeniem stringów itd. Należy tego oczywiście unikać, jeśli zbudowana wiadomość nie zostanie zalogowana z powodu konfiguracji loggera. Po co pchać coś do Debug na produkcji, gdzie prawdopodobnie logowanie zaczyna się od mniej szczegółowego poziomu?

W tym celu obie biblioteki umożliwiają sprawdzenie w kodzie czy w warto budować wiadomość do zalogowania czy też nie przez właściwości _log.IsDebugEnabled, _log.IsInfoEnabled etc. Wystarczy instrukcja if i wiemy wszystko…

Ale w nLog można pójść o krok dalej:

  1:  _log.Debug(() => "log msg");

Przekazana funkcja wykona się tylko gdy faktycznie ma zostać zalogowana. (oczywiście dodanie tego do log4net to jedna banalna extension method, ale znowu: po co skoro gdzieś indziej mamy to za darmo?).

Inicjalizacja

Początek logowania w log4net może nastąpić po… inicjalizacji konfiguracji. Czyż to nie oczywiste? Piszemy gdzieś przy starcie aplikacji taką instrukcję:

  1:  log4net.Config.XmlConfigurator.ConfigureAndWatch(logConfigurationFile);

i let the logging begin!

Czy w ogóle da się prościej?

Ano okazało się że da się, i chyba to mnie najbardziej zafascynowało w nLog. Bo tam… tworzymy plik konfiguracyjny NLog.config, umieszczamy w katalogu aplikacji i… tyle! W kodzie nie mamy żadnej inicjalizacji, po prostu działa SAMO. Wypas.

Log levels

log4net oferuje następujące poziomy logowania: DEBUG, INFO, WARN, ERROR, FATAL.

nLog z kolei: TRACE, DEBUG, INFO, WARN, ERROR, FATAL.

Różnica? W nLog mamy TRACE. Bardzo przydatny dodatek, nadający się na przykład do logowania wywoływania wszystkich metod i ich parametrów (za pomocą AOP). Oczywiście na produkcji czegoś takiego nie załączymy, ale podczas debuggowania informacja ta może być nieoceniona. Cały poziom DEBUG mamy wówczas dla siebie – walimy tam informacje ręcznie (więcej o tym jak sam staram się wykorzystywać poziomy logowania – wkrótce).


Jeszcze raz podkreślam: absolutnie nie uważam, że z log4net jest coś nie tak albo że nie warto go używać. Jak zresztą widać przekonały mnie głównie niewielkie pierdoły, bez których spokojnie można żyć. Obie biblioteki są świetne i jeżeli nie korzystasz z żadnej nich to bardzo radzę zacząć przygodę z którąkolwiek. Jeśli jedna bez namysłu dodajesz zawsze do referencji log4net zakładając, że właściwie nie ma dla niej alternatywy, to zachęcam do spojrzenia na nLoga. Może Cię miło zaskoczyć.

Inna sprawa, że bez większego problemu można by uzupełnić log4net kilkoma powyższymi funkcjami za pomocą extension methods i też byłoby całkiem git.


Na koniec ciekawostka: nLog został napisany (i jest nadal utrzymywany) przez naszego rodaka, Jarka Kowalskiego, aktualnie pracującego w MS nad Entity Framework.

0 0 votes
Article Rating
24 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
jdubrownik
13 years ago

nLog wymiata, ale nie nazywałbym LAB z EntLib koszmarnym. Możliwości ma dość przyzwoite i dodatkowo oferuje wizualną konfugirację z poziomu Visual Studio co jest nie do przecenienia w porównianiu z log4net. Dla mnie nLog, później LAB i na końcu log4net.

jdubrownik
13 years ago

argument z XMLem of coz tyczy się też nLoga, ale za bardzo mi się ten framework podoba żebym mógł być obiektywny ;)

Tomasz Wójcik
13 years ago

Wszystko zależy od problemu, np. LAB z EntLib był mi bardzo przydatny, gdy klient chciał mieć możliwość samodzielnego skonfigurowania sobie co, gdzie i jak będzie logowane – dostaje wraz z EntLib aplikację, w której wszystko może sobie wyklikać, bez bawienia się w jakieś pliki konfiguracyjne i inne XMLe. Więc też bym go nie przekreślał. Ale faktycznie nLog z nich wszystkich jest chyba najprzyjemniejszy w użyciu.

Artur
Artur
13 years ago

Najpierw poznałem NLog, a kilka lat temu chciałem przyjrzeć się log4net i dałem sobie spokój. Dla mnie konfiguracja w NLog jest bardziej intuicyjna.

yaceq
yaceq
13 years ago

Jakis czas temu prywatnie stanalem przed wyborem biblioteki logowania. Jak zaczalem przegladac konfiguracje log4net’a (z ktorego korzystamy z pracy, ale ze tym sie akurat inni zajmowali to jakos mnie to specjalnie nie obchodzilo), a potem nLoga to od razu wiedzialem co wybrac. Aczkolwiek idealny nie jest – dodawanie do kazdej klasy LogManager.GetCurrentClassLogger() mnie jakos nie pociagalo, postanowilem sobie zrobic LogHelpera ze statycznym loggerem ktory mi patrzy wlasnie po stosie skad pochodzi wywolanie:
public static Logger Logger
{
get
{
StackFrame frame = new StackFrame(2, false);
return LogManager.GetLogger(frame.GetMethod().DeclaringType.FullName);
}
}
a potem to juz wiadomo. W log4necie mozna dokladnie to samo zrobic :)
Takze, tak jak ladnie podsumowales, mozna ladnie dodac extension methods/dopisac sobie wlasnego helpera, ktory Ci ladnie wszystko opakuje, ale zagmatwana konfiguracja i brak TRACE’a to juz byc zaczynaja powazne wady w log4necie.

Jacek
Jacek
13 years ago

Właśnie dlatego śledzę Twój blog. Krótko, zwięźle i na temat. W kilka minut można się dowiedzieć ciekawych rzeczy, bez konieczności męczenia się z dokumentacją.

Z przyzwyczajenia używam log4net, widzę, że czas wypróbować nLog-a.

bezieur
bezieur
13 years ago

Czekajcie….
jest przecież takie coś: http://netcommon.sourceforge.net/. I już więcej nie musimy wybierać. Używam i polecam.

dario-g
13 years ago

Dodam, że swego czasu, jak Jarek mieszkał jeszcze w Polsce (na jednym z pierwszych spotkań WG.NET), zrobił test wydajności: NLog, log4net i LAB. Kolejność była taka jak napisałem, przy czym LAB był baaaaaardzo wolny, a NLog troszkę szybszy od log4net’a. :)

twk
twk
13 years ago

#bezieur: Common.Logging ma sens kiedy tworzysz bibliotekę, której ktoś inny będzie używał i nie wiadomo jaki ma mechanizm logowania, a dzięki Common.Logging może sobie używać tego który lubi.

Dla własnych projektów chyba nie ma większego sensu, chyba że ktoś się przyzwyczai.

#procent: od pewnego czasu miałem wątpliwości, czemu tyle zachodnich projektów używa log4net a NLoga jakby nie widzieli… może to amerykański patriotyzm, nie wiem, mi też NLog przypadł do gustu jak pierwsza miłość – po prostu ten i żaden inny ;-) Dlatego cieszę się, że nie jestem sam :)

procent
13 years ago

Jestem zaskoczony takimi komentarzami. Myślałem że nikt nLoga nie używa, a tu proszę…:)

@bezieur:
Zgadzam się z @twk, common logging w zastosowaniach innych niż "biblioteki szerokiego użycia" moim zdaniem nie ma sensu.

Gutek
13 years ago

@bezieur

popieram @procenta i @twk… zreszta przejechalem sie juz 2 krotnie na tym Common.Logging wlasnie z powodu "bibliotek szerokiego uzycia" przez co momentami bylem zmuszony do uzywania log4net mimo iz tego jakos nie trawie

G
G
13 years ago

Jest jeszcze ELMAH http://code.google.com/p/elmah/

procent
13 years ago

@G:
ELMAH ma dużo węższe zastosowanie – logowanie błędów w aplikacjach ASP.NET. Opisane przeze mnie frameworki logują wszystkie informacje w każdym typie aplikacji. ELMAH może korzystać z tych wymienionych w poście.

RobertS
RobertS
13 years ago

"common logging w zastosowaniach innych niż "biblioteki szerokiego użycia" moim zdaniem nie ma sensu"

Jak najbardziej ma sens. Powiedzmy, że w swoich kodach używasz NLog, ale w pewnym momencie trzeba skorzystać z jakiejś biblioteki zewnętrznej, która używa log4net (albo LAB). Common.Logging umożliwi ci zrobienie "pomostu": log4net -> CommonLogging -> NLog. W ten sposób logowanie zarówno z Twojej aplikacji, jak i zewnętrznej biblioteki wpadnie w jeden "kanał". Poza tym ma prosty szablon do tworzenia i podstawiania własnych, niestandardowych loggerów (nie ćwiczyłem, oglądałem tylko przykład).

"przejechalem sie juz 2 krotnie na tym Common.Logging wlasnie z powodu "bibliotek szerokiego uzycia""
Na czym konkretnie się przejechałeś? Przydałoby się to innym jako ostrzeżenie, aczkolwiek nie za bardzo mogę sobie wyobrazić o co chodzi, bo używam już od roku i nigdy nie było problemu. Jeśli już miałem problemy z logowaniem, to były następujące: błędy w pliku konfiguracyjnym log4net oraz problemy z uprawnieniami do zapisu loga w ASP.NET.

marek
marek
13 years ago

Również używam nLog’a – jak dla mnie bomba. Ostatnio zastanowiła mnie pewna kwestia: Jak zalogować zdarzenie w transakcji, gdy Target nLoga skonfigurowany jest na database, a aplikacja łączy się bazą przy użyciu EntityFramework. np.: do bazy leci insert do tabel User oraz UserInRole, następnie loguję zdarzenie nlogiem w ramach transakcji. Niestety otrzymuję błąd. Pomaga oczywiście wypchnięcie logowania poza blok TransactionScope. Ale to nie do końca mi się podoba. Spotkaliście się z takim scenariuszem?

marek
13 years ago

Hej,

piszesz:
"Nie uznaję czegoś takiego jak wstrzykiwanie loggera przez DI czy pisanie własnej abstrakcji nad zewnętrzną biblioteką. Jest to sztuka dla sztuki i nie ma żadnego sensu (jeśli ktoś uważa inaczej to chętnie poznam argumenty)."

Hmm, a wyobraź sobie dość dojrzałą aplikację korzystającą z log4net i nagle pojawia się post zachwalający nLog’a, postanawiasz zmienić komponent logowania – nie łatwiej byłoby zmienić konfigurację DI niż zmieniać każdą klasę korzystającą z loggera’a? :)

Nie wiem jak Wy, ale jeśli korzysta się z NHibernate, to warto mieć log4net jednak… Można wprawdzie kupić NHProf, ale to kosztuje.

Poza tym ciekawy post, ja również uważam, że konfiguracja log4net to lekka porażka… Trzeba się przyjżeć nLog w takim razie :)

Dzięki i pozdrawiam.
Marek

dario-g
13 years ago

"Nie wiem jak Wy, ale jeśli korzysta się z NHibernate, to warto mieć log4net jednak"

Zdaje się, że to w wersji 3 nie jest już koniecznością: http://nhforge.org/wikis/howtonh/using-nlog-via-common-logging-with-nhibernate.aspx :)

dario-g
13 years ago

Dodam tylko, że nie chodzi o Common.Logging, a wyrzucenie referencji do log4net’a i dodanie interfejsu ‘IInternalLogger’. :)

twk
twk
13 years ago

@marek:
rozważ osobną bazę na logi, bo niepotrzebnie śmiecisz sobie i obciążasz bazę danych aplikacji.

mgrzeg
13 years ago

O NLogu swego czasu pokazał się tekst w zinie + rozmowa Mai i moja z Jarkiem… ciekawe, czy ktoś jeszcze pamięta Maję ;)
BTW – Jarek przerzucił się z EF na WP7, a że ma córeczkę w wieku mojej niedobroty, to robi takie zabaweczki: http://colorsprouts.com/ ;)

m.g.

Artur
Artur
13 years ago

@mgrzeg
Pamiętam :)
A chyba na pierwszym spotkaniu wg.net Jarek pokazywał SOODA (a jak źle pamiętam to NLog) :)

Artur

PS. btw, developers.pl też pamiętam, ale jak przez mgłę;)

dario-g
13 years ago

Napisałem o tym właśnie kilka komentarzy wyżej :)

andrzejp
13 years ago

Ja z kolei mam inne pytanie – czy znasz może jakieś dostępne przeglądarki logów do log4net lub nLog?
W aplikacjach webowych używam Elmah i tam jest dostępny handler, który pozwala na przeglądanie błędów. Czy spotkałeś się z czymś podobnym do log4net lub nLog?

procent
13 years ago

@andrzejp:
Uzywam albo notepad2 albo KIWI albo baretail/baregrep. Są tez jakieś komercyjne, ale nie testowalem.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również