Zum Inhalt

Logging #

Logging Video Thumbnail Logging Video Thumbnail

Logging Video

Was ist Logging?#

Logging ist das Protokollieren von Prozessen. Es dient zur Aufzeichnung und Nachvollziehung von Prozessen, wie z.B. die Ausführung von Befehlen, die durchgeführt werden. Durch Logs wird eine Applikation in Echtzeit überwacht und ausgewertet.

Wenn eine Applikation in Produktion läuft, dann wird sie meistens in einem externen Rechenzentrum ausgeführt. Dort ist es nicht mehr möglich, mithilfe des Debuggers nachzuschauen, was passiert. Das Einzige, was man dann zur Verfügung hat, sind Logs.

Das Ziel von Logging#

Das Ziel von Logging ist es, dass Gefahren frühzeitig erkennt werden. Falls es zu einem Fehler kommt, sollte die Ursache ermittelt werden können. Anhand von Logs sollte nachvollziehbar sein, was passiert und ob es sich richtig verhaltet.

Was wird geloggt?#

Das Verhalten der Applikation wird geloggt. Das bedeutet, was für Befehle werden ausgeführt, was passiert im Hintergrund und wie sieht das Ergebnis aus. Als Beispiel einer API könnte folgendes geloggt werden:

  • Aufrufe:

    • Welche IP / Nutzer
    • Endpunkt
  • Verbindungen:

    • Datenbank
    • Microservice
  • Auslastung

  • Ungewöhnliche Ausführungszeit

Wie wird geloggt?#

Um eine Applikation zu Loggen wir meistens ein Logging-Framework verwenden. Dieses übernimmt die Aufgabe vom Formatieren, Verwalten und Ausgeben von Logs. Java selbst hat einen SimpleLogger, welcher die einfachsten Funktionalitäten bietet. Meist wird jedoch Log4J verwendet, welches eine vollständige Logging-Framework bietet. Im Spring Boot-Framework ist ein Logger-Bean verfügbar. Dieses funktioniert mithilfe von Logback, nochmals ein anderes Logging-Framework. Um schnell das Framework zu ändern und unabhängig zu sein, kann SLF4J verwendet werden.

Apache Log4J Sicherheitslücke

Im Dezember 2021 wurde eine Sicherheitslücke in Log4J entdeckt. Diese ermöglichte einem Angreifer das entfernte Ausführen von Befehlen. Es musste nur eine Verbindung zu einem Server aufgebaut werden und schon konnte das System übernommen werden. Aus diesem Grund sollte die Version Log4J 2.17+ verwendet werden, welche die Sicherheitslücke nicht mehr enthält. Mehr Informationen finden Sie auf LunaSec.

Log Level#

Bevor wir uns anschauen, wie Logging in Java angewendet wird, sollten wir zuerst über die verschiedenen Log-Level reden. Diese werden verwendet, um Logs zu kategorisieren. Sie sagen aus, wie wichtig eine Meldung ist.

TRACE: Das ist die geringste Priorität, bei welcher jede Kleinigkeit geloggt wird. Es wird sehr selten und nur zum Debuggen genutzt.

DEBUG: Dies ist der Standard Log Level für die Entwicklung. Es kann als Ersatz für einen Sysout genutzt werden. Es wird bei der Entwicklung genutzt, um zu sehen, wie die Applikation läuft und ob Variablen und Methoden die richtigen Werte zurückgeben.

INFO: Dieses Level zeigt das normale Verhalten einer Applikation an. Es gibt zum Beispiel Info über einen ausgeführten Prozess oder einen eingehenden Request. Diese Meldungen sind harmlos und können ignoriert werden.

WARN: Dieses Level zeigt Warnungen an. Damit sollten Gefahren frühzeitig erkannt und verhindert werden. Es zeigt an das etwas nicht normal läuft, jedoch nicht problematisch für das Weiterausführen ist.

ERROR: Sobald eine Fehlermeldung auf diesem Level auftritt, gibt es ein schwerwiegendes Problem. Als Beispiel wäre eine gescheiterte SQL-Abfrage oder ein nicht erreichbarer Endpunkt. Eine Meldung in diesem Level sollte sofort behoben werden.

FATAL: Dieses Level ist für Fehler, die das System komplett zusammenbrechen. Wenn eine solche Meldung auftritt, dann kann die Applikation oder wichtige Teile davon nicht mehr funktionieren. Als Beispiel wäre eine Datenbankverbindung, die nicht mehr aufgebaut werden kann.

Die Loglevels sind hierarchisch aufgebaut. Das bedeutet, wenn als Level WARN gewählt wird, werden nur Warnungen und alles darüber (WARN, ERROR, FATAL) geloggt.

In der Produktion wird das Log-Level meistens auf WARN oder sogar ERROR gesetzt. Dies wird gemacht, um die Effizienz zu erhöhen, da das Loggen von einfachen Infomeldungen nicht wichtig ist und nur Zeit zum Speichern verbraucht.

Beispiele#

Installieren

Log4J kann mithilfe von Maven installiert und verwaltet werden.

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
    </dependency>
Beispiel Klasse
package ch.bztf.logging.logging;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Example {

    // Logger der aktuellen Klasse erstellen (1)
    private final Logger log = LogManager.getLogger(Example.class);

    public int logDebug(int a, int b) {
        // Rechne zwei Zahlen zusammen
        int c = a + b;

        // Logge die Ergebnisse (2)
        log.debug("{} + {} = {}", a, b, c);

        // Gib das Ergebnis zurück
        return c;
    }

    public int logError(int a, int b) {
        try {
            // Rechne zwei Zahlen durcheinander (3)
            int c = a / b;

            // Gib das Ergebnis zurück
            return c;

        } catch (ArithmeticException e) {
            // Division durch 0 ist nicht möglich (4)
            log.error("Division durch 0 ist nicht möglich. Werte: {} / {}", a, b);

            return Integer.MIN_VALUE;
        }
    }
}
  1. Hier wird ein Logger der aktuellen Klasse erstellt. Dafür wird der LogManager verwendet, welcher in Log4J enthalten ist.

  2. Die Rechnung mit dem Ergebnis wird geloggt. So kann in den Logs überprüft werden, ob die richtig gerechnet wurde. Dieser Log hilft zum Debuggen der Applikation. Deswegen wurde das Log Level DEBUG benutzt.

  3. Bei dieser Rechnung kann eine Division durch 0 auftreten.

  4. Falls eine Division durch 0 auftritt, wird ein Fehler geworfen. Da es ein Fehler ist, welcher nicht auftreten sollte wird das Log Level ERROR gewählt.

Output
// So könnten die Log-Meldungen in der Konsole aussehen: (1)
14:41:00.870 [main] DEBUG ch.bztf.logging.logging.Example - 1 + 2 = 3
14:41:00.873 [main] ERROR ch.bztf.logging.logging.Example - Division durch 0 ist nicht möglich. Werte: 2 / 0
  1. Die Log-Meldungen sind nach dem Format: [DATUM] [THREAD] [LEVEL] [KLASSENNAME] - [MELDUNG] aufgebaut.

Was passiert mit den Logs?#

Nun sollt ein Basiswissen über die Logging-Technologie vorhanden sein. Eine Frage besteht jedoch noch. Was passiert mit den Logs? Wenn eine Log-Meldung entsteht, dann muss sie irgendwo gespeichert werden, damit sie später ausgewertet werden können. Dafür gibt es viele verschiedene Lösungen, die immer auf den Anwendungszweck und den Umfang der Anwendung angepasst werden müssen.

Die einfachste und simpelste Lösung ist eine Log-Datei. Alle Meldungen werden in dieser Datei gespeichert und können später ausgewertet werden. Wenn die Applikation bei einem Endnutzer läuft, dann ist dies eine sehr gute Lösung, da dieser bei einem Fehler nur die Datei per Mail dem Support versenden muss.

Für umfangreichere Projekte lohnt es sich, eine Datenbank zu nutzen. Diese Datenbank wird dann für alle Meldungen gespeichert und kann später ausgewertet werden. Die Datenbank InfluxDB ist für Zeit- und Datenbank-Logs optimiert. Der Vorteil ist, dass mehrere Anwendungen auf die gleiche Datenbank zugreifen können und somit alle Meldungen sich an einem zentralen Ort befinden.

Der Elastic-Stack (Elasticsearch – Logstash – Kibana) ist ein Entwicklungssack, welcher das Speichern, Suchen, Analysieren und Visualisieren von Logs ermöglicht. Er ist sehr umfangreich und kann für kleinere Anwendungen Overkill sein. Das Endergebnis ist jedoch kein Vergleich zu einer einfachen Log-Datei, da Meldungen sehr schnell entdeckt und visualisiert werden können. Dabei wird auch die Applikation geprüft, ob sie erreichbar ist und richtige Werte zurückliefert.