Wie man Logging in einer Node.js-Anwendung mit Pino-logger implementiert

Avatar of Sarthak Duggal
Sarthak Duggal am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Logging ist für sich genommen ein wichtiger Aspekt jeder Anwendung. Logging hilft Entwicklern zu verstehen, was ihr Code tut. Es hilft auch dabei, Stunden an Debugging-Arbeit zu sparen. Dieses Tutorial befasst sich mit der Implementierung von Logging in einer Node.js-Anwendung unter Verwendung von Pino-logger.

Mit Logging können Sie jede Information über den Ablauf der Anwendung speichern. Mit Pino als Abhängigkeit für eine Node.js-Anwendung wird die Implementierung von Logging mühelos, und sogar das Speichern dieser Logs in einer separaten Log-Datei. Und seine 7,8K Sterne auf GitHub sind ein Beweis dafür.

In dieser Anleitung

  • Sie werden lernen, wie Sie Logging-Dienste mit verschiedenen Logging-Leveln konfigurieren.
  • Sie werden lernen, wie Sie die Logs in Ihrem Terminal aufhübschen und ob Sie die JSON-Antwort in Ihren Logs einschließen möchten oder nicht.
  • Sie werden sehen, wie Sie diese Logs in einer separaten Log-Datei speichern.

Wenn Sie fertig sind, können Sie Logging mit Best Practices in Ihrer Node.js-Anwendung mit Pino-logger implementieren.

Voraussetzungen

Stellen Sie vor dem Befolgen dieses Tutorials sicher, dass Sie

  • Vertrautheit mit der Verwendung von Express für einen Server.
  • Vertrautheit mit der Einrichtung einer REST-API ohne Authentifizierung.
  • Ein Verständnis von Kommandozeilen-Tools oder integrierten Terminals in Code-Editoren.

Das Herunterladen und Installieren eines Tools wie Postman wird für das Testen von API-Endpunkten empfohlen.

Schritt 1: Einrichten des Projekts

In diesem Schritt richten Sie eine grundlegende Node.js CRUD-Anwendung mit Express und Mongoose ein. Dies tun Sie, weil es besser ist, die Logging-Funktionalität in einer Codebasis zu implementieren, die eine reale Anwendung nachahmt.

Da sich dieser Artikel mit der Implementierung des Loggers befasst, können Sie die Anleitung „How To Perform CRUD Operations with Mongoose and MongoDB Atlas“ befolgen, um Ihre grundlegende CRUD-Anwendung in Node.js zu erstellen.

Nach Abschluss dieses Tutorials sollten Sie über eine Node.js-Anwendung verfügen, die Routen für create, read, update und delete enthält.

Außerdem können Sie zu diesem Zeitpunkt nodemon herunterladen, sodass der Server jedes Mal, wenn Sie Änderungen in Ihrer Codebasis speichern, automatisch neu gestartet wird und Sie ihn nicht manuell mit node server.js neu starten müssen.

Geben Sie also diesen Befehl in Ihr Terminal ein

npm install -g --force nodemon

Das Flag -g bedeutet, dass die Abhängigkeit global installiert wird. Um etwas global auszuführen, fügen Sie das Flag --force zum Befehl hinzu.

Schritt 2: Installation von Pino

In diesem Schritt installieren Sie die neuesten Versionen der für das Logging benötigten Abhängigkeiten. Dazu gehören Pino, Express-Pino-logger und Pino-pretty. Sie benötigen den folgenden Befehl in Ihrem Kommandozeilen-Tool im Stammverzeichnis des Projekts.

npm install [email protected] [email protected] [email protected]

An dieser Stelle sind Sie bereit, einen Logger-Dienst mit Pino zu erstellen.

Schritt 3: Erstellen des Logger-Dienstes

In diesem Schritt erstellen Sie einen Pino-Logger-Dienst mit verschiedenen Log-Leveln wie warning, error, info usw.

Danach konfigurieren Sie diesen Logger-Dienst in Ihrer App mit Node.js-Middleware. Beginnen Sie damit, ein neues Verzeichnis services im Stammverzeichnis zu erstellen.

mkdir services

Erstellen Sie in diesem neuen Verzeichnis eine neue Datei loggerService.js und fügen Sie den folgenden Code hinzu.

const pino = require('pino')
module.exports = pino({})

Dieser Code definiert den grundlegendsten Logger-Dienst, den Sie mit Pino-logger erstellen können. Die exportierte Funktion pino nimmt zwei optionale Argumente entgegen: options und destination, und gibt eine logger instance zurück.

Derzeit übergeben Sie jedoch keine Optionen, da Sie diesen Logger-Dienst in späteren Schritten konfigurieren werden. Dies kann jedoch ein kleines Problem mit diesem Logger-Dienst verursachen: Das JSON-Log, das Sie gleich sehen werden, ist nicht lesbar. Um es in ein lesbares Format zu ändern, erwähnen Sie die Option prettyPrint in der exportierten Funktion pino und danach sollte Ihre Datei loggerService.js etwa so aussehen.

const pino = require('pino')
module.exports = pino(
  {
    prettyPrint: true,
  },
)

Die Konfiguration Ihres loggerService wird in späteren Schritten behandelt.

Der nächste Schritt zur Fertigstellung dieses Logger-Dienstes ist das Hinzufügen der folgenden Codezeilen zu Ihrer Datei server.js im Stammverzeichnis.

const expressPinoLogger = require('express-pino-logger');
const logger = require('./services/loggerService');

In diesem Code importieren Sie den gerade erstellten logger service sowie das zuvor installierte npm-Paket express-pino-logger.

Der letzte Schritt ist die Konfiguration von express-pino-logger mit dem erstellten logger service. Fügen Sie diesen Code nach const app = express(); in derselben Datei hinzu.

// ...

const loggerMidlleware = expressPinoLogger({
  logger: logger,
  autoLogging: true,
});

app.use(loggerMidlleware);

// ...

Dieser Code erstellt ein loggerMiddleware mit expressPinoLogger. Die erste an die Funktion übergebene Option ist der logger selbst, der den zuvor erstellten loggerService darstellt. Die zweite Option ist autoLogging, die entweder true oder false als Wert annehmen kann. Sie gibt an, ob Sie die JSON-Antwort in Ihren Logs wünschen oder nicht. Darauf kommen wir gleich.

Um nun den loggerService zu testen, besuchen Sie Ihre Datei foodRoutes.js erneut. Importieren Sie den loggerService mit diesem Code am Anfang.

const logger = require('../services/loggerService')

Fügen Sie dann im GET-Routen-Controller, den Sie zuvor erstellt haben, diese Codezeile am Anfang der Callback-Funktion ein.

// ...

app.get("/food", async (request, response) => {
  logger.info('GET route is accessed')
  // ...
});

// ...

Die Methode info ist einer der Standard-Level, die mit Pino-logger geliefert werden. Andere Methoden sind: fatal, error, warn, debug, trace oder silent.

Sie können jede davon verwenden, indem Sie eine Nachrichten-Zeichenkette als Argument übergeben.

Bevor Sie nun den Logging-Dienst testen, hier der vollständige Code für die Datei server.js bis zu diesem Punkt.

const express = require("express");
const expressPinoLogger = require('express-pino-logger');
const logger = require('./services/loggerService');
const mongoose = require("mongoose");
const foodRouter = require("./routes/foodRoutes.js");
const app = express();
// ...
const loggerMidleware = expressPinoLogger({
  logger: logger,
  autoLogging: true,
});
app.use(loggerMidleware);
// ...
app.use(express.json());
mongoose.connect(
  "mongodb+srv://madmin:<password>@clustername.mongodb.net/<dbname>?retryWrites=true&w=majority",
  {
    useNewUrlParser: true,
    useFindAndModify: false,
    useUnifiedTopology: true
  }
);
app.use(foodRouter);

app.listen(3000, () => {
  console.log("Server is running...");
});

Vergessen Sie auch nicht, Ihren Server neu zu starten.

nodemon server.js

Nun können Sie den Log in Ihrem Terminal sehen. Testen Sie diesen API-Routen-Endpunkt in Postman oder etwas Ähnlichem, um ihn zu sehen. Nach dem Testen der API sollten Sie etwas wie dieses in Ihrem Terminal sehen.

Showing a black terminal window with output, including a first line in bright yellow, a second line in green and rest of the information in white. The information indicates the tool is watching files, starting the node server, when the GET route is accessed, and different API endpoints.

Dies liefert viele Informationen.

  • Das erste Stück Information ist der Zeitstempel des Logs, der im Standardformat angezeigt wird, aber wir können ihn in späteren Schritten in etwas Lesbareres ändern.
  • Als nächstes kommt info, einer der Standard-Level, die mit Pino-logger geliefert werden.
  • Als nächstes folgt eine kleine Meldung, dass die Anfrage abgeschlossen wurde.
  • Zuletzt sehen Sie die gesamte JSON-Antwort für die jeweilige Anfrage in der nächsten Zeile.

Schritt 4: Konfigurieren der Logs

In diesem Schritt lernen Sie, wie Sie den Logger-Dienst konfigurieren und die Logs in Ihrem Terminal mit pino-pretty sowie integrierten Optionen aus dem zuvor installierten pino-Paket aufhübschen.

Benutzerdefinierte Level

An dieser Stelle wissen Sie, dass pino-logger Standard-Logging-Level mitbringt, die Sie als Methoden zur Anzeige von Logs verwenden können.

Sie haben im vorherigen Schritt logger.info verwendet.

Aber pino-logger gibt Ihnen die Möglichkeit, benutzerdefinierte Level zu verwenden. Beginnen Sie damit, die Datei loggerService.js in Ihrem services-Verzeichnis erneut zu besuchen. Fügen Sie die folgenden Codezeilen hinzu, nachdem Sie das pino-Paket oben importiert haben.

// ...
const levels = {
  http: 10,
  debug: 20,
  info: 30,
  warn: 40,
  error: 50,
  fatal: 60,
};
// ...

Dieser Code ist ein einfaches JavaScript-Objekt, das zusätzliche Logging-Level definiert. Die Schlüssel dieses Objekts entsprechen dem Namensraum des Log-Levels, und die Werte sollten dem numerischen Wert dieses Levels entsprechen.

Um dies nun zu verwenden, müssen Sie all das in der exportierten Pino-Funktion angeben, die Sie zuvor definiert haben. Denken Sie daran, dass das erste Argument, das sie nimmt, ein Objekt mit einigen integrierten Optionen ist.

Schreiben Sie diese Funktion wie folgt um.

module.exports = pino({
  prettyPrint: true,
  customLevels: levels, // our defined levels
  useOnlyCustomLevels: true,
  level: 'http',
})

Im obigen Code

  • Die erste Option, customLevels: levels, gibt an, dass unsere benutzerdefinierten Log-Level als zusätzliche Log-Methoden verwendet werden sollen.
  • Die zweite Option, useOnlyCustomLevels: true, gibt an, dass Sie nur Ihre customLevels verwenden und die Pino-Level weglassen möchten.

/explanation Um die zweite Option, useOnlyCustomLevels, anzugeben, muss das Standard-level des Loggers auf einen Wert in customLevels geändert werden. Deshalb haben Sie die dritte Option angegeben.

Nun können Sie Ihren loggerService erneut testen und versuchen, ihn mit einem Ihrer customLevels zu verwenden. Versuchen Sie es in Ihrer Datei foodRoutes.js mit etwas wie diesem.

// ...

app.get"/foods", async (request, response) => {
    logger.http('GET route is accessed')
});

// ...

/explanation Vergessen Sie nicht, in Ihrer server.js-Datei autoLogging: false zu setzen, da die irrelevante JSON-Antwort, die damit einhergeht, nicht wirklich benötigt wird.

const pino = require('pino')
const levels = {
  http: 10,
  debug: 20,
  info: 30,
  warn: 40,
  error: 50,
  fatal: 60,
};
module.exports = pino(
  {
    prettyPrint: true,
    customLevels: levels, // our defined levels
    useOnlyCustomLevels: true,
    level: 'http',
  },
)

Sie sollten etwas wie dieses in Ihrem Terminal erhalten.

A black terminal window that shows the node server starting in green, a note that the server is running, and a timestamp for when the GET route is accessed.

Und alle unnötigen Informationen sollten verschwunden sein.

Logs aufhübschen

Nun können Sie fortfahren und die Logs aufhübschen. Mit anderen Worten, Sie fügen dem Terminal-Output Stil hinzu, der das Lesen erleichtert (oder „hübscher“ macht).

Beginnen Sie damit, eine weitere Option in die exportierte Funktion pino einzufügen. Ihre Funktion pino sollte nach dem Hinzufügen dieser Option etwa so aussehen.

module.exports = pino({
  customLevels: levels, // our defined levels
  useOnlyCustomLevels: true,
  level: 'http',
  prettyPrint: {
    colorize: true, // colorizes the log
    levelFirst: true,
    translateTime: 'yyyy-dd-mm, h:MM:ss TT',
  },
})

Sie haben eine weitere Option hinzugefügt, prettyPrint, ein JavaScript-Objekt, das das Aufhübschen aktiviert. Nun gibt es auch innerhalb dieses Objekts weitere Eigenschaften.

  • colorize: Fügt Farben zu den Terminal-Logs hinzu. Unterschiedliche Log-Level werden mit unterschiedlichen Farben versehen.
  • levelFirst: Zeigt den Namen des Log-Levels vor dem protokollierten Datum und der Uhrzeit an.
  • translateTime: Übersetzt den Zeitstempel in ein menschlich lesbares Datums- und Zeitformat.

Versuchen Sie nun erneut, den API-Endpunkt aufzurufen, aber stellen Sie vorher sicher, dass Sie mehr als eine Logging-Anweisung hinzufügen, um verschiedene Arten von Logs in Ihrem Terminal zu sehen.

// ...

app.get("/foods", async (request, response) => {
  logger.info('GET route is accessed')
  logger.debug('GET route is accessed')
  logger.warn('GET route is accessed')
  logger.fatal('GET route is accessed')

// ...

Sie sollten etwas wie dieses in Ihrem Terminal sehen.

A black terminal window with the same information as before, but with colored labels for different lines of information, like a red label for a fatal message.

An dieser Stelle haben Sie Ihren Logger-Dienst ausreichend konfiguriert, um ihn in einer produktionsreifen Anwendung zu verwenden.

Schritt 5: Speichern von Logs in einer Datei

In diesem letzten Schritt lernen Sie, wie Sie diese Logs in einer separaten Log-Datei speichern. Das Speichern von Logs in einer separaten Datei ist sehr einfach. Alles, was Sie tun müssen, ist die Option destination in Ihrer exportierten pino-function zu verwenden.

Sie können damit beginnen, die pino-function zu bearbeiten, indem Sie die Option destination wie folgt übergeben.

module.exports = pino(
  {
    customLevels: levels, // the defined levels
    useOnlyCustomLevels: true,
    level: 'http',
    prettyPrint: {
      colorize: true, // colorizes the log
      levelFirst: true,
      translateTime: 'yyyy-dd-mm, h:MM:ss TT',
    },
  },
  pino.destination(`${__dirname}/logger.log`)
)

pino.destination nimmt den Pfad zur Log-Datei als Argument entgegen. Die Variable __dirname verweist auf das aktuelle Verzeichnis, das für diese Datei das Verzeichnis services ist.

/explanation Sie haben die Datei logger.log in Ihren Pfad eingefügt, obwohl sie noch nicht existiert. Das liegt daran, dass die Datei beim Speichern dieser Datei automatisch erstellt wird. Wenn die Datei aus irgendeinem Grund nicht erstellt wird, können Sie eine manuell erstellen und sie zum Ordner hinzufügen.

Hier ist die vollständige Datei loggerService.js.

const pino = require('pino')
const levels = {
  http: 10,
  debug: 20,
  info: 30,
  warn: 40,
  error: 50,
  fatal: 60,
};
module.exports = pino(
  {
    customLevels: levels, // our defined levels
    useOnlyCustomLevels: true,
    level: 'http',
    prettyPrint: {
      colorize: true, // colorizes the log
      levelFirst: true,
      translateTime: 'yyyy-dd-mm, h:MM:ss TT',
    },
  },
  pino.destination(`${__dirname}/logger.log`)
)

Testen Sie Ihre API erneut, und Sie sollten Ihre Logs anstelle Ihres Terminals in Ihrer Log-Datei sehen.

Fazit

In diesem Artikel haben Sie gelernt, wie Sie einen Logging-Dienst erstellen, den Sie in produktionsreifen Anwendungen verwenden können. Sie haben gelernt, wie Sie Logs konfigurieren und wie Sie diese Logs zur späteren Referenz in einer separaten Datei speichern können.

Sie können immer noch mit verschiedenen Konfigurationsoptionen experimentieren, indem Sie die offizielle Pino-logger-Dokumentation lesen.

Hier sind einige Best Practices, die Sie bei der Erstellung eines neuen Logging-Dienstes beachten können:

  • Kontext: Ein Log sollte immer einen bestimmten Kontext über die Daten, die Anwendung, die Zeit usw. haben.
  • Zweck: Jeder Log sollte einen bestimmten Zweck haben. Wenn der gegebene Log beispielsweise zum Debuggen verwendet wird, können Sie sicherstellen, dass Sie ihn vor einem Commit löschen.
  • Format: Das Format für alle Logs sollte immer leicht lesbar sein.