it-swarm.com.de

Vorteile der strukturierten Protokollierung gegenüber der grundlegenden Protokollierung

Wir erstellen eine neue App und möchten die strukturierte Protokollierung einbeziehen. Mein ideales Setup wäre so etwas wie Serilog für unseren C # -Code und Bunyan für unseren JS. Diese würden in fluentd einfließen und könnten dann zu einer beliebigen Anzahl von Dingen gehen, dachte ich anfangs elasticsearch + kibana. Wir haben bereits eine MySQL-Datenbank, daher bin ich kurzfristig mehr daran interessiert, Serilog + Bunyan-Setup und die Entwickler dazu zu bringen, sie zu verwenden, und wir können uns bei MySQL anmelden, während wir uns etwas mehr Zeit nehmen, um fließend und den Rest einzubringen.

Einer unserer erfahreneren Programmierer würde es jedoch vorziehen, einfach Folgendes zu tun: log.debug("Disk quota {0} exceeded by user {1}", quota, user); mit log4net Und dann einfach select-Anweisungen gegen MySQL auszuführen, wie: SELECT text FROM logs WHERE text LIKE "Disk quota";

Abgesehen davon, welcher Ansatz ist besser und/oder welche Dinge müssen wir bei der Auswahl des Typs des Protokollierungssystems berücksichtigen?

119
DTI-Matt

Es gibt zwei grundlegende Fortschritte beim Ansatz strukturiert, die mit Textprotokollen nicht ohne (manchmal extremen) zusätzlichen Aufwand emuliert werden können.

Ereignistypen

Wenn Sie zwei Ereignisse mit log4net schreiben, wie:

log.Debug("Disk quota {0} exceeded by user {1}", 100, "DTI-Matt");
log.Debug("Disk quota {0} exceeded by user {1}", 150, "nblumhardt");

Diese erzeugen einen ähnlichen Text:

Disk quota 100 exceeded by user DTI-Matt
Disk quota 150 exceeded by user nblumhardt

Bei der maschinellen Verarbeitung handelt es sich jedoch nur um zwei Zeilen unterschiedlichen Textes.

Möglicherweise möchten Sie alle Ereignisse "Datenträgerkontingent überschritten" finden, aber der vereinfachte Fall der Suche nach Ereignissen like 'Disk quota%' Wird herunterfallen, sobald ein anderes Ereignis wie folgt auftritt:

Disk quota 100 set for user DTI-Matt

Die Textprotokollierung wirft die Informationen weg, die wir ursprünglich über die Quelle des Ereignisses haben, und diese müssen rekonstruiert werden, wenn die Protokolle normalerweise mit immer ausgefeilteren Übereinstimmungsausdrücken gelesen werden.

Wenn Sie dagegen die folgenden zwei Serilog-Ereignisse schreiben:

log.Debug("Disk quota {Quota} exceeded by user {Username}", 100, "DTI-Matt");
log.Debug("Disk quota {Quota} exceeded by user {Username}", 150, "nblumhardt");

Diese erzeugen eine ähnliche Textausgabe wie die log4net-Version, aber hinter den Kulissen wird das "Disk quota {Quota} exceeded by user {Username}"Nachrichtenvorlage von beiden Ereignissen übertragen.

Mit einer geeigneten Senke können Sie später Abfragen schreiben where MessageTemplate = 'Disk quota {Quota} exceeded by user {Username}' Und genau die Ereignisse abrufen, bei denen das Festplattenkontingent überschritten wurde.

Es ist nicht immer bequem, die gesamte Nachrichtenvorlage bei jedem Protokollereignis zu speichern, daher haben einige Senken die Nachrichtenvorlage in einen numerischen EventType -Wert (z. B. 0x1234abcd), Oder Sie können einen Enricher hinzufügen die Protokollierungspipeline zu mach das selbst .

Es ist subtiler als der nächste Unterschied unten, aber ein enorm leistungsfähiger, wenn es um große Protokollvolumina geht.

Strukturierte Daten

Auch hier kann es unter Berücksichtigung der beiden Ereignisse zur Speicherplatznutzung einfach genug sein, mithilfe von Textprotokollen einen bestimmten Benutzer mit like 'Disk quota' and like 'DTI-Matt' Abzufragen.

Die Produktionsdiagnose ist jedoch nicht immer so einfach. Stellen Sie sich vor, Sie müssen Ereignisse finden, bei denen das überschrittene Festplattenkontingent unter 125 MB lag.

Mit Serilog ist dies in den meisten Spülen mit einer Variante von:

Quota < 125

Das Konstruieren dieser Art von Abfrage aus einem regulären Ausdruck ist möglich, aber es wird schnell anstrengend und ist normalerweise ein Maß für den letzten Ausweg.

Fügen Sie nun einen Ereignistyp hinzu:

Quota < 125 and EventType = 0x1234abcd

Hier sehen Sie, wie diese Funktionen auf einfache Weise kombiniert werden, damit sich das Debuggen in der Produktion mit Protokollen wie eine erstklassige Entwicklungsaktivität anfühlt.

Ein weiterer Vorteil, der vielleicht nicht so einfach zu verhindern ist, aber sobald das Produktions-Debugging aus dem Land der Regex-Hackerei entfernt wurde, beginnen Entwickler, Protokolle viel mehr zu schätzen und beim Schreiben mehr Sorgfalt und Rücksichtnahme zu üben. Bessere Protokolle -> qualitativ bessere Anwendungen -> rundum mehr Glück.

149

Wenn Sie Protokolle für die Verarbeitung sammeln, sei es zum Parsen in einer Datenbank und/oder zum späteren Durchsuchen der verarbeiteten Protokolle, macht die Verwendung von strukturierter Protokollierung einen Teil der Verarbeitung einfacher/effizienter. Der Parser kann die bekannte Struktur (, z. B. JSON, XML, ASN.1 usw.) nutzen und Zustandsmaschinen zum Parsen verwenden, im Gegensatz zu regulären Ausdrücken (die rechnerisch sein können) teuer (relativ) zu kompilieren und auszuführen). Das Parsen von Freiformtext, wie er von Ihrem Kollegen vorgeschlagen wurde, basiert in der Regel auf regulären Ausdrücken, nd auf dem Text , der sich nicht ändert . Dies kann das Parsen von Freiformtext ziemlich fragil machen (, d. H. Das Parsen ist eng an den genauen Text im Code gekoppelt).

Betrachten Sie auch den Such-/Suchfall , z. B. :

SELECT text FROM logs WHERE text LIKE "Disk quota";

LIKE Bedingungen erfordern Vergleiche mit jedem text Zeilenwert; Auch dies ist relativ rechenintensiv, insbesondere , wenn Platzhalter verwendet werden:

SELECT text FROM logs WHERE text LIKE "Disk %";

Bei der strukturierten Protokollierung sieht Ihre festplattenfehlerbezogene Protokollmeldung in JSON möglicherweise folgendermaßen aus:

{ "level": "DEBUG", "user": "username", "error_type": "disk", "text": "Disk quota ... exceeded by user ..." }

Die Felder dieser Art von Struktur können ziemlich einfach zugeordnet werden, z. B. Spaltennamen von SQL-Tabellen, was wiederum bedeutet, dass die Suche spezifischer/detaillierter sein kann:

SELECT user, text FROM logs WHERE error_type = "disk";

Sie können Indizes für die Spalten platzieren, deren Werte Sie häufig suchen/suchen möchten , solange Sie keine LIKE -Klauseln für diese Spaltenwerte verwenden. Je mehr Sie Ihre Protokollnachricht in bestimmte Kategorien unterteilen können, desto gezielter können Sie nachschlagen. Zusätzlich zu dem Feld/der Spalte error_type Im obigen Beispiel können Sie beispielsweise auch "error_category": "disk", "error_type": "quota" Oder ähnliches festlegen.

Je mehr Struktur Sie in Ihren Protokollnachrichten haben, desto mehr können Ihre Parsing-/Suchsysteme (wie fluentd, elasticsearch, kibana) diese Struktur nutzen und ausführen ihre Aufgaben mit höherer Geschwindigkeit und weniger CPU/Speicher.

Hoffe das hilft!

16
Castaglia

Sie werden von der strukturierten Protokollierung nicht viel profitieren, wenn Ihre App einige hundert Protokollnachrichten pro Tag erstellt. Sie werden es auf jeden Fall tun, wenn Sie einige hundert Protokollnachrichten pro Sekunde von vielen verschiedenen bereitgestellten Apps erhalten.

In diesem Zusammenhang ist das Setup, bei dem Protokollnachrichten in the ELK Stack landen, auch für die Skalierung geeignet, bei der die Protokollierung in SQL zu einem Engpass wird.

Ich habe die Einrichtung von "Basic Logging and Search" mit SQL select .. like Und Regexps gesehen, die an ihre Grenzen gestoßen sind, wo sie auseinanderfallen - es gibt Fehlalarme, Auslassungen, schrecklichen Filtercode mit bekannten Fehlern, die schwer zu pflegen sind und nein -eine möchten neue Protokollnachrichten berühren, die nicht den Annahmen des Filters entsprechen, die Protokollierungsanweisungen im Code nur ungern berühren, damit sie keine Berichte beschädigen usw.

Es entstehen also mehrere Softwarepakete, um dieses Problem besser zu lösen. Es gibt Serilog, ich höre, dass das NLog-Team es sich ansieht , und wir haben StructuredLogging.Json Für Nlog geschrieben , ich sehe auch, dass das new ASP.Net-Kernprotokollierungsabstraktionen "ermöglichen es Protokollierungsanbietern, ... strukturierte Protokollierung zu implementieren".

Ein Beispiel mit StructuredLogging. Sie melden sich wie folgt bei einem NLog-Logger an:

logger.ExtendedError("Order send failed", new { OrderId = 1234, RestaurantId = 4567 } );

Diese strukturierten Daten gehen an Kibana. Der Wert 1234 Wird im Feld OrderId des Protokolleintrags gespeichert. Sie können dann mithilfe der Kibana-Abfragesyntax nach z. Alle Protokolleinträge mit @LogType:nlog AND Level:Error AND OrderId:1234.

Message und OrderId sind jetzt nur noch Felder, die nach Bedarf nach genauen oder ungenauen Übereinstimmungen durchsucht oder nach Anzahl aggregiert werden können. Dies ist leistungsstark und flexibel.

Aus den Best Practices StructuredLogging :

Die protokollierte Nachricht sollte jedes Mal dieselbe sein. Es sollte eine konstante Zeichenfolge sein, keine Zeichenfolge, die so formatiert ist, dass sie Datenwerte wie IDs oder Mengen enthält. Dann ist es einfach zu suchen.

Die protokollierte Nachricht sollte eindeutig sein, d. H. Nicht mit der Nachricht übereinstimmen, die von einer nicht verwandten Protokollanweisung erzeugt wird. Dann passt die Suche danach nicht zu nicht verwandten Dingen.

8
Anthony