| author | Carsten Gips (HSBI) |
|---|---|
| title | Logging |
::: tldr
Im Paket java.util.logging findet sich eine einfache Logging-API.
Über die Methode getLogger() der Klasse Logger (Factory-Method-Pattern) kann
ein (neuer) Logger erzeugt werden, dabei wird über den String-Parameter eine
Logger-Hierarchie aufgebaut analog zu den Java-Package-Strukturen. Der oberste
Logger (der "Root-Logger") hat den leeren Namen.
Jeder Logger kann mit einem Log-Level (Klasse Level) eingestellt werden;
Log-Meldungen unterhalb des eingestellten Levels werden verworfen.
Vom Logger nicht verworfene Log-Meldungen werden an den bzw. die Handler des Loggers und (per Default) an den Eltern-Logger weiter gereicht. Die Handler haben ebenfalls ein einstellbares Log-Level und verwerfen alle Nachrichten unterhalb der eingestellten Schwelle. Zur tatsächlichen Ausgabe gibt man einem Handler noch einen Formatter mit. Defaultmäßig hat nur der Root-Logger einen Handler.
Der Root-Logger (leerer String als Name) hat als Default-Level (wie auch sein
Console-Handler) "Info" eingestellt.
Nachrichten, die durch Weiterleitung nach oben empfangen wurden, werden nicht am Log-Level des empfangenden Loggers gemessen, sondern akzeptiert und an die Handler des Loggers und (sofern nicht deaktiviert) an den Elternlogger weitergereicht. :::
::: youtube
- VL Logging
- Demo Logging (Überblick)
- Demo Log-Level
- Demo Logging: Handler und Formatter
- Demo Weiterleitung an den Elternlogger :::
- Debugging
- Beeinflusst Code nicht
- Kann schnell komplex und umständlich werden
- Sitzung transient - nicht wiederholbar
\bigskip
- "Poor-man's-debugging" (Ausgaben mit
System.out.println)- Müssen irgendwann entfernt werden
- Ausgabe nur auf einem Kanal (Konsole)
- Keine Filterung nach Problemgrad - keine Unterscheidung zwischen Warnungen, einfachen Informationen, ...
\bigskip
- Logging
- Verschiedene (Java-) Frameworks:
\newline{=tex}java.util.logging(JDK), log4j (Apache), SLF4J, Logback, ...
- Verschiedene (Java-) Frameworks:
Paket java.util.logging
\bigskip
::: notes Eine Applikation kann verschiedene Logger instanziieren. Die Logger bauen per Namenskonvention hierarchisch aufeinander auf. Jeder Logger kann selbst mehrere Handler haben, die eine Log-Nachricht letztlich auf eine bestimmte Art und Weise an die Außenwelt weitergeben.
Log-Meldungen werden einem Level zugeordnet. Jeder Logger und Handler hat ein Mindest-Level eingestellt, d.h. Nachrichten mit einem kleineren Level werden verworfen.
Zusätzlich gibt es noch Filter, mit denen man Nachrichten (zusätzlich zum Log-Level) nach weiteren Kriterien filtern kann. :::
[Konsole: logging.LoggingDemo]{.ex href="https://github.com/Programmiermethoden-CampusMinden/PM-Lecture/blob/master/markdown/coding/src/logging/LoggingDemo.java"}
import java.util.logging.Logger;
Logger l = Logger.getLogger(MyClass.class.getName());-
Factory-Methode der Klasse
java.util.logging.Loggerpublic static Logger getLogger(String name);
=> Methode liefert bereits vorhandenen Logger mit diesem Namen [(sonst neuen Logger)]{.notes}
-
Best Practice:
\newline{=tex} Nutzung des voll-qualifizierten Klassennamen:MyClass.class.getName()- Leicht zu implementieren
- Leicht zu erklären
- Spiegelt modulares Design
- Ausgaben enthalten automatisch Hinweis auf Herkunft (Lokalität) der Meldung
- Alternativen: Funktionale Namen wie "XML", "DB", "Security"
public void log(Level level, String msg);\bigskip \bigskip
-
Diverse Convenience-Methoden (Auswahl):
public void warning(String msg) public void info(String msg) public void entering(String srcClass, String srcMethod) public void exiting(String srcClass, String srcMethod)
\bigskip
-
Beispiel
import java.util.logging.Logger; Logger l = Logger.getLogger(MyClass.class.getName()); l.info("Hello World :-)");
java.util.logger.Leveldefiniert 7 Stufen:SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST\newline{=tex} (von höchster zu niedrigster Prio)- Zusätzlich
ALLundOFF
\bigskip
- Nutzung der Log-Level:
- Logger hat Log-Level: Meldungen mit kleinerem Level werden verworfen
- Prüfung mit
public boolean isLoggable(Level) - Setzen mit
public void setLevel(Level)
[Konsole: logging.LoggingLevel]{.ex href="https://github.com/Programmiermethoden-CampusMinden/PM-Lecture/blob/master/markdown/coding/src/logging/LoggingLevel.java"}
::: notes
=> Warum wird im Beispiel nach log.setLevel(Level.ALL); trotzdem nur ab INFO
geloggt? Wer erzeugt eigentlich die Ausgaben?!
:::
\bigskip
\bigskip
- Pro Logger mehrere Handler möglich
- Logger übergibt nicht verworfene Nachrichten an Handler
- Handler haben selbst ein Log-Level (analog zum Logger)
- Handler verarbeiten die Nachrichten, wenn Level ausreichend
\smallskip
- Standard-Handler:
StreamHandler,ConsoleHandler,FileHandler
\smallskip
- Handler nutzen zur Formatierung der Ausgabe einen
Formatter - Standard-Formatter:
SimpleFormatterundXMLFormatter
[Konsole: logging.LoggingHandler]{.ex href="https://github.com/Programmiermethoden-CampusMinden/PM-Lecture/blob/master/markdown/coding/src/logging/LoggingHandler.java"}
::: notes
=> Warum wird im Beispiel nach dem Auskommentieren von
log.setUseParentHandlers(false); immer noch eine zusätzliche Ausgabe angezeigt (ab
INFO aufwärts)?!
:::
- Logger bilden Hierarchie über Namen
- Trenner für Namenshierarchie: "
." (analog zu Packages) [=> mit jedem "." wird eine weitere Ebene der Hierarchie aufgemacht ...]{.notes} - Jeder Logger kennt seinen Eltern-Logger:
Logger#getParent() - Basis-Logger: leerer Name (
"")- Voreingestelltes Level des Basis-Loggers:
Level.INFO(!)
- Voreingestelltes Level des Basis-Loggers:
- Trenner für Namenshierarchie: "
\bigskip
- Weiterleiten von Nachrichten
- Nicht verworfene Log-Aufrufe werden an Eltern-Logger weitergeleitet
[(Default)]{.notes}
- Abschalten mit
Logger#setUseParentHandlers(false);
- Abschalten mit
- Diese leiten [an ihre Handler sowie]{.notes} an ihren Eltern-Logger weiter (unabhängig von Log-Level!)
- Nicht verworfene Log-Aufrufe werden an Eltern-Logger weitergeleitet
[(Default)]{.notes}
[Konsole: logging.LoggingParent; Tafel: Skizze Logger-Baum]{.ex href="https://github.com/Programmiermethoden-CampusMinden/PM-Lecture/blob/master/markdown/coding/src/logging/LoggingParent.java"}
- Java Logging API im Paket
java.util.logging
\smallskip
- Neuer Logger über Factory-Methode der Klasse
Logger- Einstellbares Log-Level (Klasse
Level) - Handler kümmern sich um die Ausgabe, nutzen dazu Formatter
- Mehrere Handler je Logger registrierbar
- Log-Level auch für Handler einstellbar (!)
- Logger (und Handler) "interessieren" sich nur für Meldungen ab bestimmter Wichtigkeit
- Logger reichen nicht verworfene Meldungen defaultmäßig an Eltern-Logger weiter (rekursiv)
- Einstellbares Log-Level (Klasse
::: readings
- @JDK-Doc [Kap. 8] :::
::: outcomes
- k3: Nutzung der Java Logging API im Paket java.util.logging
- k3: Erstellung eigener Handler und Formatter :::
::: quizzes
::: challenges
-
Schreiben Sie einen Formatter, welcher die Meldungen in folgendem Format auf der Konsole ausgibt. Bauen Sie diesen Formatter in alle Logger ein.
------------ Logger: record.getLoggerName() Level: record.getLevel() Class: record.getSourceClassName() Method: record.getSourceMethodName() Message: record.getMessage() ------------ -
Schreiben Sie einen weiteren Formatter, welcher die Daten als Komma-separierte Werte (CSV-Format) mit der folgenden Reihenfolge in eine Datei ausgibt (durch Anfügen einer neuen Zeile an bereits bestehenden Inhalt). Bauen Sie diesen Formatter in den Logger für den Ringpuffer ein.
record.getLoggerName(),record.getLevel(),record.getSourceMethodName(),record.getSourceClassName(),record.getMessage() -
Ersetzen Sie in einem Beispielprogramm sämtliche Konsolenausgaben (
System.out.printlnundSystem.err.println) in der Vorgabe durch geeignete Logger-Aufrufe mit passendem Log-Level.Alle Warnungen und Fehler sollen zusätzlich in eine
.csv-Datei geschrieben werden. Auf der Konsole sollen alle Log-Meldungen ausgegeben werden. :::
