Czy rejestrator powinien być prywatny statyczny, czy nie


103

Czy rejestrator powinien być uznany za statyczny, czy nie? Zwykle widziałem dwa rodzaje deklaracji dla rejestratora:

    chroniony dziennik dziennika = nowy Log4JLogger (aClass.class);

lub

    prywatny statyczny Log log = new Log4JLogger (aClass.class);

Którego należy użyć? jakie są plusy i minusy obu?


1
Logowanie to problem przekrojowy. Użyj aspektów, a pytanie jest dyskusyjne.
Dave Jarvis

4
staticto jedna referencja na klasę. niestatyczne to jedno odwołanie na instancję (+ inicjalizacja). Więc w niektórych przypadkach ta ostatnia ma znaczący wpływ na pamięć, jeśli masz mnóstwo instancji. Nigdy nie używaj niestatyki w częstym obiekcie. Zawsze używam wersji statycznej. (które powinny być duże litery LOG )
zrezygnował - anony-Mus

2
jak już sugerowano, użyj AOP i adnotacji, na przykład: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256

1
RobertHume wersja statyczna jest przy stałej. Właśnie dlatego powinien być pisany wielkimi literami.
WYJŚCIE - Anony-Mousse

2
Nie, to powinno być private static final Log logmałe. Rejestrator nie jest stałą, rejestrator jest statycznym obiektem końcowym (który można zmutować). Osobiście zawsze używam logger.
osundblad

Odpowiedzi:


99

Zaletą niestatycznej postaci jest to, że możesz zadeklarować ją w (abstrakcyjnej) klasie bazowej, jak poniżej, bez obawy, że zostanie użyta właściwa nazwa klasy:

protected Log log = new Log4JLogger(getClass());

Jednak jego wadą jest oczywiście to, że dla każdej instancji klasy zostanie utworzona zupełnie nowa instancja programu rejestrującego. Może to nie być kosztowne, ale powoduje znaczne obciążenie. Jeśli chcesz tego uniknąć, wolisz skorzystać z staticformularza. Ale jego wadą jest z kolei to, że musisz zadeklarować to w każdej indywidualnej klasie i uważać w każdej klasie, aby podczas budowy rejestratora została użyta właściwa nazwa klasy, ponieważ getClass()nie można jej użyć w kontekście statycznym. Jednak w przeciętnym środowisku IDE można w tym celu utworzyć szablon autouzupełniania. Np. logger+ctrl+space . .

Z drugiej strony, jeśli otrzymujesz rejestrator z fabryki, która z kolei może buforować już utworzone instancje rejestratorów, to użycie niestatycznej postaci nie spowoduje zbyt dużego obciążenia. Na przykład Log4j ma LogManagerdo tego celu.

protected Log log = LogManager.getLogger(getClass());

6
Zadeklaruj abstract Log getLogger();w klasie abstrakcyjnej. Zaimplementuj tę metodę, zwracając statyczny rejestrator dla określonego wystąpienia. Dodaj private final static Log LOG = LogManager.getLogger(Clazz.class);do szablonu klasy IDE.
WYJŚCIE - Anony-Mousse

2
Dla slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt

3
@BalusC Problem z przekazaniem getClass () do metody getLogger polega na tym, że zwraca ona klasę aktualnej instancji. Zwykle bardziej pożądane jest, aby logowanie było powiązane z klasą, w której znajduje się kod. Na przykład, jeśli kod logowania znajduje się w klasie Parent, to chcemy, aby rejestrowanie było skojarzone z klasą Parent, nawet jeśli instancja wykonawcza jest i instancja klasy Child, która jest podklasą Parent. Z getClass () zostanie skojarzony z dzieckiem, niepoprawnie
inor

@inor: "niepoprawnie"? Jeśli nie chcesz abstrahować klasy, nie powinieneś po prostu używać dziedziczonej metody getClass () w pierwszej kolejności. Są deweloperzy, którzy uważają to za poprawne i przydatne, ponieważ ujawnia informacje, w której podklasie została wykonana logika.
BalusC

2
@ BalusC getLogger (getClass ()) powoduje, że nazwa podklasy jest zawsze niepoprawnie rejestrowana. Klasy rejestrujące powinny zawsze wykonywać metodę getLogger (Clazz.class), aby skojarzyć rejestrowanie wykonane przez kod w klasie Clazz. Deweloperzy, którzy chcą wiedzieć, która z podklas jest wykonywana (np. SubClazz rozszerza Clazz), powinni zrobić w SubClazz: getLogger (SubClazz.class) i coś w rodzaju: log.info ("call <coś w mojej klasie bazowej>");
inor

44

Kiedyś myślałem, że wszystkie rejestratory powinny być statyczne; jednak ten artykuł na wiki.apache.org kilka ważnych problemów z pamięcią, dotyczących wycieków classloadera. Zadeklarowanie programu rejestrującego jako statycznego zapobiega gromadzeniu deklarowanej klasy (i skojarzonych programów ładujących klasy) w kontenerach J2EE korzystających ze współdzielonego modułu ładującego. Spowoduje to błędy PermGen, jeśli ponownie wdrożysz aplikację wystarczająco dużo razy.

Naprawdę nie widzę żadnego sposobu na obejście tego problemu z wyciekiem klas, poza deklarowaniem rejestratorów jako niestatycznych.


4
Podejrzewałem, że pole statyczne może również powodować wyciek pamięci. Niestatyka może mieć problem z wydajnością, jak powiedzieli inni. Jaki jest zatem idealny sposób?
liang

@piepera głównym problemem opisanym w artykule, do którego się odwołujesz, jest możliwość kontrolowania poziomu rejestrowania w każdej aplikacji, gdy „weź pod uwagę przypadek, gdy klasa korzystająca z„ private static Log log = ”jest wdrażana za pośrednictwem ClassLoadera, który należy do wielu przypuszczalnie niezależne „aplikacje”. Nie uważam tego za problem, ponieważ w tej konkretnej sytuacji aplikacje mają „wspólną płaszczyznę” i na tej „wspólnej płaszczyźnie” decyduje się o poziomie rejestrowania dla klasy i tak, obowiązuje dla wszystkich aplikacji ... ale zachowaj pamiętaj, że ta klasa jest [ładowana] poza tymi aplikacjami
inor

17

Najważniejsza różnica dotyczy tego, jak wpływa na pliki dziennika: do której kategorii trafiają dzienniki?

  • W pierwszym przypadku dzienniki podklasy trafiają do kategorii nadklasy. Wydaje mi się to bardzo sprzeczne z intuicją.
  • Istnieje wariant twojego pierwszego przypadku:

    chroniony dziennik dziennika = nowy Log4JLogger (getClass ());

    W takim przypadku kategoria dziennika mówi, na którym obiekcie pracował zarejestrowany kod.

  • W drugiej opcji (private static) kategorią dziennika jest klasa zawierająca kod rejestrowania. Zwykle jest to klasa, która wykonuje rejestrowaną czynność.

Gorąco polecam tę ostatnią opcję. Ma te zalety w porównaniu z innymi rozwiązaniami:

  • Istnieje bezpośredni związek między dziennikiem a kodem. Łatwo jest znaleźć miejsce, z którego pochodzi komunikat dziennika.
  • Jeśli ktoś musi dostroić poziomy rejestrowania (co jest wykonywane dla kategorii), dzieje się tak zazwyczaj dlatego, że jest zainteresowany (lub nie) jakimiś konkretnymi wiadomościami, napisanymi przez określoną klasę. Jeśli kategorią nie jest klasa, która pisze wiadomości, trudniej jest dostroić poziomy.
  • Możesz zalogować się do metod statycznych
  • Loggery muszą być zainicjowane (lub wyszukane) tylko raz na klasę, więc przy starcie, a nie dla każdej utworzonej instancji.

Ma też wady:

  • Musi być zadeklarowany w każdej klasie, w której rejestrujesz komunikaty (bez ponownego wykorzystywania rejestratorów nadklasy).
  • Musisz zadbać o podanie prawidłowej nazwy klasy podczas inicjalizacji rejestratora. (Ale dobre IDE zajmą się tym za Ciebie).

4

Użyj odwrócenia kontroli i przekaż rejestrator do konstruktora. Jeśli utworzysz program rejestrujący w klasie, będziesz mieć niesamowity czas z testami jednostkowymi. Piszesz testy jednostkowe, prawda?


5
Testy jednostkowe, które sprawdzają rejestrowanie dźwięku, są zarówno bezużyteczne, jak i niezwykle kruche.
Michael

1
Przydatność w zależności od testowanego systemu. Czasami rejestrowanie jest wszystkim, do czego masz dostęp.
Wayne Allen

@Wayne Allen, gdy wykonujesz testy jednostkowe, z definicji masz również wyniki testów. Czy sugerujesz sytuację, w której wykonuje się testy jednostkowe, ale nie ma wyników testów? masz tylko logi?
inor

tworzenie loggera wewnątrz klasy nie stwarza problemów. Czy możesz pokazać prosty przykład, który jest trudny do UT, ponieważ klasa tworzy własny rejestrator?
inor

1
Pewnie. A może logger, który wysyła e-maile. Nie chcę tego robić za każdym razem, gdy przeprowadzasz testy. Poza tym, jak zapewnisz efekt uboczny?
Wayne Allen,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.