Czy Logger.getLogger (MyClass.class) jest najlepszym sposobem na zainicjowanie rejestratorów log4j?

13

W tym samouczku Mkyong zaproponowano zainicjalizowanie rejestratorów w ten sposób:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Prawdopodobnie teraz każda inna klasa, której używasz, ma program rejestrujący, zainicjuje swoje programy rejestrujące w ten sam sposób.

Moje pytanie brzmi - czy to najlepszy sposób, aby to zrobić? Wydaje się ... powtarzalne.

dwjohnston
źródło
2
Co możesz powiedzieć na ten temat (odkładając na bok składnię Javy) ?. Musisz utworzyć zmienną do przechowywania programu rejestrującego i musisz podać getLogger()nazwę programu rejestrującego, aby uzyskać.
kdgregory
2
@kdgregory fakt, że robię to samo w każdej klasie.
dwjohnston
1
Spójrz na to pytanie na Stack Overflow
toniedzwiedz
3
Za stary na migrację, ale to pytanie jest tutaj nie na temat i bardziej odpowiednie dla StackOverflow.
Andres F.
1
@AndresF. Myślę, że umieściłem to tutaj, ponieważ jest to bardziej pytanie o styl kodu / wzorce projektowe niż problem techniczny.
dwjohnston

Odpowiedzi:

16

Twój komentarz mówi, że „pełny” odnosi się do potrzeby powtarzania tego wiersza kodu w każdej klasie. Moja pierwsza odpowiedź jest taka, że ​​na szerokim obrazie dodanie dwóch linii kodu (definicja zmiennej i instrukcja importu) do każdej klasy nie jest aż tak wielkim problemem. Zwłaszcza, że ​​musisz tylko dodać je do klas, które zachowują się i dlatego musisz wykonać rejestrowanie. To powiedziawszy, konkretny wiersz kodu, którego używasz, jest podatny na błędy kopiowania i wklejania (więcej na ten temat później).

Ale ponieważ chcesz alternatywy, oto kilka z powodów, dla których możesz chcieć lub nie chcesz ich używać.

Użyj jednego rejestratora dla całej aplikacji

Jeśli nie obchodzi Cię, którą klasę zgłaszasz, lub chcesz umieścić w wiadomości cały niezbędny kontekst, wystarczy prosty rejestrator singletonów:

LoggerSingleton.getInstance().debug("MyController is running")

Moim zdaniem jedną z dużych zalet frameworka rejestrowania jest kontekst zapewniany przez osobne instancje rejestratora - choćby po to, aby kierować komunikaty dziennika do różnych miejsc docelowych. Nie zrezygnowałbym z tego tylko po to, aby zapisać jeden wiersz kodu (nadal potrzebujesz importu).

Dodatkowo zwiększa to gadatliwość w miejscu użycia, co skończy się znacznie większą liczbą naciśnięć klawiszy.

Utwórz swoje rejestratory w miejscu użytkowania

Wyrzucam to tylko dlatego, że eliminuje zmienną. Nie sądzę, żebym musiał to komentować. Chociaż pokazuje moją preferowaną technikę uzyskiwania instancji rejestratora.

Logger.getLogger(getClass()).debug("blah blah blah");

Użyj postprocesora komponentu bean, aby wstrzyknąć program rejestrujący

W twoim przykładzie użyto Spring, a Spring pozwala ci podpiąć się do kodu inicjującego komponent bean. Możesz utworzyć postprocesor, który sprawdza komponent bean w poszukiwaniu loggerzmiennej członka i tworzy Loggerinstancję, gdy ją znajdzie.

Chociaż taki postprocesor to zaledwie kilkadziesiąt wierszy kodu, to kolejna ruchoma część Twojej aplikacji, a zatem kolejne potencjalne źródło błędów. Wolę mieć ich jak najmniej.

Użyj mixin

Scala i Groovy zapewniają cechy , które pozwalają ci zakreślić zachowanie. Typowym wzorem Scali jest stworzenie Loggingcechy, a następnie dodanie jej do klasy, która wymaga rejestrowania:

class MyController with Logging

Niestety oznacza to, że musisz zmieniać języki. O ile nie używasz Java 8, w takim przypadku możesz utworzyć Logginginterfejs za pomocą „metody domyślnej”:

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

Teraz, w ramach kodu klasy, możesz po prostu użyć

getLogger().debug("blah blah blah");

Jest to łatwe, ale ma kilka wad. Po pierwsze, zanieczyszcza interfejs każdej klasy, która go używa, ponieważ wszystkie metody interfejsu są publiczne. Być może nie jest tak źle, jeśli używasz go tylko dla klas, które są tworzone i wprowadzane przez Springa, szczególnie jeśli przestrzegasz rozdziału interfejs / implementacja.

Większy problem polega na tym, że musi sprawdzać rzeczywistą instancję rejestratora przy każdym połączeniu. Co jest szybkie, ale niepotrzebne.

Nadal potrzebujesz wyciągu z importu.

Przenieś program rejestrujący do nadklasy

Powtórzę: nie znajduję powtarzających się definicji rejestratora, ale jeśli tak, uważam, że to najlepsze podejście do ich eliminacji.

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Teraz Twoje klasy kontrolerów dziedziczą AbstractControlleri mają dostęp do loggerzmiennej. Pamiętaj, że musisz umieścić @Controlleradnotację na konkretnej klasie.

Niektórzy ludzie uznają to za wypaczenie dziedzictwa. Próbowałem je zmiękczyć, nazywając klasę, AbstractControllera nie AbstractProjectClass. Możesz sam zdecydować, czy istnieje związek typu „ czy to związek”.

Inne osoby sprzeciwiają się użyciu zmiennej instancji zamiast zmiennej statycznej. Rejestratory statyczne IMO są podatne na błędy kopiuj-wklej, ponieważ musisz jawnie odwoływać się do nazwy klasy; getClass()zapewnia, że ​​Twój rejestrator jest zawsze poprawny.

kdgregory
źródło
Myślę, że powinieneś uważać na getClass () w klasie dziedziczenia, MethodHandles.lookup (). LookupClass () jest lepszy po jdk 7
yuxh 15.07.19
@luxh - czy masz na to wytłumaczenie?
kdgregory
@yuxh - jedyna wzmianka o MethodHandles w tym pytaniu (której nie było w podanej przez ciebie odpowiedzi), jest podobnym nieobsługiwanym stwierdzeniem z komentarzem z prośbą o wyjaśnienie. Czy masz wiarygodne poparcie dla twierdzenia, które MethodHandles.lookup().lookupClass()jest lepsze niż Object.getClass()?
kdgregory
MethodHandles.lookup().lookupClass()może być używany do zmiennych statycznych, nie jest podatny na błędy kopiowania i wklejania i jest szybki: stackoverflow.com/a/47112323/898747 Oznacza to dodatkowy inport, ale bardzo lubię moje rejestratory statyczne, więc warto przynajmniej wspomnieć :)
FableBlaze
0

Aby rozszerzyć odpowiedź dostarczoną przez @kdgregory, Groovy dostarcza @Slf4j( groovy.util.logging.Slf4j) po wyjęciu z pudełka jako adnotację, która wykonuje transformację AST w klasie, aby przypiąć ją do loggera, który domyślnie przyjmuje nazwę zmiennej, logjeśli nie zostanie określona.

Daniel Paul Anzaldo
źródło