Logging gehört zu den Dingen, die am Anfang oft nebensächlich wirken. Der Code läuft, die Ausgabe passt, also scheint alles in Ordnung zu sein. Spätestens wenn ein Fehler nur in einer bestimmten Umgebung auftritt, eine Anfrage unerwartet lange dauert oder ein Problem nicht sauber reproduzierbar ist, merkst du aber schnell, wie wichtig gute Logs sind. Ein sauberes Logging hilft dir nicht nur beim Debugging, sondern auch dabei, Anwendungen im laufenden Betrieb nachvollziehbar zu halten.
Gerade in Java-Projekten wird Logging häufig entweder zu sparsam oder komplett übertrieben eingesetzt. Beides ist unpraktisch. Wenn du zu wenig loggst, fehlen dir im Ernstfall die entscheidenden Informationen. Wenn du zu viel loggst, findest du das Wesentliche zwischen belanglosen Meldungen nicht mehr. Gutes Logging ist deshalb kein Selbstzweck. Es soll dir helfen, den Zustand deiner Anwendung zu verstehen, ohne dabei unnötigen Lärm zu erzeugen.
Was gutes Logging in der Praxis leisten soll
Ein gutes Log beantwortet dir im besten Fall drei einfache Fragen: Was ist passiert, wann ist es passiert und in welchem Kontext ist es passiert. Genau deshalb sollten Log-Meldungen immer so formuliert sein, dass du sie auch Wochen später noch verstehst. Eine Nachricht wie Fehler aufgetreten bringt dir praktisch nichts. Eine Nachricht wie Bestellung konnte nicht gespeichert werden, orderId=4711 ist deutlich nützlicher, weil sie direkt einen fachlichen Bezug und einen konkreten Kontext liefert.
Du solltest vor allem an Stellen loggen, an denen Zustandswechsel, externe Kommunikation oder Fehler relevant sind. Dazu gehören zum Beispiel der Start wichtiger Prozesse, Aufrufe externer Systeme, fehlgeschlagene Validierungen, technische Ausnahmen oder unerwartete Abzweigungen im Code. Nicht jede einzelne Schleifeniteration und nicht jeder Getter braucht ein Log-Statement. Logging ist kein Ersatz für sauberes Debugging und auch kein Ersatz für Tests.
Ebenso wichtig ist die passende Log-Stufe. INFO eignet sich für wichtige, normale Ereignisse im Ablauf. DEBUG ist für zusätzliche technische Details gedacht, die du bei der Fehlersuche brauchst, aber nicht dauerhaft in jeder Umgebung sehen willst. WARN ist sinnvoll, wenn etwas nicht ideal läuft, die Anwendung aber noch weiterarbeiten kann. ERROR steht für echte Fehler, bei denen ein fachlicher oder technischer Ablauf scheitert. Wenn du diese Stufen sauber trennst, lassen sich Logs deutlich besser filtern und auswerten.
Was du loggen solltest und was nicht
Sinnvoll sind Informationen, mit denen du einen Ablauf eindeutig einordnen kannst. Dazu gehören IDs, Statuswerte, technische Endpunkte, fachliche Schlüssel oder klare Fehlerursachen. Wenn ein Request reinkommt, ist zum Beispiel eine Kunden-ID, eine Auftragsnummer oder eine Korrelations-ID meist wertvoll. So kannst du später nachvollziehen, welche Aktion zu welchem Ergebnis geführt hat.
Was du auf keinen Fall loggen solltest, sind sensible Daten. Dazu gehören Passwörter, Tokens, Session-IDs, komplette personenbezogene Daten, Kreditkarteninformationen oder interne Geheimnisse wie API-Keys. Auch komplette Request- oder Response-Objekte solltest du nicht blind ins Log kippen. Erstens riskierst du damit Datenschutzprobleme, zweitens blähst du deine Logs unnötig auf und drittens landen dort schnell Daten, die dort nie hätten auftauchen dürfen.
Vorsichtig solltest du auch mit Exceptions umgehen. Eine Exception einfach nur mit e.getMessage() zu loggen reicht oft nicht aus, weil dir dann der Stacktrace fehlt. Andersherum solltest du dieselbe Exception nicht an mehreren Stellen erneut als ERROR loggen. Das führt zu doppelten oder dreifachen Fehlermeldungen für ein einziges Problem. Die Regel ist einfach: Entweder du behandelst den Fehler sauber und loggst ihn dort sinnvoll, oder du reichst ihn weiter und loggst ihn nur an der Stelle, an der er wirklich relevant abgeschlossen wird.
Ein weiterer häufiger Fehler ist unpräzises Logging. Nachrichten wie Start, läuft, hier angekommen oder debug1 helfen niemandem, auch dir selbst nicht. Schreib stattdessen konkret, welcher Vorgang gerade passiert. Wenn du Logs liest, solltest du möglichst ohne Blick in den Code verstehen können, was gerade abgelaufen ist.
Log4j sinnvoll einsetzen
Mit Log4j kannst du Logging sauber strukturieren, ohne überall System.out.println() zu verteilen. Genau das ist auch der richtige Weg. System.out.println() mag für einen schnellen Test lokal funktionieren, ist in echten Projekten aber unpraktisch, weil dir Log-Stufen, Konfiguration, Formatierung und zentrale Steuerung fehlen.
Ein Logger gehört in die Klasse, die auch wirklich etwas protokolliert. Typischerweise legst du ihn als private static final an. Wichtig ist außerdem, dass du Parameter sauber an die Log-Methode übergibst, statt Strings unnötig per + zu verketten. Das ist lesbarer und vermeidet unnötige Arbeit, wenn ein bestimmtes Log-Level gerade gar nicht aktiv ist.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class OrderService {
private static final Logger log = LogManager.getLogger(OrderService.class);
public void saveOrder(Long orderId) {
log.info("Speichere Bestellung mit ID {}", orderId);
try {
// Speichern
log.debug("Bestellung {} wurde erfolgreich verarbeitet", orderId);
} catch (Exception e) {
log.error("Bestellung {} konnte nicht gespeichert werden", orderId, e);
}
}
}
In dem Beispiel ist vor allem wichtig, dass die Log-Nachrichten einen klaren Kontext haben. Du siehst sofort, um welche Bestellung es geht und ob es sich um einen normalen Ablauf oder einen Fehler handelt. Die Exception wird direkt mitgegeben, damit der Stacktrace im Log landet. Genau das brauchst du später für die Analyse.
Die eigentliche Stärke von Log4j liegt in der Konfiguration. Du kannst steuern, welche Log-Stufen in welcher Umgebung aktiv sind und wohin geloggt wird. In einer lokalen Entwicklungsumgebung darf DEBUG ruhig aktiver sein. In produktionsnahen Umgebungen willst du meist gezielter loggen, damit die Menge beherrschbar bleibt. Gerade in Java-Anwendungen mit Application Servern wie WildFly ist das wichtig, weil du dort oft mehrere Komponenten zusammenspielen hast und Logs schnell unübersichtlich werden können.
Auch das Paket oder die Klasse spielt bei der Konfiguration eine Rolle. Du musst nicht global alles auf DEBUG setzen, nur weil du in einem Service tiefer reinschauen willst. Meist ist es besser, gezielt nur die relevanten Klassen oder Packages anzupassen. So behältst du die Kontrolle über die Log-Menge.
Fazit
Gutes Logging ist kein Beiwerk, sondern Teil von sauberem Softwaredesign. Du loggst nicht, um möglichst viele Meldungen zu erzeugen, sondern um Verhalten nachvollziehbar zu machen. Logge deshalb nur Dinge, die dir wirklich helfen, einen Ablauf zu verstehen oder einen Fehler einzugrenzen. Nutze die Log-Level bewusst, formuliere Meldungen konkret und achte konsequent darauf, keine sensiblen Daten ins Log zu schreiben.
Mit Log4j hast du in Java ein solides Werkzeug dafür. Wenn du dich früh daran gewöhnst, Logs bewusst und sparsam einzusetzen, sparst du dir später viel Zeit bei der Fehlersuche. Gute Logs sind am Ende keine Dekoration im Terminal, sondern ein Arbeitswerkzeug, auf das du dich verlassen können musst.
