2009-11-11 20 views
13

Mam rozwiązanie składające się z głównej aplikacji WinForm, z powiązanymi wewnętrznie napisanymi bibliotekami DLL klasy, z których chcę się logować. Rejestrowanie powinno być wykonywane przez ten sam program rejestrujący, niezależnie od tego, czy jest to główny klient UI lub powiązane z nim dll. Biblioteki DLL mogą oczywiście być używane przez inne aplikacje, które mają różne rejestratory w innych rozwiązaniach, ale w tych okolicznościach będą miały inną konfigurację log4net i być może inny zestaw dopełniaczy.Jaki jest najlepszy wzorzec do używania tego samego rejestratora Log4net w wielu złożeniach w rozwiązaniu?

Jednym z podejść byłoby utworzenie singletonu w głównej aplikacji i zalogowanie się z niego, jednak ponieważ log4net jest jego własnym singletonem, do którego można się odwoływać, o ile przekazujemy ten sam ciąg (lub typ) do log4net.LogManager.GetLogger będziemy logować się do tego samego miejsca docelowego (w moim przypadku chcę użyć RollingFileAppender).

To działa. Jednak biorąc pod uwagę, że biblioteka DLL będzie miała kilka klas, oznaczałoby to, że każda instancja klasy lub klasa statyczna, z której chcemy się logować, wymagały i) argumentu definiującego nazwę rejestratora (w celu zalogowania się do tego samego miejsca docelowego) i ii) w każdym punkcie wejścia trzeba zadzwonić pod numer log4net.LogManager.GetLogger(loggerName).

Jaki jest najlepszy wzór do zastosowania? Czy właściwym podejściem byłoby stworzenie pojedynczej instancji w każdym zespole? Moją obawą jest to, że nadal będziemy musieli przekazać nazwę rejestratora do każdego punktu wejścia dla biblioteki dll, co wydaje się przesadą. Aby uniknąć podania nazwy rejestratora, mogę założyć, że zawsze jest równa System.Reflection.Assembly.GetCallingAssembly().GetName().Name.

Jeśli jest to zbyt trudne dla log4net, czy istnieją inne łatwiejsze rozwiązania, takie jak blok logowań Enterprise? A może najlepszym rozwiązaniem jest podejście zorientowane na aspekt (AOP)?

Reference for anti-pattern approach for singleton here.

Odpowiedz

16

Prawie rok później, ale pomyślałem, że i tak przyczynię się do tego!

Na podstawie Twojego komentarza do wpisu Carla, myślę, że może źle zrozumiałeś działanie log4net. Tak, loggername identyfikuje rejestrator. Tak, logger może mieć wielu aplikantów. ALE, wiele rejestratorów może również pisać do aplikacji SAME. Tak więc, możesz skonfigurować rejestratory "A", "B" i "C" dla wszystkich logów do pliku "X". Można uzyskać rejestratory tak:

ILog logger_a = LogManager.GetLogger("A"); 
ILog logger_b = LogManager.GetLogger("B"); 
ILog logger_c = LogManager.GetLogger("C"); 

Teraz, jeśli log z któregokolwiek z tych rejestratorów, wszystkie one mogą iść w tym samym miejscu (plik „X”) Jeśli skonfigurowano je w ten sposób.

Zaletą NIE używania tej samej nazwy rejestratora w aplikacji jest możliwość kontrolowania poziomu logowania w różnych miejscach aplikacji za pośrednictwem pliku konfiguracyjnego. Tak więc, jeśli kod, który używa rejestratorów "A" i "B" działa dobrze, ale kod, który używa logger "C" ma problem, możesz wyłączyć "A" i "B", i włączyć " C "do samego końca. W ten sposób będziesz miał mniej informacji do przebicia, aby znaleźć swój problem.

Większość ludzi (lub przynajmniej większość przykładów log4net) faktycznie tworzy instancję statycznego rejestratora dla każdego CLASS, nazwanego dla tej klasy (nie pamiętam dokładnej składni, ale łatwo jest znaleźć przykłady). Zapewnia to bardzo wysoki poziom szczegółowości w kontrolowaniu rejestrowania.

W pliku app.config można kontrolować wszystkie rejestratory na tym samym poziomie, konfigurując pojedynczy program rejestrujący o nazwie "*" lub można skonfigurować określone rejestratory (przy użyciu w pełni kwalifikowanej nazwy) lub można nawet skonfigurować według części w pełni kwalifikowanego typu. Na przykład bardzo łatwo można utworzyć wszystkie klasy w dzienniku ABC obszaru nazw na poziomie "informacji", wszystkie klasy w dzienniku DEF przestrzeni nazw na poziomie "błędu", a wszystkie klasy w przestrzeni nazw GHI w ogóle się nie logują. I wszystkie te rejestratory mogą logować się do tego samego miejsca docelowego (np. Plik X).

mogło być za późno, aby pomóc, ale być może nie ...

+0

W moim pierwotnym pytaniu zdecydowałem, że wiele z tego, co chciałem zarchiwizować, to w rzeczywistości informacja o postępie, więc opublikowaliśmy wydarzenia, które pochłonęły główną aplikację, ale twoje wyjaśnienie jest zdecydowanie najbardziej oczywiste i użyłem tego również gdzie indziej. – Topdown

+0

Czytam to 6 lat później, więc nie, nigdy nie jest za późno! :-) – nashwan

1

Domyślam się, że możesz mieć nazwę LoggerName w pliku app.config.

Czy mogę zapytać, dlaczego nazwa logger musi być taka sama dla całej aplikacji?

+0

Rozumiem, że loggername jest identyfikatorem rejestratora. Gdzie logger może mieć wielu aplikantów.Więc jeśli chcę korzystać z tych samych aplikacji, a w przypadku dziennika RollingFileAppender do tego samego pliku z biblioteki DLL, to potrzebuję mieć tę samą nazwę loggera dla każdego wywołania funkcji GetLogger. Czy też źle mnie zrozumiałem? – Topdown

+0

@ Topdown: Źle zrozumiałeś. Powinieneś zapoznać się z http://logging.apache.org/log4net/release/faq.html –

0

Zastosowaliśmy podejście do używania log4net podobnego do tego.

Używamy log4net zarówno w głównych zestawach bibliotek aplikacji, jak i klas. Napisaliśmy wokół niego klasę opakowującą, dzięki czemu wszystkie rejestracje pojawiają się z taką samą konfiguracją z dowolnego miejsca, w którym ją nazywamy, a także w celu zapewnienia, że ​​jest to singleton. Ta klasa opakowania jest wywoływana z dowolnego miejsca w naszej aplikacji.

W rezultacie biblioteki klas i główna aplikacja wylogowują się do tego samego pliku dziennika o tej samej konfiguracji, co jest tym, czego chcieliśmy. Czy to właśnie chciałeś osiągnąć?

3

Najpierw należy utworzyć klasę opakowania (aby oddzielić aplikację od dostawcy rejestrowania).

Ta klasa opakowania może przyjąć identyfikator rejestratora. Jeśli korzystasz z kontenera IoC, możesz po prostu wprowadzić nazwę rejestratora lub istniejącą, wstępnie skonfigurowaną instancję.

Jeśli używasz Jedności (inne pojemniki są podobne), można zrobić coś takiego

// During application initialization 
IUnityContainer myContainer = new UnityContainer(); 
LoggingService concreteLoggingService = new LoggingService("logID"); 
myContainer.RegisterInstance<ILoggingService>(concreteLoggingService); 

// This would be injected, so you wouldn't see this, but it's here for consistency 
ILoggingService loggingService = myContainer.Resolve<ILoggingService>(); 
loggingService.LogMessage("message"); 

Teraz ta zakłada masz kontenera IoC.Alternatywnie można utworzyć lokalizator usług:

// During application initialization 
ServiceLocator.Register<ILoggingService>(new LoggingService("logID")); 

// Retrieved as needed 
ILoggingService loggingServce = LoggingServiceLocator.Locate<ILoggingService>(); 
loggingService.LogMessage("message"); 

W drugim przypadku należy wpisać cały kod instalacyjny. W przypadku kontenera IoC otrzymasz go natychmiast po wyjęciu z opakowania.

+0

Dlaczego -1? Absolutnie nie polecam tego podejścia. Aby "odłączyć" szkielet logowania od dostawcy, należy wstrzyknąć kolejną warstwę pośrednią bez uzyskiwania niczego wartościowego. Wzorzec użycia log4net jest prosty do kodowania i można go łatwo wymienić. Zawijanie jej w warstwę pośrednią nie zapewnia żadnej wartości i jest teoretycznym rozwiązaniem problemu. –

+1

@Casper, uważam, że abstrakcja jest przydatna, aby umożliwić kpiny podczas testów jednostkowych. –

+0

Rozumiem słowa, których używasz, ale to, co opisujesz, jest, moim zdaniem, niezrozumieniem natury twojego loggera. Po co niepotrzebnie abstrakcjonować coś, co daje dodatkowy kod produkcyjny, więcej punktów porażki produkcji i nic wartościowego - po to tylko, aby zaspokoić pewne zniechęcony wymóg "abstrakcji wszystkiego w teście jednostkowym". Log4Net samo w sobie jest już wyodrębnione w taki sposób, że nie będzie - ani nigdy nie będzie - wpływać na twoje testy jednostkowe. –

0

Co powiesz na wdrożenie własnego TraceListener, abyś mógł używać aplikacji Trace.Write w całej aplikacji?

+0

Jak to jest związane z pytaniem? –

+0

@CasperLeonNielsen Implementujesz niestandardowy TraceListener (który jest częścią .NET), który zapisuje do dziennika przy użyciu Log4net. Dodaj go do kolekcji Listeners (część .NET) przy uruchamianiu, a następnie w dowolnej bibliotece DLL lub klasie lub cokolwiek chcesz, użyj Trace.Write (część .NET) i kończy się logem Log4net. –

+0

Pozwolę sobie powtórzyć: To nie jest związane z pytaniem. To, co proponujesz, jest sposobem na obejście normalnego scenariusza użycia dla log4net, nie zyskując nic poza zwiększoną złożonością. –

0

nigdy nie widziałem innego, wzór sensowne, użytkowaniu niż ten dla log4net:

Na górze każdej klasy, która potrzebuje rejestrowanie, do:

private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 

w warstwach gospodarza AssemblyInfo.cs, należy:

[assembly: log4net.Config.XmlConfigurator] 

Podczas uruchamiania komputera uruchom programową konfigurację aplikacji itd. Lub użyj prostej konfiguracji app.config.

Koniec historii. Brak opakowań. Brak wyprowadzenia z powyższego.

Każdy inny wzorzec użycia, na który natknąłem się, ma na celu obejście nie do końca zrozumiałego doprecyzowania log4net, w tym niektórych/wszystkich odpowiedzi tutaj.

+0

Jeśli mam jedną bibliotekę (rdzeń) programu rejestrującego, ale jest ona wymieniona na różnych warstwach, ta statyczna inicjalizacja rejestratora zawsze prowadzi rejestrację tylko jako biblioteka (jądro), ale jako biblioteka referencyjna. masz jakieś sugestie na ten temat? – HydPhani

+0

Wygląda na to, że nie chcesz iść z podejściem, które polecam tutaj, ale zamiast tego chcę zawinąć log4net. Nie polecam tego, ale jeśli tak bardzo chcesz to sprawdzić, sprawdź odpowiedzi tutaj: http://stackoverflow.com/questions/166438/what-would-a-log4net-wrapper-class-look-like –

Powiązane problemy