Amarengo

Articles and news

9 Protokollierungssünden in Ihren Java-Anwendungen

Das Protokollieren von Laufzeitinformationen in Ihrer Java-Anwendung ist äußerst nützlich, um das Verhalten einer App zu verstehen, insbesondere in Fällen, in denen unerwartete Szenarien auftreten, Fehler oder müssen nur bestimmte Anwendungsereignisse verfolgen.

In einer realen Produktionsumgebung haben Sie normalerweise nicht den Luxus des Debuggens. Daher können Protokollierungsdateien das einzige sein, was Sie tun müssen, wenn Sie versuchen, ein Problem zu diagnostizieren, das nicht einfach zu reproduzieren ist.

Richtig gemacht, können Protokolldateien Ihnen auch viel Zeit sparen, indem Sie Hinweise auf die Ursache des Problems und auf den Zustand des Systems zum Zeitpunkt des Auftretens geben. Die Protokollierung kann auch für Überwachungszwecke, das Sammeln von Statistiken, das Extrahieren von Business Intelligence und eine Vielzahl anderer Aufgaben nützlich sein.

Insgesamt ist die Protokollierung sicherlich eine grundlegende Praxis, die während der Lebensdauer der Anwendung erhebliche Vorteile bietet – daher kann es verlockend sein, so viele Protokolldaten wie möglich aufzuzeichnen.

Die unsachgemäße Verwendung der Protokollierung kann jedoch auch erhebliche Nachteile haben.

In den folgenden Abschnitten werfen wir einen Blick auf einige der häufigsten und schädlichsten Praktiken, auf die Sie bei der Anmeldung in einer Anwendung stoßen können.

Alle Beispiele und Konfigurationen verwenden die beliebte log4j 2-Bibliothek. Logback ist eine weitere großartige Option, die auch von Stackify gut unterstützt wird.

9 Java-Protokollierungsprobleme und wie man sie vermeidet

Protokollierung sensibler Informationen

Zunächst einmal ist die wahrscheinlich schädlichste Protokollierungspraxis, die durch den Ansatz „So viel wie möglich protokollieren, nur für den Fall“ hervorgerufen wird, die Anzeige sensibler Informationen in den Protokollen.

Die meisten Anwendungen verarbeiten Daten, die privat bleiben sollen, z. B. Benutzeranmeldeinformationen oder Finanzinformationen. Die Gefahr, dass diese Art von Informationen in einer Nur–Text-Datei protokolliert wird, ist klar – Protokolldateien werden sehr wahrscheinlich von mehreren, ungesicherten Systemen verarbeitet.

Darüber hinaus ist die Protokollierung einiger Kategorien von Daten, wie z. B. Finanzinformationen, stark reguliert und kann schwerwiegende rechtliche Auswirkungen haben.

Der beste Weg, dies zu vermeiden, besteht einfach darin, sicherzustellen, dass Sie diese Art von sensiblen Informationen niemals protokollieren.

Es gibt Alternativen, wie die Verschlüsselung der Protokolldateien, aber das macht diese Dateien im Allgemeinen insgesamt viel weniger nutzbar, was nicht ideal ist.

Bevor wir fortfahren, finden Sie hier eine umfassendere Liste der Arten von Informationen, mit denen Sie sehr vorsichtig sein müssen.

Protokollierung einfacher Benutzereingaben

Ein weiteres häufiges Sicherheitsproblem in Java-Anwendungen ist das JVM-Logging.

Einfach ausgedrückt kann Log-Forging auftreten, wenn Daten von einer externen Quelle wie Benutzereingaben oder einer anderen nicht vertrauenswürdigen Quelle direkt in die Protokolle geschrieben werden. Ein böswilliger Angreifer kann Eingaben eingeben, die einen Protokolleintrag simulieren, z. B. „\n\nweb – 2017-04-12 17:47:08,957 INFO Amount reversed successfully“, was zu beschädigten Protokolldaten führen kann.

Es gibt verschiedene Möglichkeiten, mit dieser Art von Sicherheitsanfälligkeit umzugehen:

  • protokollieren Sie keine Benutzereingaben – nicht immer möglich, da die Benutzerdaten für die Ermittlung der Ursache einiger Probleme von entscheidender Bedeutung sein können
  • Verwenden Sie die Validierung vor der Protokollierung – Diese Lösung kann sich auf die Leistung auswirken und auf die Protokollierung wichtiger Informationen verzichten
  • Melden Sie sich in einer Datenbank an – sicherer, aber hinsichtlich der Leistung kostspieliger und kann eine weitere Sicherheitsanfälligkeit verursachen – SQL Injection
  • Sicherheits-API von OWASP

Die Verwendung von ESAPI ist definitiv ein guter Weg; diese Open-Source-Sicherheitsbibliothek von OWASP kann Daten codieren, bevor sie in die Protokolle geschrieben werden:

message = message.replace( '\n' , '_' ).replace( '\r' , '_' ) .replace( '\t' , '_' );message = ESAPI.encoder().encodeForHTML( message );

Übermäßige Protokollierung

Eine weitere zu vermeidende Praxis ist die Protokollierung zu vieler Informationen. Dies kann bei dem Versuch geschehen, alle potenziell relevanten Daten zu erfassen.

Ein mögliches und sehr reales Problem bei diesem Ansatz ist eine verminderte Leistung. Mit der Entwicklung der Protokollierungsbibliotheken verfügen Sie jedoch jetzt über die Tools, um dies weniger problematisch zu machen.

Als Beispiel für eine verbesserte Leistung, die 2.die x-Version von log4j verwendet die asynchrone Protokollierung, dh die Ausführung von E / A-Vorgängen in einem separaten Thread.

Zu viele Protokollmeldungen können auch zu Schwierigkeiten beim Lesen einer Protokolldatei und beim Identifizieren der relevanten Informationen führen, wenn ein Problem auftritt.

Eine Möglichkeit, die Anzahl der Protokollzeilen zu reduzieren, besteht darin, wichtige Informationen über übergreifende Probleme im System zu protokollieren.

Wenn Sie beispielsweise den Start und das Ende bestimmter Methoden protokollieren möchten, können Sie einen Aspekt hinzufügen, der dies für jede Methode mit einer angegebenen benutzerdefinierten Annotation ausführt:

@Aspectpublic class MyLogger { private static final Logger logger = LogManager.getLogger(MyLogger.class); @Around("execution(* *(..)) && @annotation(LogMethod)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("Starting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); Object result = joinPoint.proceed(); logger.info("Exiting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); return result; }}

Mit Hilfe des benutzerdefinierten Aspekts können wir jetzt sehr selektiv sein und die genauen Bereiche der Anwendung auswählen, in denen wir diese Informationen tatsächlich in den Protokollen benötigen. Dadurch können wir den gesamten Logging-Footprint des Systems erheblich reduzieren.

Kryptische Protokollmeldungen

Beim Parsen von Protokolldateien kann es frustrierend sein, auf eine Zeile zu stoßen, die nicht genügend Informationen liefert. Eine häufige Falle ist der Mangel an Spezifität oder Kontext in Protokollnachrichten.

Um das Problem zu veranschaulichen, werfen wir einen Blick auf eine Protokollnachricht, der die Spezifität fehlt:

Operation failed.

Stattdessen können Sie spezifischere und identifizierbarere Informationen hinzufügen:

File upload picture.jpg failed.

Denken Sie immer daran, dass Ihre Protokolle mit Sicherheit von einem anderen Entwickler oder Systemadministrator gelesen werden und diese verstehen müssen, was in der Anwendung passiert ist.

Eine gute Möglichkeit, Kontext in Protokollnachrichten hinzuzufügen, besteht darin, den Zeitstempel, die Protokollebene, den Threadnamen und den vollständig qualifizierten Klassennamen des Ereignisses einzuschließen. Auf diese Weise können Sie leichter erkennen, wann und wo bestimmte Ereignisse zur Laufzeit auftreten.

Um diese Informationen bei Verwendung von log4j 2 hinzuzufügen, können Sie ein Musterlayout mit den Optionen %d für das Datum, %p für die Protokollebene, %t für den Threadnamen und %c für den Klassennamen konfigurieren:

<PatternLayout> <Pattern>%d %p %c - %m%n</Pattern></PatternLayout>

Eine Protokollnachricht mit dem obigen Layout sieht folgendermaßen aus:

2017-05-11 22:51:43,223 INFO com.stackify.service.MyService - User info updated

Verwenden einer einzelnen Protokolldatei

Der Nachteil der Verwendung nur einer Protokolldatei für die Anwendung besteht darin, dass diese im Laufe der Zeit ziemlich groß und schwierig zu bearbeiten wird.

Um relevante Informationen schnell zu finden, empfiehlt es sich, jeden Tag eine neue Protokolldatei mit dem Datum als Teil des Dateinamens zu erstellen.

Sehen wir uns ein Beispiel an, wie Sie eine Protokolldatei mit dem Namen des aktuellen Datums erstellen, wenn Sie die log4j2-Bibliothek verwenden:

SimpleLayout layout = new SimpleLayout();FileAppender appender = new FileAppender(layout, LocalDate.now().toString(), false);logger.addAppender(appender);

Dieselbe Bibliothek bietet auch die Möglichkeit, einen fortlaufenden Datei-Appender zu konfigurieren, der in bestimmten Zeitintervallen neue Protokolldateien erstellt:

<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c - %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="250 MB"/> </Policies> <DefaultRolloverStrategy max="20"/></RollingFile>

Die obige Konfiguration führt zu einer oder mehreren Dateien, die für jeden Tag bis zu 250 MB pro Datei mit dem aktuellen Datum als Dateinamen erstellt werden und in Ordnern mit Namen des Formulars Jahr-Monat abgelegt werden.

Auswahl falscher Protokollebenen

Die Auswahl einer unzureichenden Protokollebene führt dazu, dass entweder wichtige Ereignisse fehlen oder viele weniger wichtige Daten überflutet werden.

Einfach ausgedrückt, ist die Auswahl der richtigen Protokollebene für die verschiedenen Protokolle in Ihrem System eines der wichtigsten Dinge, die Sie richtig machen müssen, um Ihre Protokolle gut zu verstehen.

Die meisten Protokollierungsframeworks haben eine Reihe von Ebenen, die FATAL, ERROR, WARN, INFO, DEBUG, TRACE ähneln und vom höchsten zum niedrigsten geordnet sind.

Verfügbare Protokollebenen

Werfen wir einen Blick auf diese und die Art der Protokollnachrichten, die sie enthalten sollten, basierend auf dem Schweregrad:

  • FATAL sollte für Fehler reserviert werden, die dazu führen, dass die Anwendung abstürzt oder nicht gestartet wird (z: Speicher)
  • FEHLER sollte technische Probleme enthalten, die für das ordnungsgemäße Funktionieren des Systems behoben werden müssen (z. B. konnte keine Verbindung zur Datenbank hergestellt werden)
  • WARNUNG wird am besten für temporäre Probleme oder unerwartetes Verhalten verwendet, das die Funktion der Anwendung nicht wesentlich beeinträchtigt (z. B. fehlgeschlagene Benutzeranmeldung)
  • INFO sollte Nachrichten enthalten, die beschreiben, was in der Anwendung passiert (z. B. Benutzer registriert, Bestellung aufgegeben)
  • DEBUG ist für Meldungen gedacht, die beim Debuggen eines Problems nützlich sein könnten (z: methodenausführung gestartet)
  • TRACE ähnelt DEBUG, enthält jedoch detailliertere Ereignisse (z. B. Datenmodell aktualisiert)

Protokollebenen steuern

Die Protokollebene einer Nachricht wird beim Schreiben festgelegt:

logger.info("Order ID:" + order.getId() + " placed.");

Mit Protokollierungs-APIs können Sie normalerweise die Ebene festlegen, auf der Nachrichten angezeigt werden sollen. Wenn Sie beispielsweise die Protokollebene für die Anwendung oder bestimmte Klassen auf INFO festlegen, werden nur Nachrichten auf den Ebenen FATAL, ERROR, WARN und INFO angezeigt, während DEBUG- und TRACE-Nachrichten nicht enthalten sind.

Dies ist hilfreich, da Sie normalerweise DEBUG- oder Lower-Meldungen in der Entwicklung, aber nicht in der Produktion anzeigen würden.

So können Sie die Protokollebene für eine Klasse, ein Paket oder eine gesamte Anwendung in log4j 2 mithilfe von log4j2 festlegen.eigenschaften Datei:

loggers=classLogger,packageLoggerlogger.classLogger.name=com.stackify.service.MyServicelogger.classLogger.level=infologger.packageLogger.name=com.stackify.configlogger.packageLogger.level=debug
rootLogger.level=debug

Um die Protokollebene in der Nachricht anzuzeigen, können Sie die Option %p im log4j2PatternLayout hinzufügen.

Bevor wir fortfahren, denken Sie daran, dass Sie, wenn keine der vorhandenen Ebenen für Ihre Anwendungsanforderungen geeignet ist, auch die Möglichkeit haben, eine benutzerdefinierte Protokollebene zu definieren.

Verfolgen eines einzelnen Vorgangs über mehrere Systeme und Protokolle hinweg

In verteilten Systemen mit mehreren unabhängig voneinander bereitgestellten Diensten, die zusammenarbeiten, um eingehende Anforderungen zu verarbeiten, kann es schwierig sein, eine einzelne Anforderung über alle diese Systeme hinweg zu verfolgen.

Eine einzelne Anfrage wird sehr wahrscheinlich mehrere dieser Dienste treffen, und wenn ein Problem auftritt, müssen wir alle einzelnen Protokolle dieser Systeme bestätigen, um ein vollständiges Bild davon zu erhalten, was passiert ist.

Um diese Art von Architektur zu adressieren, haben wir jetzt eine neue Generation von Protokollierungshilfstools im Ökosystem, wie Zipkin und Spring Cloud Sleuth.

Zipkin verfolgt Anforderungen über Microservice-Architekturen hinweg, um zu ermitteln, welche Anwendung das Problem verursacht. Es kommt auch mit einer hilfreichen Benutzeroberfläche, wo Sie Spuren basierend auf Anwendung, Länge der Spur oder Zeitstempel filtern können.

Und das Spring Cloud Sleuth-Projekt fügt jedem Trace eine eindeutige 64-Bit-ID hinzu. Auf diese Weise kann die Anforderung über mehrere Dienste hinweg identifiziert werden.

Diese Tools adressieren die Einschränkungen der Kernbibliotheken und Sie navigieren durch die neuen Realitäten des verteilteren Architekturstils.

Keine Protokollierung mit JSON

Während die Protokollierung in einem Klartextformat sehr verbreitet ist, hat das Aufkommen von Protokollspeicher- und Datenanalysesystemen dies in Richtung JSON verschoben.

JSON als primäres Anwendungsprotokollformat hat den Vorteil, dass es genauso lesbar ist wie reiner Text und gleichzeitig durch automatisierte Verarbeitungstools viel einfacher zu analysieren ist.

Zum Beispiel bietet Log4j 2 das JSONLayout für genau diesen Zweck an:

<JSONLayout complete="true" compact="false"/>

Dadurch wird ein wohlgeformtes JSON-Dokument erstellt:

Als JSON werden die Protokolldaten semantisch reicher, wenn sie von einem Protokollverwaltungssystem wie Retrace verarbeitet werden – was sofort seine leistungsstarken strukturierten / semantischen Protokollierungsfunktionen ermöglicht.

Auswirkungen der Protokollierung auf die Leistung

Betrachten wir abschließend ein Problem, das beim Hinzufügen von Protokollierung zu einer Anwendung unvermeidlich ist: die Auswirkungen auf die Leistung.

Ein kleiner Leistungsabfall ist zu erwarten. Es ist jedoch wichtig, dies zu verfolgen, damit Sie es minimieren und das System nicht verlangsamen können.

Einige leistungsbezogene Aspekte, die bei der Auswahl einer Protokollierungs-API zu berücksichtigen sind, sind:

  • Datei–E / A–Vorgänge unter Verwendung eines Puffers – dies ist kritisch, da Datei–E / A eine teure Operation ist
  • Asynchrone Protokollierung – Dies sollte berücksichtigt werden, damit die Protokollierung andere Anwendungsprozesse nicht blockiert
  • Protokollierungsantwortzeit – die Zeit, die benötigt wird, um einen Protokolleintrag zu schreiben und zurückzukehren
  • Anzahl der für die Protokollierung verwendeten Threads
  • Protokollieren ebenenfilterung – Dies wird durchgeführt, um zu überprüfen, ob die einer Nachricht entsprechende Protokollebene aktiviert ist, und kann durch Durchlaufen der Hierarchie oder durch direkten Zugriff des Loggers auf die Loggerkonfiguration erfolgen; der letztere Ansatz ist in Bezug auf die Leistung vorzuziehen

Wenn Sie die Auswahl offen und das System flexibel halten müssen, können Sie natürlich immer eine Abstraktion auf höherer Ebene wie slf4j verwenden.

Bevor wir einen verschieben, finden Sie hier nur einige Schritte, mit denen Sie die Protokollierungsleistung Ihres Systems verbessern können:

  • passen Sie die Protokollebene der Anwendung für ausführliche Pakete an
  • Vermeiden Sie die Protokollierung von Quellstandortinformationen zur Laufzeit, da das Nachschlagen des aktuellen Threads, der Datei, einer Methode eine kostspielige Operation ist
  • Vermeiden Sie Protokollierungsfehler mit langen Stack–Traces
  • Überprüfen Sie, ob eine bestimmte Protokollebene aktiviert ist, bevor Sie eine Nachricht mit dieser Ebene schreiben – auf diese Weise wird die Nachricht nicht erstellt, wenn sie nicht benötigt wird
  • Überprüfen Sie die Protokolle, bevor Sie zur Produktion übergehen, um zu überprüfen, ob Protokollierungen entfernt werden können

Lobende Erwähnungen

Bevor wir einpacken, lassen Sie uns schauen Sie sich eine letzte Übung an, die Sie vermeiden sollten – und zwar die Verwendung von Standard out anstelle der Protokollierung.

Während System.out() kann ein schneller Weg sein, um sehr früh im Entwicklungszyklus zu beginnen, es ist definitiv keine gute Praxis, nach diesem Punkt zu folgen.

Neben der Tatsache, dass Sie alle leistungsstarken Funktionen einer dedizierten Protokollierungs-API verlieren, ist dieser primäre Nachteil hier einfach die Tatsache, dass die Protokollierungsdaten nirgendwo gespeichert werden.

Schließlich ist eine weitere lobende Erwähnung eine Praxis, die das Lesen und Analysieren von Protokolldaten erheblich erleichtern kann – standardisierte Protokollnachrichten. Einfach ausgedrückt sollten ähnliche Ereignisse ähnliche Meldungen im Protokoll enthalten.

Wenn Sie nach allen Instanzen dieses bestimmten Ereignisses suchen oder aussagekräftige Erkenntnisse aus Ihren Protokolldaten extrahieren müssen, sind Standardprotokollmeldungen sehr wichtig.

Wenn beispielsweise ein Upload-Vorgang fehlschlägt– wäre es verwirrend, diese verschiedenen Meldungen im Protokoll zu haben:

Could not upload file picture.jpg
File upload picture.jpg failed.

Wenn der Datei-Upload fehlschlägt, sollten Sie stattdessen konsequent eine dieser Meldungen verwenden, um den Fehler zu protokollieren.

Fazit

Die Verwendung der Protokollierung ist in der Anwendungsentwicklung aufgrund der äußerst nützlichen und umsetzbaren Erkenntnisse, die sie in die Laufzeit des Systems bringt, allgegenwärtig geworden.

Um jedoch das Beste aus Ihren Protokolldaten herauszuholen, ist es wichtig, über die Grundlagen hinauszugehen, eine Protokollierungskultur zu entwickeln und die Feinheiten des Betriebs mit diesen Daten in großem Maßstab und in der Produktion zu verstehen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.