Dlaczego komunikaty logowania Level.FINE nie są wyświetlane?

110

W Javadocs dlajava.util.logging.Level państwa:


Poziomy w porządku malejącym to:

  • SEVERE (najwyższa wartość)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (najniższa wartość)

Źródło

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(Level.FINER);
        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}

Wynik

Logging level is: FINER
Jun 11, 2011 9:39:23 PM LoggingLevelsBlunder main
INFO: 0 0
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 1 1
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 2 4
Press any key to continue . . .

Opis problemu

Mój przykład ustawia wartość Levelna FINER, więc spodziewałem się zobaczyć 2 komunikaty dla każdej pętli. Zamiast tego widzę pojedynczą wiadomość dla każdej pętli ( Level.FINEbrakuje wiadomości).

Pytanie

Co należy zmienić, aby zobaczyć FINE( FINERlub FINEST) wynik?

Aktualizacja (rozwiązanie)

Dzięki odpowiedzi Vineet Reynolds ta wersja działa zgodnie z moimi oczekiwaniami. Wyświetla 3 x INFOwiadomości i 3 x FINEwiadomości.

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        // LOG this level to the log
        logger.setLevel(Level.FINER);

        ConsoleHandler handler = new ConsoleHandler();
        // PUBLISH this level
        handler.setLevel(Level.FINER);
        logger.addHandler(handler);

        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}
Andrew Thompson
źródło
10
Wydaje mi się, że komunikaty będą drukowane na konsoli dwukrotnie dla INFO i wyższych: najpierw przez anonimowego loggera, potem przez jego rodzica, globalnego loggera, który również ma domyślnie ustawiony ConsoleHandler na INFO. Aby wyłączyć globalny rejestrator, musisz dodać następującą linię kodu: logger.setUseParentHandlers (false);
minuty
Chcę tylko potwierdzić min. Komentarz dotyczący gry podwójnej. otrzymasz dwa wyjścia, chyba że użyjesz .setUseParentHandlers (false);
xpagesbeast

Odpowiedzi:

124

Loggery rejestrują tylko komunikat, tj. Tworzą zapisy dziennika (lub żądania logowania). Nie publikują wiadomości do miejsc docelowych, o co dbają Handlowcy. Ustawienie poziomu programu rejestrującego powoduje jedynie utworzenie rekordów dziennika odpowiadających temu poziomowi lub wyższemu.

Być może używasz ConsoleHandler(nie mogłem wywnioskować, gdzie twoje wyjście to System.err lub plik, ale przypuszczam, że jest to pierwszy), który domyślnie publikuje rekordy dziennika poziomu Level.INFO. Będziesz musiał skonfigurować tę procedurę obsługi, aby publikować rekordy dziennika poziomu Level.FINERi wyższego, aby uzyskać pożądany wynik.

Polecam przeczytanie przewodnika Java Logging Overview , aby zrozumieć podstawowy projekt. Przewodnik przedstawia różnicę między koncepcją Logger i Handler.

Edycja poziomu obsługi

1. Korzystanie z pliku konfiguracyjnego

Plik właściwości java.util.logging (domyślnie jest to logging.propertiesplik w JRE_HOME/lib) można zmodyfikować, aby zmienić domyślny poziom ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Tworzenie programów obsługi w czasie wykonywania

Nie jest to zalecane, ponieważ spowodowałoby zastąpienie konfiguracji globalnej. Użycie tego w całej bazie kodu spowoduje prawdopodobnie niemożliwą do zarządzania konfigurację programu rejestrującego.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
źródło
2
Dzięki. To załatwiło sprawę. Zdaję sobie teraz sprawę, dlaczego początkowo byłem taki zdezorientowany. Wcześniej pracowałem z poziomami rejestrowania, ale moja implementacja w tym czasie po prostu upuściła każdą zarejestrowaną wiadomość na listę, która była wyświetlana bez względu na rozszerzenie Handler.
Andrew Thompson,
3
Nie ma za co. I tak, projekt dociera do ciebie, jeśli ktoś pisał
loggery,
7
A zmiana pliku logging.properties w globalnej bibliotece JRE jest łatwa do zarządzania?
James Anderson,
2
Należy pamiętać, że sam poziom loggera musi być mniej restrykcyjny niż poziom obsługi. Przed zalogowaniem Java sprawdza maksymalny poziom między programem rejestrującym a programem obsługi. Więc jeśli ustawisz tylko handler.setLevel (Level.FINER); nic nie zobaczysz, ponieważ domyślny poziom rejestratora to Level.INFO. Musisz ustawić go na FINER, FINEST lub ALL. Powiedz logger.setLevel (Level.ALL);
Jeff_Alieffson
Myślę, że to rozwiąże problem, jako jednej liniowej, nawet podczas korzystania anonimowe i nazwane rejestratorów w domyślnym środowisku:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

Dlaczego

java.util.logging ma główny program rejestrujący, który domyślnie jest ustawiony na Level.INFO, i dołączony do niego ConsoleHandler, który również ma wartość domyślną Level.INFO. FINEjest mniejsza niż INFO, więc dokładne komunikaty nie są wyświetlane domyślnie.


Rozwiązanie 1

Utwórz rejestrator dla całej aplikacji, np. Z nazwy pakietu lub użycia Logger.getGlobal(), i podłącz do niego własny ConsoleLogger. Następnie poproś administratora programu rejestrującego o zamknięcie systemu (aby uniknąć zduplikowanych wyników komunikatów wyższego poziomu) lub poproś go, aby nie przekazywał dzienników rootowi.

public static final Logger applog = Logger.getGlobal();
...

// Create and set handler
Handler systemOut = new ConsoleHandler();
systemOut.setLevel( Level.ALL );
applog.addHandler( systemOut );
applog.setLevel( Level.ALL );

// Prevent logs from processed by default Console handler.
applog.setUseParentHandlers( false ); // Solution 1
Logger.getLogger("").setLevel( Level.OFF ); // Solution 2

Rozwiązanie 2

Alternatywnie możesz obniżyć pasek głównego programu rejestrującego.

Możesz ustawić je kodem:

Logger rootLog = Logger.getLogger("");
rootLog.setLevel( Level.FINE );
rootLog.getHandlers()[0].setLevel( Level.FINE ); // Default console handler

Lub z plikiem konfiguracyjnym logowania, jeśli go używasz :

.level = FINE
java.util.logging.ConsoleHandler.level = FINE

Obniżając poziom globalny, możesz zacząć widzieć komunikaty z podstawowych bibliotek, na przykład z niektórych komponentów Swing lub JavaFX. W takim przypadku możesz ustawić filtr w głównym programie rejestrującym, aby odfiltrowywać wiadomości nie pochodzące z programu.

Owca
źródło
applog.setUseParentHandlers (false) ten kod mi pomaga, wielkie dzięki.
aolphn
4

dlaczego moje logowanie java nie działa

udostępnia plik jar, który pomoże ci zrozumieć, dlaczego logowanie nie działa zgodnie z oczekiwaniami. Daje pełny zrzut tego, jakie programy rejestrujące i programy obsługi zostały zainstalowane, jakie poziomy są ustawione i na którym poziomie w hierarchii rejestrowania.

Mateusz
źródło
To jest komentarz, a nie odpowiedź.
hfontanez
4

CZEMU

Jak wspomniała @Sheepy, powodem, dla którego nie działa, jest to, że java.util.logging.Loggerma główny program rejestrujący, który domyślnie ma Level.INFO, a ConsoleHandlerdołączony do tego głównego rejestratora również domyślnie Level.INFO. Dlatego, aby oglądnąć FINE(, FINERlub FINEST) wyjścia, trzeba ustawić wartość domyślną rejestratorze korzeniowego i jego ConsoleHandlersię Level.FINEnastępująco:

Logger.getLogger("").setLevel(Level.FINE);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);


Problem z aktualizacją (rozwiązanie)

Jak wspomniano w @mins, komunikaty będą drukowane na konsoli dwukrotnie dla INFOi powyżej: najpierw przez anonimowego programu rejestrującego, następnie jego rodzica, głównego programu rejestrującego, który również ma ConsoleHandlerustawienie INFOdomyślne. Aby wyłączyć root logger, musisz dodać ten wiersz kodu:logger.setUseParentHandlers(false);

Istnieją inne sposoby zapobiegania przetwarzaniu dzienników przez domyślną procedurę obsługi konsoli głównego programu rejestrującego, o której wspomina @Sheepy, np .:

Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF );

Ale Logger.getLogger("").setLevel( Level.OFF );nie zadziała, ponieważ blokuje tylko wiadomość przekazaną bezpośrednio do głównego programu rejestrującego, a nie wiadomość pochodzi z podrzędnego programu rejestrującego. Aby zilustrować, jak to Logger Hierarchydziała, rysuję następujący diagram:

wprowadź opis obrazu tutaj

public void setLevel(Level newLevel)ustaw poziom dziennika określający, które poziomy komunikatów będą rejestrowane przez ten program rejestrujący. Poziomy wiadomości niższe niż ta wartość zostaną odrzucone. Wartość poziomu Level.OFF może służyć do wyłączania rejestrowania. Jeśli nowy poziom ma wartość null, oznacza to, że ten węzeł powinien dziedziczyć swój poziom po swoim najbliższym przodku z określoną (inną niż zerowa) wartością poziomu.

Joe Yichong
źródło
0

Znalazłem mój rzeczywisty problem i nie został on wymieniony w żadnej odpowiedzi: niektóre z moich testów jednostkowych powodowały, że kod inicjujący rejestrowanie był uruchamiany wiele razy w ramach tego samego zestawu testów, zakłócając rejestrowanie w późniejszych testach.

Alex R.
źródło
0

Wypróbowałem inne warianty, to może być właściwe

    Logger logger = Logger.getLogger(MyClass.class.getName());        
    Level level = Level.ALL;
    for(Handler h : java.util.logging.Logger.getLogger("").getHandlers())    
        h.setLevel(level);
    logger.setLevel(level);
// this must be shown
    logger.fine("fine");
    logger.info("info");
zhen_khokh
źródło
0

To rozwiązanie wydaje mi się lepsze, jeśli chodzi o łatwość konserwacji i projektowanie pod kątem zmian:

  1. Utwórz plik właściwości rejestrowania, osadzając go w folderze projektu zasobów, który zostanie dołączony do pliku jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Załaduj plik właściwości z kodu:

    public static java.net.URL retrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    public synchronized void initializeLogger() {
       try (InputStream is = retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
user2627331
źródło