Logging-Konfiguration über externe Datei

Das Logging durch die Java Logging-API kann durch eine externe Datei mit Hilfe eines LogManager gesteuert werden.

Java verwendet zum Logging ein zweistufiges System, das im einfachsten Fall aus einem Logger-Objekt, das für die Erzeugung der Log-Nachricht, dem LogRecord, zuständig ist und einem die Ausgabe steuernden Handler besteht. Der Logger ist üblicherweise auf package- oder class-Ebene definiert, während der Handler programmweit existiert. Im Gegensatz zum Logger, der initialisiert werden muss, muss ein Handler nicht zwingend explizit erzeugt werden. Java initialisiert zur Laufzeit als Standard einen ConsoleHandler, der die Ausgabe regelt. Er gibt die Meldungen bis zum Level INFO incl. auf System.err aus.

import java.util.logging.Logger;

public class LoggingConfigBsp {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.severe("schwerer Fehler");
        logger.warning("Vorsicht!");
        logger.fine("vielleicht anders...?");
        logger.finest("gaaaaanz fein");
    }
}

Zur Demonstration dient das obige Quelltext-Beispiel, in dem in main() lediglich ein Logger-Objekt erzeugt wird und einige Meldungen auf unterschiedlichen Logebenen generiert werden. Die Methoden severe(), warning(), fine() und finest() erzeugen Lognachrichten, deren Text als Parameter übergeben wird. Führt man es aus, so erzeugt das Programm die folgende Ausgabe:

Nov 02, 2017 7:59:57 AM allgemeines.logging.LoggingConfigBsp main
SCHWERWIEGEND: schwerer Fehler
Nov 02, 2017 7:59:57 AM allgemeines.logging.LoggingConfigBsp main
WARNUNG: Vorsicht!

Zur Ausgabe wird, wie oben angesprochen, der Standard-ConsoleHandler verwendet. Greift man nicht verändernd ein, so sind sowohl der Logger als auch der Handler so konfiguriert, dass sie die Meldungen nur bis zum Level INFO ausgeben. Das Logging der Ebenen SEVERE und WARNING wird durchgeführt, da diese Logebenen oberhalb von INFO liegen1. Eine Ausgabe der Meldungen der Level FINE und FINEST unterbleibt jedoch.

Für diese Konfiguration ist eine Datei logging.properties verantwortlich, die sich im Verzeichnis lib des JRE befindet. Sie enthält2 den folgenden, der Übersicht halber um die Kommentare bereinigten Inhalt:

############################################################
#   Default Logging Configuration File
# ...
handlers= java.util.logging.ConsoleHandler
.level= INFO
# ...
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# ...
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
#...

Die erste Zeile legt fest, dass ein ConsoleHandler als Standard-Handler Verwendung finden soll. Die zweite Zeile definiert INFO als globales Loglevel. In den beiden dann folgenden Blöcken werden zunächst die Standard-Einstellungen für einen FileHandler, der die Ausgabe in eine Datei steuert, und dann diejenigen für einen ConsoleHandler festgelegt. Details zur Konfiguration können der Tabelle unten entnommen werden.

LogManager

Die angegebenen Werte werden von einem LogManager verwaltet, der auch alle Logger-Objekte managed. Als Singleton existiert von ihm nur ein Objekt, auf das durch LogManager.getLogManager() zugegriffen werden kann. Durch Aufruf der überladenen Methode LogManager.readConfiguration() kann der LogManager zur Laufzeit veranlasst werden, die Konfigurationsdatei neu einzulesen, sodass die vorgegebenen Standardeinstellungen u.U. sogar geändert werden können.
Hierzu muss dann ggf. eine abweichende Konfigurationsdatei angegeben werden, die die Werte der Standarddatei funktional ersetzt. Sie kann durch einen FileInputStream eingelesen werden, der der Methode als Parameter übergeben wird.

Der obige Quelltext kann hierzu auf folgende Weise ergänzt werden, um die Konfigurationsdatei myLogging.properties aus dem Arbeitsverzeichnis einzulesen:

Logger logger = Logger.getAnonymousLogger();
LogManager manager = LogManager.getLogManager();
try {
    manager.readConfiguration(new FileInputStream("myLogging.properties"));
} catch (IOException e) {
    logger.warning(e.getMessage());
}
//...

Eine zweite Möglichkeit besteht darin, die abgewandelte Konfigurationsdatei direkt als Systemeigenschaft einzutragen und sie dann durch Angabe des Property-Keys einzulesen:

System.setProperty("java.util.logging.config.file", "myLogging.properties");
//...
manager.readConfiguration(new FileInputStream(System.getProperty("java.util.logging.config.file")));
//...
         

Geänderte Standard-Konfiguration

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

Enthält myLogging.properties die obigen Konfigurationsangaben, so wird durch .level=FINEST das Ausgabelevel des Loggers global auf FINEST gesetzt, in der zweiten Zeile der Standard-Handler als ConsoleHandler definiert und dessen Ausgabelevel in der dritten Zeile schließlich auf FINE gesetzt. Entsprechend wird die Nachricht der Ebene FINEST hier unterdrückt.
Da die zentrale Konfigurationsdatei funktional ersetzt wird, muss beachtet werden, dass hier explizit ein Handler aufgeführt werden muss. Würde der Handler (hier ConsoleHandler) nicht angegeben, so würde auch nichts auf System.err ausgegeben.
Die Ausgabe sieht folgendermaßen aus:

Nov 04, 2017 9:51:05 AM allgemeines.logging.LoggingConfigBsp main
SCHWERWIEGEND: schwerer Fehler
Nov 04, 2017 9:51:05 AM allgemeines.logging.LoggingConfigBsp main
WARNUNG: Vorsicht!
Nov 04, 2017 9:51:05 AM allgemeines.logging.LoggingConfigBsp main
FEIN: vielleicht anders...?

Ein zusätzlicher FileHandler

Soll zusätzlich zum ConsoleHandler noch eine Ausgabe des Loggings in eine Datei erfolgen, so kann die Konfigurationsdatei entsprechend erweitert und zur Konfiguration eines FileHandler eingerichtet werden:

handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler

java.util.logging.FileHandler.pattern=%h/java%u.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level=WARNING

Im Quelltext muss der FileHandler dann unter Angabe des Speicherortes der Datei noch erzeugt und beim Logger angemeldet werden. Hierbei ist wichtig, dass die Konfigurationsdatei vor der Objektbildung des Handlers eingelesen wird, ansonsten wird er entsprechend der dann noch aktiven Standard-Konfigurationsdatei eingerichtet.

try {
    manager.readConfiguration(new FileInputStream("myLogging.properties"));
    FileHandler fileHandler = new FileHandler("myLogFile.log");
    logger.addHandler(fileHandler);
} catch (IOException e) {
    logger.warning(e.getMessage());
}

Konfiguration des FileHandler

Ein FileHandler kann durch eine Anzahl an Eigenschaften zusätzlich konfiguriert werden. Sie werden durch den Punktoperator mit dem voll qualifizierten Namen des Handlers verbundenen:

java.util.logging.FileHandler.pattern=%h/java%u.log

Ein Überblick über die zur Verfügung stehenden Eigenschaften finden sich in der folgenden Liste:

.level
Ausgabelevel des Handlers
.filter
spezifiziert ein Objekt vom Typ java.util.logging.Filter. Filter ist ein funktionales Interface zur Feinsteuerung des Loggings.
.formatter
spezifitiert einen Formatter, der das Format der Ausgabe steuert. Standard ist ein XMLFormatter.
.encoding
spezifiziert das Character-Set der Ausgabe.
.limit
spezifiziert eine ungefähre Maximalgröße einer Ausgabedatei. 0 bezeichnet keine Grenze.
.count
spezifiziert, wieviele Ausgabedateien durchlaufen werden sollen.
.pattern
spezifiziert ein Muster zur Benennung der Logdateien.

Musterangaben zur Benennung von Logdateien

Die Eigenschaft .pattern zur Erzeugung des Dateinamens von Log-Dateien definiert einen String, der spezielle Musterangaben enthalten kann, die zur Laufzeit durch die folgenden Angaben ersetzt werden:

"/" der lokale Dateitrenner
"%t" das temporäre Verzeichnis des Systems
"%h" Pfad des Heimatverzeichnisses des Users
"%g" die aufsteigende Generationsnummer bei rotierenden Logs beginnend bei 0
"%u" eine eindeutige Nummer, um Konflikte zu vermeiden, z.B. beim Zugriff auf bereits anderweitig geöffnete Dateien.
"%%" ein einfaches Prozentzeichen ("%")
Quellen

https://docs.oracle.com/javase/7/docs/api/java/util/logging/package-summary.html

1) Zu den Loggingebenen siehe https://javabeginners.de/Allgemeines/Logging/Einfaches_Logging.php#loggingebenen
2) java version "1.8.0_25" auf MacOS 10.11.6

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.