10 Tipps für die richtige Anwendungsprotokollierung

Unser neuester JCP-Partner, Tomasz Nurkiewicz, hat eine Reihe von Beiträgen eingereicht, in denen die Grundprinzipien der richtigen Anwendungsprotokollierung beschrieben werden. Ich fand sie sehr interessant, daher habe ich beschlossen, sie in einem kompakteren Format zusammenzufassen und Ihnen vorzustellen. Also, hier sind seine Vorschläge für saubere und hilfreiche Protokolle : (HINWEIS: Die ursprünglichen Beiträge wurden leicht bearbeitet, um die Lesbarkeit zu verbessern)

1) Verwenden Sie die entsprechenden Tools für den Job

Viele Programmierer scheinen zu vergessen, wie wichtig das Protokollieren des Verhaltens einer Anwendung und ihrer aktuellen Aktivität ist. Wenn jemand legt:

log.info("Happy and carefree logging");

glücklicherweise erkennt er irgendwo im Code wahrscheinlich nicht, wie wichtig Anwendungsprotokolle während der Wartung, Optimierung und Fehlererkennung sind. Den Wert guter Protokolle zu unterschätzen, ist ein schrecklicher Fehler.

Meiner Meinung nach ist SLF4J die beste verfügbare Protokollierungs-API, hauptsächlich aufgrund einer großartigen Unterstützung für die Musterersetzung:

log.debug("Found {} records matching filter: '{}'", records, filter); 

In Log4j müssten Sie verwenden:

log.debug("Found " + records + " records matching filter: '" + filter + "'");

Dies ist nicht nur länger und weniger lesbar, sondern auch ineffizient wegen der umfangreichen Verwendung von String-Verkettung. SLF4J fügt eine nette {} Substitutionsfunktion hinzu. Da die Verkettung von Zeichenfolgen vermieden wird und toString() nicht aufgerufen wird, wenn die Protokollierungsanweisung gefiltert wird, ist isDebugEnabled() nicht mehr erforderlich. Übrigens, haben Sie einfache Anführungszeichen um den String-Parameter bemerkt?

SLF4J ist nur eine Fassade. Als Implementierung würde ich das bereits beworbene Logback-Framework anstelle des etablierten Log4J empfehlen. Es hat viele interessante Funktionen und wird im Gegensatz zu Log4J aktiv entwickelt.

Das letzte zu empfehlende Tool ist Perf4J. Um ihr Motto zu zitieren:

Perf4J is to System.currentTimeMillis() als log4j ist zum System.aus.println()

Ich habe Perf4J zu einer vorhandenen Anwendung unter starker Last hinzugefügt und es in einigen anderen in Aktion gesehen. Sowohl Administratoren als auch Geschäftsanwender waren von den schönen Grafiken dieses einfachen Dienstprogramms beeindruckt. Außerdem konnten wir in kürzester Zeit Leistungsmängel entdecken. Perf4J selbst verdient einen eigenen Artikel, aber im Moment schauen Sie sich einfach das Entwicklerhandbuch an.

Beachten Sie außerdem, dass Ceki Gülcü (Gründer der Projekte Log4J, SLF4J und Logback) einen einfachen Ansatz vorgeschlagen hat, um die Commons-Logging-Abhängigkeit zu beseitigen (siehe seinen Kommentar).

2) Vergessen Sie nicht, Protokollierungsstufen sind für Sie da

Jedes Mal, wenn Sie eine Protokollierungsanweisung abgeben, überlegen Sie genau, welche Protokollierungsstufe für diese Art von Ereignis geeignet ist, nicht wahr? Irgendwie achten 90% der Programmierer nie auf Protokollierungsebenen, sondern protokollieren einfach alles auf derselben Ebene, normalerweise INFO oder DEBUG. Warum? Logging-Frameworks haben zwei große Vorteile gegenüber dem System.aus., d. h. Kategorien und Ebenen. Mit beiden können Sie Protokollierungsanweisungen selektiv dauerhaft oder nur für kurze Zeit filtern. Wenn Sie den Unterschied wirklich nicht sehen können, drucken Sie diese Tabelle aus und sehen Sie sie sich jedes Mal an, wenn Sie mit der Eingabe von „log.“ in Ihrer IDE:

FEHLER – etwas furchtbar Falsches war passiert, das sofort untersucht werden muss. Kein System kann Elemente tolerieren, die auf dieser Ebene protokolliert werden. Beispiel: NPE, Datenbank nicht verfügbar, geschäftskritischer Anwendungsfall kann nicht fortgesetzt werden.

WARN – Der Vorgang kann fortgesetzt werden, aber seien Sie besonders vorsichtig. Eigentlich wollte ich hier immer zwei Ebenen haben: eine für offensichtliche Probleme, bei denen es Workarounds gibt (zum Beispiel: „Aktuelle Daten nicht verfügbar, mit zwischengespeicherten Werten“) und zweitens (Name it: ACHTUNG) für mögliche Probleme und Vorschläge. Beispiel: „Anwendung wird im Entwicklungsmodus ausgeführt“ oder „Administrationskonsole ist nicht mit einem Kennwort gesichert“. Die Anwendung kann Warnmeldungen tolerieren, diese sollten jedoch immer begründet und geprüft werden.

INFO – Wichtiger Geschäftsprozess ist beendet. In einer idealen Welt sollten Administratoren oder fortgeschrittene Benutzer in der Lage sein, INFO-Nachrichten zu verstehen und schnell herauszufinden, was die Anwendung tut. Wenn es in einer Anwendung beispielsweise nur um die Buchung von Flugtickets geht, sollte pro Ticket nur eine INFO-Anweisung mit der Aufschrift “ gebuchtes Ticket von bis “ vorhanden sein. Andere Definition von INFO-Nachricht: Jede Aktion, die den Status der Anwendung erheblich ändert (Datenbankaktualisierung, externe Systemanforderung).

DEBUG – Entwickler Zeug. Ich werde später besprechen, welche Art von Informationen es verdient, protokolliert zu werden.

TRACE – Sehr detaillierte Informationen, die nur für die Entwicklung bestimmt sind. Sie können Ablaufverfolgungsnachrichten nach der Bereitstellung in der Produktionsumgebung für kurze Zeit beibehalten, diese Protokollanweisungen jedoch als temporär behandeln, die möglicherweise deaktiviert werden sollten oder könnten. Die Unterscheidung zwischen DEBUG und TRACE ist am schwierigsten, aber wenn Sie eine Anweisung setzen und entfernen, nachdem das Feature entwickelt und getestet wurde, sollte es wahrscheinlich auf TRACE-Ebene sein.

Die obige Liste ist nur ein Vorschlag, Sie können Ihre eigenen Anweisungen erstellen, aber es ist wichtig, einige zu haben. Meine Erfahrung ist, dass immer alles ohne Filterung protokolliert wird (zumindest aus dem Anwendungscode), aber die Möglichkeit, Protokolle schnell zu filtern und die Informationen mit der richtigen Detailstufe zu extrahieren, könnte ein Lebensretter sein.

Das letzte, was erwähnenswert ist, ist die berüchtigte is*Enabled() Bedingung. Einige setzen es vor jede Protokollierungsanweisung:

if(log.isDebugEnabled()) log.debug("Place for your commercial");

Persönlich finde ich diese Redewendung nur Unordnung, die vermieden werden sollte. Die Leistungsverbesserung (insbesondere bei Verwendung der zuvor diskutierten SLF4J-Mustersubstitution) scheint irrelevant zu sein und riecht nach einer vorzeitigen Optimierung. Können Sie auch die Duplizierung erkennen? Es gibt sehr seltene Fälle, in denen eine explizite Bedingung gerechtfertigt ist – wenn wir nachweisen können, dass das Erstellen einer Protokollierungsnachricht teuer ist. In anderen Situationen erledigen Sie einfach die Protokollierung und lassen das Protokollierungsframework seine Arbeit erledigen (Filtern).

3) Wissen Sie, was Sie protokollieren?

Nehmen Sie sich jedes Mal, wenn Sie eine Protokollierungsanweisung ausgeben, einen Moment Zeit und sehen Sie sich an, was genau in Ihrer Protokolldatei landet. Lesen Sie anschließend Ihre Protokolle und erkennen Sie fehlerhafte Sätze. Vermeiden Sie zunächst solche NPEs:

log.debug("Processing request with id: {}", request.getId());

Sind Sie absolut sicher, dass die Anfrage hier nicht null ist?

Eine weitere Falle ist das Protokollieren von Sammlungen. Wenn Sie mit Hibernate eine Sammlung von Domänenobjekten aus der Datenbank abgerufen und diese wie hier achtlos protokolliert haben:

log.debug("Returning users: {}", users);

SLF4J ruft toString() nur auf, wenn die Anweisung tatsächlich gedruckt wird, was ziemlich nett ist. Aber wenn es tut … Aus Speicherfehler, N + 1 select Problem, Thread Hunger (Protokollierung ist synchron!), Lazy Initialization Exception, logs Speicher vollständig gefüllt – jeder von diesen könnte auftreten.

Es ist eine viel bessere Idee, beispielsweise nur IDs von Domänenobjekten (oder sogar nur die Größe der Sammlung) zu protokollieren. Das Erstellen einer Sammlung von IDs bei einer Sammlung von Objekten mit der getId() -Methode ist in Java jedoch unglaublich schwierig und umständlich. Groovy hat einen großartigen Spread-Operator (users*.id ), in Java können wir ihn mit der Commons Beanutils Bibliothek emulieren:

log.debug("Returning user ids: {}", collect(users, "id"));

Wobei die collect() -Methode wie folgt implementiert werden kann:

public static Collection collect(Collection collection, String propertyName) { return CollectionUtils.collect(collection, new BeanToPropertyValueTransformer(propertyName));}

Das letzte, was zu erwähnen ist, ist die unsachgemäße Implementierung oder Verwendung von toString() . Erstellen Sie zunächst toString() für jede Klasse, die irgendwo in Protokollierungsanweisungen angezeigt wird, vorzugsweise mit ToStringBuilder (aber nicht mit seinem reflektierenden Gegenstück). Zweitens, achten Sie auf Arrays und untypische Sammlungen. Arrays und einige seltsame Sammlungen haben möglicherweise nicht toString() implementiert, das toString() jedes Elements aufruft. Verwenden Sie die JDK-Dienstprogrammmethode #deepToString . Und lesen Sie Ihre Protokolle häufig, um falsch formatierte Nachrichten zu erkennen.

4) Nebenwirkungen vermeiden

Protokollierungsanweisungen sollten keine oder nur geringe Auswirkungen auf das Verhalten der Anwendung haben. Kürzlich hat ein Freund von mir ein Beispiel für ein System gegeben, das die LazyInitializationException von Hibernates nur ausgelöst hat, wenn es in einer bestimmten Umgebung ausgeführt wird. Wie Sie wahrscheinlich aus dem Kontext erraten haben, hat eine Protokollierungsanweisung dazu geführt, dass die initialisierte Sammlung geladen wurde, als die Sitzung angehängt wurde. In dieser Umgebung wurden die Protokollierungsstufen erhöht und die Sammlung nicht mehr initialisiert. Überlegen Sie, wie lange es dauern würde, einen Fehler zu finden, ohne diesen Kontext zu kennen?

Ein weiterer Nebeneffekt verlangsamt die Anwendung. Schnelle Antwort: Wenn Sie zu viel protokollieren oder die Verkettung von toString () und / oder Zeichenfolgen nicht ordnungsgemäß verwenden, hat die Protokollierung einen Leistungsnebeneffekt. Wie groß? Nun, ich habe gesehen, dass der Server alle 15 Minuten neu gestartet wurde, weil ein Thread-Hunger durch übermäßige Protokollierung verursacht wurde. Nun, das ist ein Nebeneffekt! Aus meiner Erfahrung sind einige hundert MiB wahrscheinlich die Obergrenze dafür, wie viel Sie sich pro Stunde auf der Festplatte anmelden können.

Wenn die Protokollierungsanweisung selbst fehlschlägt und der Geschäftsprozess aufgrund einer Ausnahme beendet wird, ist dies natürlich auch ein großer Nebeneffekt. Ich habe ein solches Konstrukt gesehen, um dies zu vermeiden:

try { log.trace("Id=" + request.getUser().getId() + " accesses " + manager.getPage().getUrl().toString())} catch(NullPointerException e) {}

Dies ist ein echter Code, aber bitte machen Sie die Welt ein bisschen besser und tun Sie es niemals.

5) Seien Sie prägnant und beschreibend

Jede Protokollierungsanweisung sollte sowohl Daten als auch Beschreibung enthalten. Betrachten Sie die folgenden Beispiele:

log.debug("Message processed");log.debug(message.getJMSMessageID());log.debug("Message with id '{}' processed", message.getJMSMessageID());

Welches Protokoll möchten Sie bei der Fehlerdiagnose in einer unbekannten Anwendung sehen? Glauben Sie mir, alle obigen Beispiele sind fast gleich häufig. Ein weiteres Anti-Muster:

if(message instanceof TextMessage) //...else log.warn("Unknown message type");

War es so schwer, den tatsächlichen Nachrichtentyp, die Nachrichten-ID usw. anzugeben. in der Warnzeichenfolge? Ich weiß, dass etwas schief gelaufen ist, aber was? Was war der Kontext?

Ein drittes Anti-Pattern ist das „Magic-log“. Beispiel aus der Praxis: Die meisten Programmierer im Team wussten, dass 3 kaufmännische und gefolgt von einem Ausrufezeichen, gefolgt von einem Hash, gefolgt von einer pseudozufälligen alphanumerischen Zeichenfolge „Nachricht mit XYZ-ID empfangen“ bedeutet. Niemand die Mühe, das Protokoll zu ändern, einfach jemand auf die Tastatur und wählte einige einzigartige „&&&!#“ String, so dass es leicht von sich selbst gefunden werden kann.

Infolgedessen sieht die gesamte Protokolldatei wie eine zufällige Zeichenfolge aus. Jemand könnte diese Datei sogar als gültiges Perl-Programm betrachten. Stattdessen sollte eine Protokolldatei lesbar, sauber und beschreibend sein. Verwenden Sie keine magischen Zahlen, Protokollwerte, Zahlen oder IDs und schließen Sie deren Kontext ein. Zeigen Sie die verarbeiteten Daten und deren Bedeutung an. Zeigen Sie, was das Programm tatsächlich tut. Gute Protokolle können als großartige Dokumentation des Anwendungscodes selbst dienen.

Habe ich erwähnt, dass ich keine Passwörter und keine persönlichen Informationen protokollieren soll? Nicht!

6) Tune your pattern

Logging Pattern ist ein wunderbares Werkzeug, das jeder Logging-Anweisung, die Sie machen, transparent einen aussagekräftigen Kontext hinzufügt. Sie müssen jedoch sehr sorgfältig überlegen, welche Informationen in Ihr Muster aufgenommen werden sollen. Beispielsweise ist das Protokollierungsdatum, wenn Ihre Protokolle stündlich gerollt werden, sinnlos, da das Datum bereits im Namen der Protokolldatei enthalten ist. Im Gegenteil, ohne den Threadnamen zu protokollieren, können Sie keinen Prozess mithilfe von Protokollen verfolgen, wenn zwei Threads gleichzeitig arbeiten – die Protokolle überlappen sich. Dies könnte in Single-Threaded-Anwendungen in Ordnung sein – die heutzutage fast tot sind.

Aus meiner Erfahrung sollte das ideale Protokollierungsmuster Folgendes enthalten (natürlich mit Ausnahme der protokollierten Nachricht selbst): aktuelle Uhrzeit (ohne Datum, Millisekundengenauigkeit), Protokollierungsstufe, Name des Threads, einfacher Loggername (nicht vollständig qualifiziert) und die Nachricht. In Logback ist es so etwas wie:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} %-5level %m%n</pattern> </encoder></appender>

Sie sollten niemals Dateinamen, Klassennamen und Zeilennummern angeben, obwohl dies sehr verlockend ist. Ich habe sogar leere Protokollanweisungen aus dem Code gesehen:

log.info("");

da der Programmierer davon ausgegangen ist, dass die Zeilennummer Teil des Protokollierungsmusters ist, wusste er, dass „Wenn in der 67. Zeile der Datei eine leere Protokollierungsmeldung angezeigt wird (in der authenticate () -Methode), bedeutet dies, dass der Benutzer authentifiziert ist“. Außerdem hat das Protokollieren des Klassennamens, des Methodennamens und / oder der Zeilennummer schwerwiegende Auswirkungen auf die Leistung.

Ein etwas fortgeschritteneres Merkmal eines Protokollierungsframeworks ist das Konzept des zugeordneten Diagnosekontexts. MDC ist einfach eine Karte, die thread-lokal verwaltet wird. Sie können ein beliebiges Schlüssel-Wert-Paar in diese Zuordnung einfügen, und seitdem wird jeder von diesem Thread ausgegebenen Protokollierungsanweisung dieser Wert als Teil des Musters angehängt.

7) Log Methodenargumente und Rückgabewerte

Wenn Sie während der Entwicklung einen Fehler finden, führen Sie normalerweise einen Debugger aus, um die mögliche Ursache aufzuspüren. Stellen Sie sich nun eine Weile vor, Sie können keinen Debugger verwenden. Zum Beispiel, weil sich der Fehler vor einigen Tagen in einer Kundenumgebung manifestiert hat und alles, was Sie haben, Protokolle sind. Würden Sie in der Lage sein, etwas in ihnen zu finden?

Wenn Sie die einfache Regel befolgen, jede Methodeneingabe und -ausgabe (Argumente und Rückgabewerte) zu protokollieren, benötigen Sie nicht einmal mehr einen Debugger. Natürlich müssen Sie vernünftig sein, aber jede Methode, die: auf externes System (einschließlich Datenbank) zugreift, blockiert, wartet usw. sollte berücksichtigt werden. Folgen Sie einfach diesem Muster:

public String printDocument(Document doc, Mode mode) { log.debug("Entering printDocument(doc={}, mode={})", doc, mode); String id = //Lengthy printing operation log.debug("Leaving printDocument(): {}", id); return id;}

Da Sie sowohl den Anfang als auch das Ende des Methodenaufrufs protokollieren, können Sie ineffizienten Code manuell erkennen und sogar mögliche Ursachen für Deadlocks und Hunger erkennen – indem Sie einfach nach „Eingabe“ suchen, ohne „Verlassen“ zu entsprechen. Wenn Ihre Methoden aussagekräftige Namen haben, ist das Lesen von Protokollen ein Vergnügen. Außerdem ist es viel einfacher zu analysieren, was schief gelaufen ist, da Sie bei jedem Schritt genau wissen, was verarbeitet wurde. Sie können sogar einen einfachen AOP-Aspekt verwenden, um eine Vielzahl von Methoden in Ihrem Code zu protokollieren. Dies reduziert die Code-Duplizierung, aber seien Sie vorsichtig, da dies zu einer enormen Anzahl großer Protokolle führen kann.

Sie sollten DEBUG- oder TRACE-Ebenen als für diese Protokolltypen am besten geeignet betrachten. Und wenn Sie feststellen, dass einige Methoden zu oft aufgerufen werden und die Protokollierung die Leistung beeinträchtigen kann, verringern Sie einfach die Protokollierungsstufe für diese Klasse oder entfernen Sie das Protokoll vollständig (möglicherweise nur eine für den gesamten Methodenaufruf?), aber es ist immer besser, zu viele als zu wenige Protokollierungsanweisungen zu haben. Behandeln Sie Protokollierungsanweisungen mit dem gleichen Respekt wie Komponententests – Ihr Code sollte mit Protokollierungsroutinen wie mit Komponententests behandelt werden. Kein Teil des Systems sollte ohne Protokolle bleiben. Denken Sie daran, dass das Beobachten von vorbeilaufenden Protokollen manchmal die einzige Möglichkeit ist, festzustellen, ob Ihre Anwendung ordnungsgemäß funktioniert oder für immer hängt.

8) Achten Sie auf externe Systeme

Dies ist der Sonderfall des vorherigen Tipps: Wenn Sie mit einem externen System kommunizieren, sollten Sie alle Daten protokollieren, die aus Ihrer Anwendung kommen und eingehen. Zeitraum. Integration ist eine schwierige Aufgabe und die Diagnose von Problemen zwischen zwei Anwendungen (denken Sie an zwei verschiedene Anbieter, Umgebungen, Technologie-Stacks und Teams) ist besonders schwierig. Kürzlich haben wir beispielsweise festgestellt, dass das Protokollieren des vollständigen Nachrichteninhalts, einschließlich SOAP- und HTTP-Headern in Apache CXF-Webdiensten, während der Integration und Systemtests äußerst nützlich ist.

Dies ist ein großer Overhead, und wenn die Leistung ein Problem darstellt, können Sie die Protokollierung jederzeit deaktivieren. Aber was bringt es, eine schnelle, aber kaputte Anwendung zu haben, die niemand reparieren kann? Seien Sie besonders vorsichtig bei der Integration in externe Systeme und bereiten Sie sich darauf vor, diese Kosten zu tragen. Wenn Sie Glück haben und Ihre gesamte Integration von einem ESB abgewickelt wird, ist der Bus wahrscheinlich der beste Ort, um jede eingehende Anfrage und Antwort zu protokollieren. Siehe zum Beispiel die Log-Komponente von Mules.

Manchmal ist es aufgrund der Menge an Informationen, die mit externen Systemen ausgetauscht werden, nicht akzeptabel, alles zu protokollieren. Auf der anderen Seite möchten wir während des Tests und für kurze Zeit in der Produktion (zum Beispiel wenn etwas nicht stimmt) alles in Protokollen speichern und sind bereit, die Leistungskosten zu tragen. Dies kann durch sorgfältige Verwendung von Protokollierungsebenen erreicht werden. Schauen Sie sich einfach das folgende Idiom an:

Collection<Integer> requestIds = //...if(log.isDebugEnabled()) log.debug("Processing ids: {}", requestIds);else log.info("Processing ids size: {}", requestIds.size());

Wenn dieser spezielle Logger zum Protokollieren von DEBUG-Nachrichten konfiguriert ist, wird der gesamte Inhalt der requestIds-Sammlung gedruckt. Wenn es jedoch zum Drucken von Infonachrichten konfiguriert ist, wird nur die Größe der Sammlung ausgegeben. Wenn Sie sich fragen, warum ich die Bedingung isInfoEnabled () vergessen habe, kehren Sie zu Tipp 2 zurück. Erwähnenswert ist, dass die requestIds-Auflistung in diesem Fall nicht null sein sollte. Es wird zwar korrekt als null protokolliert, wenn DEBUG aktiviert ist, aber eine große, fette NullPointerException wird ausgelöst, wenn der Logger für INFO konfiguriert ist. Erinnern Sie sich an meine Lektion über Nebenwirkungen in Tipp # 4?

9) Ausnahmen ordnungsgemäß protokollieren

Vermeiden Sie zunächst die Protokollierung von Ausnahmen. Es gibt eine, ekhem, Ausnahme von dieser Regel: wenn Sie Ausnahmen von einem Remote-Dienst (RMI, EJB Remote Session Bean usw.) auslösen.), das Ausnahmen serialisieren kann, stellen Sie sicher, dass alle für den Client verfügbar sind (Teil der API sind). Andernfalls erhält der Client NoClassDefFoundError: SomeFancyException anstelle des Fehlers „true“.

Das Protokollieren von Ausnahmen ist eine der wichtigsten Rollen der Protokollierung überhaupt, aber viele Programmierer neigen dazu, das Protokollieren als eine Möglichkeit zu behandeln, die Ausnahme zu behandeln. Sie geben manchmal den Standardwert zurück (normalerweise null, 0 oder leere Zeichenfolge) und geben vor, dass nichts passiert ist. In anderen Fällen protokollieren sie zuerst die Ausnahme und wickeln sie dann ein und werfen sie zurück:

log.error("IO exception", e);throw new MyCustomException(e);

Dieses Konstrukt druckt fast immer denselben Stack-Trace zweimal, da MyCustomException schließlich MyCustomException und seine Ursache MyCustomException . Protokollieren oder wickeln und zurückwerfen (was vorzuziehen ist), niemals beides, da sonst Ihre Protokolle verwirrend sind.

Aber wenn wir die Ausnahme wirklich protokollieren wollen? Aus irgendeinem Grund (weil wir APIs und Dokumentation nicht lesen?), etwa die Hälfte der Protokollierungsanweisungen, die ich sehe, sind falsch. Schnelles Quiz, welche der folgenden Protokollanweisungen protokolliert die NPE ordnungsgemäß?

try { Integer x = null; ++x;} catch (Exception e) { log.error(e); //A log.error(e, e); //B log.error("" + e); //C log.error(e.toString()); //D log.error(e.getMessage()); //E log.error(null, e); //F log.error("", e); //G log.error("{}", e); //H log.error("{}", e.getMessage()); //I log.error("Error reading configuration file: " + e); //J log.error("Error reading configuration file: " + e.getMessage()); //K log.error("Error reading configuration file", e); //L}

Überraschenderweise sind nur G und vorzugsweise L korrekt! A und B kompilieren nicht einmal in SLF4J, andere verwerfen Stack-Traces und / oder drucken falsche Nachrichten. Beispielsweise druckt E nichts, da NPE normalerweise keine Ausnahmemeldung bereitstellt und der Stack-Trace ebenfalls nicht gedruckt wird. Denken Sie daran, das erste Argument ist immer die Textnachricht, schreiben Sie etwas über die Art des Problems. Fügen Sie keine Ausnahmemeldung hinzu, da diese automatisch nach der Log-Anweisung vor dem Stack-Trace gedruckt wird. Dazu müssen Sie jedoch die Ausnahme selbst als zweites Argument übergeben.

10) Protokolle leicht zu lesen, leicht zu analysieren

Es gibt zwei Gruppen von Empfängern, die besonders an Ihren Anwendungsprotokollen interessiert sind: Menschen (Sie mögen anderer Meinung sein, aber Programmierer gehören ebenfalls zu dieser Gruppe) und Computer (normalerweise Shell-Skripte, die von Systemadministratoren geschrieben wurden). Protokolle sollten für beide Gruppen geeignet sein. Wenn jemand, der hinter Ihrem Rücken auf Ihre Anwendungsprotokolle schaut, sieht (Quelle Wikipedia):

dann hast du meine Tipps wahrscheinlich nicht befolgt. Protokolle sollten lesbar und leicht verständlich sein, genau wie der Code.

Auf der anderen Seite, wenn Ihre Anwendung eine halbe GB Protokolle pro Stunde erzeugt, wird kein Mensch und kein grafischer Texteditor es jemals schaffen, sie vollständig zu lesen. Hier sind Grep, sed und awk der alten Schule nützlich. Wenn es möglich ist, versuchen Sie, Protokollnachrichten so zu schreiben, dass sie sowohl von Menschen als auch von Computern verstanden werden können, z. vermeiden Sie die Formatierung von Zahlen, verwenden Sie Muster, die durch reguläre Ausdrücke leicht erkannt werden können usw. Wenn dies nicht möglich ist, drucken Sie die Daten in zwei Formaten:

log.debug("Request TTL set to: {} ({})", new Date(ttl), ttl);// Request TTL set to: Wed Apr 28 20:14:12 CEST 2010 (1272478452437)final String duration = DurationFormatUtils.formatDurationWords(durationMillis, true, true);log.info("Importing took: {}ms ({})", durationMillis, duration);//Importing took: 123456789ms (1 day 10 hours 17 minutes 36 seconds)

Computer werden das Zeitformat „ms nach 1970 Epoche“ zu schätzen wissen, während sich die Leute über den Text „1 Tag 10 Stunden 17 Minuten 36 Sekunden“ freuen würden. Übrigens werfen Sie einen Blick auf DurationFormatUtils, nettes Tool.

That’s all guys, eine „Logging tips Extravaganza“ von unserem JCP-Partner Tomasz Nurkiewicz. Vergiss nicht zu teilen!

In Verbindung stehende Artikel :
  • Logging Antipatterns
  • Dinge, die jeder Programmierer wissen sollte
  • Gesetze des Software–Designs
  • Java Best Practices – Vektor vs ArrayList vs HashSet
  • Java Best Practices – DateFormat in einer Multithreading–Umgebung
  • Java Best Practices – High Performance Serialisierung
  • Java Best Practices – String-Performance und exakter String-Matching
Verwandte Snippets :
  • Benutzerdefinierten Formatierer für Logger–Handler erstellen
  • Aufruf der Protokollmethode
  • Filter für Logger-Handler festlegen
  • Formatierer für Logger-Handler festlegen
  • Protokollierungsaspekt Beispiel
  • Log4j Maven Beispiel
  • Hibernate-Protokollierung Konfiguration – SLF4J + Log4j und Logback