Ein Logger für jede Gelegenheit

Node.js-Module: Winston
Keine Kommentare

Mit Node.js lassen sich Applikationen sehr schnell entwickeln. Dinge, die in der Anfangseuphorie allerdings schnell zu kurz kommen, sind die Behandlung von Fehlern, Testing oder Logging – also das Festhalten von Nachrichten beispielsweise in einer Logdatei. Mit Winston gibt es eine frameworkunabhängige Bibliothek für Node.js, die Ihnen die Aufgabe des Loggings erheblich vereinfacht.

Einfach bedeutet jedoch nicht gleichzeitig unflexibel, wie Sie im Folgenden noch sehen werden, denn nahezu sämtliche Aspekte von Winston sind konfigurierbar. Angefangen bei den Loglevels über das Format der Logmeldungen bis hin zum Transport, können Sie Winston ganz nach Ihren Anforderungen beeinflussen.

Installation

Zum Zeitpunkt des Verfassens dieses Artikels befand sich Winston gerade an der Schwelle zur Version 3. Ein Major-Release bedeutet in der Regel auch eine Reihe von Breaking Changes. Dieser Artikel bezieht sich auf die neue Version.

Die Installation von Winston erfolgt über npm mit dem Kommando npm install winston@next. Nach der Installation wird der Logger konfiguriert und kann anschließend verwendet werden. Normalerweise erfolgt die Konfiguration in einer separaten Datei, die die Logger-Instanz exportiert. Das hat den Vorteil, dass Sie nur die Datei importieren müssen und den Logger dann sofort verwenden können.

const appLogger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'logs/app.log' })
  ]
});

module.exports = appLogger;

Wie Sie Listing 1 entnehmen können, erzeugen Sie den Logger mit der Methode createLogger. Ihr übergeben Sie ein Konfigurationsobjekt, über das Sie die Basisfunktionalität des Loggers einstellen. Das Level gibt das minimale Loglevel der Nachrichten an, die festgehalten werden. Nachrichten mit einem niedrigeren Level werden von diesem Logger ignoriert. Die Formateigenschaft legt das Format der Logeinträge fest. Im Fall unseres Beispiels werden JSON-Zeichenketten mit den Eigenschaften message und level ins Log geschrieben. Mit der Eigenschaft transports können Sie schließlich angeben, wohin Winston das Log schreiben soll. Der im Beispiel verwendete File-Transport schreibt das Log in das lokale Dateisystem. Um nun einen Logeintrag zu schreiben, binden Sie den Logger ein und rufen beispielsweise die info-Methode auf, um einen Eintrag des entsprechenden Levels zu erzeugen. Das nachfolgende Beispiel zeigt Ihnen einen Ausschnitt aus einer Express-Applikation mit integriertem Logger:

app.listen(8080, () => {
  logger.info('Server running');
});

Loglevels

Das Loglevel gibt die Wichtigkeit des Logeintrags an. Die Levels reichen vom unkritischsten silly bis zum schwerwiegenden error. Insgesamt unterstützt Winston in der Standardkonfiguration sechs Loglevels in aufsteigender Wichtigkeit: silly, debug, verbose, info, warn und error. Jedes dieser Levels wird intern durch eine Zahl repräsentiert. Mit der levels-Eigenschaft können Sie die unterstützten Loglevels beeinflussen. Hier haben Sie die Möglichkeit, entweder auf die von Winston vordefinierten Levels zurückzugreifen oder selbst Loglevels zu definieren.

Die vordefinierten Formate finden Sie unter winston.config. Sie tragen die Namen npm, syslog und cli, wobei npm der vordefinierte Satz von Loglevels ist. Ein benutzerdefinierter Satz von Loglevels ist ein Objekt mit der Eigenschaft levels mit der jeweiligen Bezeichnung als Schlüssel und der entsprechenden Zahl als Wert. Da einige Transports die farbliche Kennzeichnung von Logeinträgen unterstützen, sollten Sie außerdem eine Eigenschaft colors vorsehen. Hierbei handelt es sich wiederum um ein Objekt, dessen Schlüssel den Loglevels entsprechen. Die Werte sind die Namen der zugeordneten Farben wie beispielsweise red, yellow oder green. Um die Farben entsprechend anzuzeigen, können Sie den Console-Transport verwenden und das colorize-Format mit einem benutzerdefinierten Logging-Format kombinieren. Ein Beispiel hierfür sehen Sie in Listing 2.

const winston = require('winston');
const customFormat = winston.format.combine(
  winston.format.colorize(),
  winston.format.printf((entry) => {
    return `[${entry.level}]: ${entry.message}`;
  }),
);

const appLogger = winston.createLogger({
  level: 'error',
  format: customFormat,
  transports: [
    new winston.transports.Console()
  ]
});

module.exports = appLogger;

Formate

Wie Sie in Listing 2 schon sehen konnten, beschränkt sich das Logging-Format von Winston nicht nur auf einfache JSON-Strings. Winston liefert zahlreiche vordefinierte Formate wie beispielsweise die bereits verwendeten json- oder printf-Formate mit. Sie haben außerdem die Möglichkeit, mit der winston.format-Methode ein eigenes Format zu definieren. Diese Funktion erhält ein info-Objekt, das beispielsweise die Nachricht in der message-Eigenschaft enthält. Außerdem haben Sie Zugriff auf ein Options-Objekt, das die Formatoptionen enthält, die Sie beim Aufruf übergeben können. Der Rückgabewert der Methode ist ein angepasstes info-Objekt. Das folgende Beispiel verdeutlicht diesen Zusammenhang:

const customFormat = winston.format((info, opts) => {
  return {...info, message: `${info.level}: ${info.message}`};
});

Transports

Ein großer Teil der Flexibilität von Winston liegt an der Fähigkeit von Winston, dass nicht nur die Formate und Loglevels konfigurierbar und austauschbar sind, sondern auch die Transports. Der Transport definiert das Ziel der Logoperation. In Winston können Sie pro Logger nicht nur einen Transport, sondern ein Array aus mehreren Transports angeben und so beispielsweise auf die Konsole und in eine Datei loggen. Pro Transport können Sie außerdem ein minimales Loglevel angeben, sodass beispielsweise sämtliche info-Einträge auf die Konsole geschrieben werden. Einträge mit dem Level error werden sowohl auf die Konsole als auch in die Datei geloggt. Listing 3 enthält den entsprechenden Quellcode.

const winston = require('winston');

const appLogger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
  new winston.transports.File({ filename: 'logs/app.log', level: 'error' }),
  new winston.transports.Console({level: 'info'})
]
});

module.exports = appLogger;

Neben den Core Transports Console, File, Http und Stream, die jeweils Bestandteil von Winston sind, gibt es weitere von der Community implementierte Transports. So können Sie mit dem Elasticsearch Transport beispielsweise Ihre Logeinträge an einen Elasticsearch-Server senden oder mit dem Mail Transport Ihre Log-Einträge per E-Mail versenden. Lassen sich Ihre Anforderungen mit den bestehenden Transports nicht bedienen, haben Sie die Möglichkeit, Ihren eigenen Transport zu implementieren.

Fazit

Binden Sie Winston in seiner Basiskonfiguration ein, handelt es sich um einen sehr einfachen Logger, mit dem Sie Ihre Logeinträge beispielsweise in eine Datei schreiben können. Je nachdem, worin Ihre Anforderung besteht, lassen sich nahezu alle Aspekte von Winston konfigurieren, durch bestehende Komponenten erweitern oder durch eigene Implementierungen ergänzen. So gibt es kaum eine Problemstellung im Bereich Logging, die sich nicht mit Winston lösen lässt. Hinzu kommt, dass Winston komplett unabhängig von Frameworks und Bibliotheken ist und sich in jeder Umgebung einsetzen lässt.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -