Hoe u Winston kunt gebruiken om Node.js-toepassingen te registreren op Ubuntu 20.04

Inleiding

Een effectieve logging-oplossing is cruciaal voor het succes van elke applicatie. Winston is een veelzijdige logging-bibliotheek en een populaire logging-oplossing die beschikbaar is voor Node.js-applicaties. De functies van Winston omvatten ondersteuning voor meerdere opslagopties, logniveaus, logquery’s en een ingebouwde profiler.

In deze zelfstudie zul je Winston gebruiken om een Node/Express-applicatie te loggen die je zult maken als onderdeel van dit proces. Je zult ook zien hoe je Winston kunt combineren met Morgan, nog een populaire HTTP-verzoek-middleware-logger voor Node.js, om HTTP-verzoekgegevenslogs samen te voegen met andere informatie. Na het voltooien van deze zelfstudie zal je Ubuntu-server een kleine Node/Express-applicatie draaien, en Winston zal geïmplementeerd zijn om fouten en berichten naar een bestand en de console te loggen.

Vereisten

Om deze zelfstudie te volgen, heb je nodig:

Stap 1 — Het maken van een Basis Node/Express App

Winston wordt vaak gebruikt voor het loggen van gebeurtenissen van webapplicaties gebouwd met Node.js. In deze stap maakt u een eenvoudige Node.js webapplicatie met behulp van het Express-framework. U zult express-generator, een commandoregeltool, gebruiken om snel een Node/Express webapplicatie te laten draaien.

Omdat u de Node Package Manager hebt geïnstalleerd tijdens de vereisten, kunt u de npm-opdracht gebruiken om express-generator te installeren:

  1. sudo npm install express-generator -g

De -g vlag installeert het pakket globaal, wat betekent dat het gebruikt kan worden als een command-line tool buiten een bestaand Node project/module.

Met express-generator geïnstalleerd, kun je je app creëren met behulp van het express commando, gevolgd door de naam van de map die je wilt gebruiken voor het project:

  1. express myApp

Voor deze tutorial zal het project myApp genoemd worden.

Let op: Het is ook mogelijk om de express-generator tool rechtstreeks uit te voeren zonder deze eerst globaal te installeren als een systeembreed commando. Om dit te doen, voer dit commando uit:

  1. npx express-generator myApp

Het npx commando is een commando-runner meegeleverd met de Node Package Manager dat het gemakkelijk maakt om command-line tools uit het npm register uit te voeren.

Tijdens de eerste keer uitvoeren, zal het je vragen of je akkoord gaat met het downloaden van het pakket:

Output
Need to install the following packages:
  express-generator
Ok to proceed? (y)

Antwoord met y en druk op ENTER. Nu kun je npx express-generator gebruiken in plaats van express.

Vervolgens, installeer Nodemon, die de applicatie automatisch zal herladen wanneer je wijzigingen aanbrengt. Een Node.js applicatie moet opnieuw worden opgestart telkens als er wijzigingen worden aangebracht in de broncode om die wijzigingen van kracht te laten worden, dus Nodemon zal automatisch wijzigingen controleren en de applicatie opnieuw starten. Aangezien je nodemon als een command-line tool wilt kunnen gebruiken, installeer het met de -g vlag:

  1. sudo npm install nodemon -g

Om de installatie van de applicatie te voltooien, ga naar de applicatiemap en installeer de afhankelijkheden als volgt:

  1. cd myApp
  2. npm install

Standaard draaien applicaties gemaakt met express-generator op poort 3000, dus je moet ervoor zorgen dat de firewall de poort niet blokkeert.

Om poort 3000 te openen, voer je het volgende commando uit:

  1. sudo ufw allow 3000

Je hebt nu alles wat je nodig hebt om je webapplicatie te starten. Voer het volgende commando uit om dit te doen:

  1. nodemon bin/www

Dit commando start de applicatie op poort 3000. Je kunt testen of het werkt door je browser te richten op http://your_server_ip:3000. Je zou iets moeten zien zoals dit:

Op dit punt kun je een tweede SSH-sessie naar je server starten voor de rest van deze tutorial, terwijl de webapplicatie die je zojuist hebt gestart actief blijft in de oorspronkelijke sessie. Voor de rest van dit artikel zal de initiële SSH-sessie waarin de applicatie wordt uitgevoerd Session A worden genoemd. Eventuele commando’s in Session A zullen verschijnen op een donker marineblauwe achtergrond zoals dit:

  1. nodemon bin/www

Je zult de nieuwe SSH-sessie gebruiken om commando’s uit te voeren en bestanden te bewerken. Deze sessie zal Session B worden genoemd. Eventuele commando’s in Session B zullen verschijnen op een lichtblauwe achtergrond zoals dit:

  1. cd ~/myApp

Tenzij anders vermeld, voer je alle overige commando’s uit in Session B.

In deze stap heb je de basisapplicatie gemaakt. Hierna zul je deze aanpassen.

Stap 2 — Aanpassen van de Logvariabelen

Terwijl de standaardapplicatie gemaakt door express-generator een goed begin is, moet je de applicatie aanpassen zodat het de juiste logger zal oproepen wanneer nodig.

express-generator bevat de Morgan HTTP logging middleware die je zult gebruiken om gegevens over alle HTTP-verzoeken te loggen. Aangezien Morgan uitvoerstreams ondersteunt, werkt het goed samen met de streamondersteuning die is ingebouwd in Winston, waardoor je HTTP-verzoekgegevenslogs kunt consolideren met al het andere dat je kiest om te loggen met Winston.

De boilerplate van express-generator gebruikt de variabele logger bij het verwijzen naar het morgan-pakket. Aangezien je zowel morgan als winston zult gebruiken, die beide logging-pakketten zijn, kan het verwarrend zijn om een van beide logger te noemen. Om aan te geven welke variabele je wilt, kun je de variabelendeclaraties wijzigen door het app.js-bestand te bewerken.

Om app.js te openen voor bewerking, gebruik nano of je favoriete teksteditor:

  1. nano ~/myApp/app.js

Zoek de volgende regel vlakbij het begin van het bestand:

~/myApp/app.js
...
var logger = require('morgan');
...

Verander de variabelenaam van logger naar morgan:

~/myApp/app.js
...
var morgan = require('morgan');
...

Deze update geeft aan dat de gedeclareerde variabele morgan de require()-methode zal oproepen die is gekoppeld aan de Morgan-verzoeklogger.

Je moet vinden waar anders de variabele logger werd genoemd in het bestand en het veranderen naar morgan. Je moet ook het logformaat dat wordt gebruikt door het morgan-pakket wijzigen naar combined, wat het standaard Apache-logformaat is en nuttige informatie zal bevatten in de logs, zoals het externe IP-adres en de user-agent HTTP-verzoekheader.

Om dit te doen, vind de volgende regel:

~/myApp/app.js
...
app.use(logger('dev'));
...

Werk het bij naar het volgende:

~/myApp/app.js
...
app.use(morgan('combined'));
...

Deze wijzigingen zullen je helpen begrijpen welk logpakket op een gegeven moment wordt genoemd na integratie van de Winston-configuratie.

Wanneer je klaar bent, sla het bestand op en sluit het.

Nu je app is ingesteld, kun je beginnen met werken met Winston.

Stap 3 — Winston installeren en configureren

In deze stap ga je Winston installeren en configureren. Je gaat ook de configuratieopties verkennen die beschikbaar zijn als onderdeel van het winston-pakket en een logger maken om informatie naar een bestand en de console te loggen.

Installeer winston met de volgende opdracht:

  1. cd ~/myApp
  2. npm install winston

Het is handig om alle ondersteunende of hulpprogrammaconfiguratiebestanden voor je applicaties in een speciale map te bewaren. Maak een config-map aan die de configuratie van winston zal bevatten:

  1. mkdir ~/myApp/config

Vervolgens maak je een map aan waarin je logbestanden zullen worden opgeslagen:

  1. mkdir ~/myApp/logs

Tenslotte installeer je app-root-path:

  1. npm install app-root-path --save

De app-root-path-package is handig bij het specificeren van paden in Node.js. Hoewel deze package niet direct gerelateerd is aan Winston, is het handig bij het bepalen van paden naar bestanden in Node.js. Je zult het gebruiken om de locatie van de Winston-logbestanden vanaf de root van het project aan te geven en lelijke relatieve pad-syntax te vermijden.

Nu de configuratie voor het omgaan met logging gereed is, kun je je instellingen definiëren. Maak ~/myApp/config/winston.js aan en open het voor bewerking:

  1. nano ~/myApp/config/winston.js

Het winston.js-bestand zal je winston-configuratie bevatten.

Voeg vervolgens de volgende code toe om de app-root-path– en winston-packages te vereisen:

~/myApp/config/winston.js
const appRoot = require('app-root-path');
const winston = require('winston');

Met deze variabelen op hun plaats, kun je de configuratie-instellingen voor je transports definiëren. Transports zijn een concept dat geïntroduceerd is door Winston en verwijst naar de opslag-/uitvoermechanismen die gebruikt worden voor de logs. Winston wordt geleverd met vier kerntransports ingebouwd: Console, File, HTTP en Stream.

Je zult je in deze handleiding richten op de console- en bestandstransports. Het console-transport zal informatie loggen naar de console, en het bestandstransport zal informatie loggen naar een gespecificeerd bestand. Elke transportdefinitie kan configuratie-instellingen bevatten, zoals bestandsgrootte, logniveaus en logformaat.

Hier is een snelle samenvatting van de instellingen die je zult gebruiken voor elk transport:

  • niveau: niveau van berichten om te loggen.
  • bestandsnaam: het bestand dat wordt gebruikt om loggegevens naar te schrijven.
  • handleExceptions: uitzonderingen die niet worden afgehandeld, vastleggen en loggen.
  • maxgrootte: maximale grootte van het logbestand, in bytes, voordat er een nieuw bestand wordt gemaakt.
  • maxBestanden: limiet van het aantal bestanden dat wordt aangemaakt wanneer de grootte van het logbestand wordt overschreden.
  • indeling: hoe de loguitvoer wordt geformatteerd.

Logniveaus geven de prioriteit van berichten aan en worden aangegeven door een integer. Winston gebruikt npm logniveaus die zijn geprioriteerd van 0 tot 6 (hoogste tot laagste):

  • 0: fout
  • 1: waarschuwing
  • 2: info
  • 3: http
  • 4: uitgebreid
  • 5: debug
  • 6: gek

Bij het specificeren van een logniveau voor een bepaald transport, wordt alles op dat niveau of hoger gelogd. Bijvoorbeeld, bij het instellen van een niveau van info, wordt alles op niveau error, warn, of info gelogd.

Logniveaus worden gespecificeerd bij het aanroepen van de logger, wat betekent dat je de volgende opdracht kunt uitvoeren om een fout te registreren: logger.error('testfoutbericht').

Nog steeds in het configuratiebestand, voeg de volgende code toe om de configuratie-instellingen te definiëren voor de bestand– en console-transporten in de winston-configuratie:

~/myApp/config/winston.js
...
// definieer de aangepaste instellingen voor elk transport (bestand, console)
const options = {
  file: {
    level: "info",
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5,
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  },
  console: {
    level: "debug",
    handleExceptions: true,
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  },
};

Voeg vervolgens de volgende code toe om een nieuwe winston-logger te instantiëren met bestands- en consoletransports met behulp van de eigenschappen gedefinieerd in de options-variabele:

~/myApp/config/winston.js
...
// instantieer een nieuwe Winston Logger met de bovenstaande instellingen
const logger = winston.createLogger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console),
  ],
  exitOnError: false, // niet afsluiten bij afgehandelde uitzonderingen
});

Standaard geeft morgan alleen uit naar de console, dus u zult een streamfunctie definiëren die morgan-gegenereerde output naar de winston-logbestanden kan leiden. U zult het niveau info gebruiken om de output op te pikken door beide transports (bestand en console). Voeg de volgende code toe aan het configuratiebestand:

~/myApp/config/winston.js
...
// maak een streamobject met een 'write'-functie die zal worden gebruikt door 'morgan'
logger.stream = {
  write: function(message, encoding) {
    // gebruik het logniveau 'info' zodat de output wordt opgepikt door beide
    // transports (bestand en console)
    logger.info(message);
  },
};

Voeg tot slot de onderstaande code toe om de logger te exporteren zodat deze kan worden gebruikt in andere delen van de toepassing:

~/myApp/config/winston.js
...
module.exports = logger;

Het voltooide configuratiebestand voor winston ziet er nu als volgt uit:

~/myApp/config/winston.js
const appRoot = require("app-root-path");
const winston = require("winston");

// definieer de aangepaste instellingen voor elk transportmiddel (bestand, console)
const options = {
  file: {
    level: "info",
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    maxsize: 5242880, // 5 MB
    maxFiles: 5,
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    ),
  },
  console: {
    level: "debug",
    handleExceptions: true,
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  },
};

// maak een nieuwe Winston Logger-instantie aan met de hierboven gedefinieerde instellingen
const logger = winston.createLogger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console),
  ],
  exitOnError: false, // stop niet bij behandelde uitzonderingen
});

// maak een streamobject met een 'write'-functie die zal worden gebruikt door `morgan`
logger.stream = {
  write: function (message, encoding) {
    // gebruik het logniveau 'info' zodat de uitvoer wordt opgepikt door beide
    // transports (bestand en console)
    logger.info(message);
  },
};

module.exports = logger;

Sla het bestand op en sluit het.

Je hebt nu de logger geconfigureerd, maar je applicatie is er nog niet van op de hoogte, of hoe het te gebruiken, dus je moet de logger integreren met de applicatie.

Stap 4 — Integratie van Winston met de Applicatie

Om je logger te laten werken met de applicatie, moet je express ervan op de hoogte stellen. Zoals je in Stap 2 hebt gezien, bevindt je express-configuratie zich in app.js, dus je kunt je logger in dit bestand importeren.

Open het bestand om te bewerken:

  1. nano ~/myApp/app.js

Voeg een variabelendefinitie voor winston toe vlak bij de bovenkant van het bestand, samen met de andere require-verklaringen:

~/myApp/app.js
...
var winston = require('./config/winston');
...

De eerste plaats waar je winston zal gebruiken is met morgan. Nog steeds in app.js, zoek de volgende regel:

~/myApp/app.js
...
app.use(morgan('combined'));
...

Werk het bij om de stream optie op te nemen:

~/myApp/app.js
...
app.use(morgan('combined', { stream: winston.stream }));
...

Hier stel je de stream optie in op de stream interface die je hebt aangemaakt als onderdeel van de winston configuratie.

Sla het bestand op en sluit het.

In deze stap heb je je Express applicatie geconfigureerd om samen te werken met Winston. Vervolgens zul je de log data bekijken.

Stap 5 — Toegang tot Log Data en Opnemen van Aangepaste Logberichten

Nu de applicatie geconfigureerd is, ben je klaar om wat log data te zien. In deze stap zul je de log entries bekijken en je instellingen bijwerken met een voorbeeld van een aangepast logbericht.

Als je de pagina opnieuw laadt in de webbrowser, zou je iets vergelijkbaars moeten zien in de console van SSH Sessie A:

Output
[nodemon] restarting due to changes... [nodemon] starting `node bin/www` info: ::1 - - [25/Apr/2022:18:10:55 +0000] "GET / HTTP/1.1" 200 170 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36" info: ::1 - - [25/Apr/2022:18:10:55 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"

Er zijn twee log entries hier: de eerste voor het verzoek naar de HTML pagina; de tweede voor de bijbehorende stylesheet. Aangezien elke transport is geconfigureerd om info niveau log data te verwerken, zou je ook vergelijkbare informatie moeten zien in de file transport gelegen op ~/myApp/logs/app.log.

Om de inhoud van het logbestand te bekijken, voer het volgende commando uit:

  1. tail ~/myApp/logs/app.log

`tail` zal de laatste delen van het bestand in uw terminal weergeven.

Je zou iets soortgelijks moeten zien als het volgende:

{"level":"info","message":"::1 - - [25/Apr/2022:18:10:55 +0000] \"GET / HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\"\n","timestamp":"2022-04-25T18:10:55.573Z"}
{"level":"info","message":"::1 - - [25/Apr/2022:18:10:55 +0000] \"GET /stylesheets/style.css HTTP/1.1\" 304 - \"http://localhost:3000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\"\n","timestamp":"2022-04-25T18:10:55.588Z"}

De uitvoer in het bestand transport zal worden geschreven als een JSON-object omdat je `winston.format.json()` hebt gebruikt in de `format` optie voor de configuratie van het bestand transport. Je kunt meer leren over JSON in Een Inleiding tot JSON.

Tot nu toe neemt jouw logger alleen HTTP-verzoeken en gerelateerde gegevens op. Deze informatie is essentieel om in je logs te hebben.

In de toekomst wil je mogelijk aangepaste logberichten opnemen, zoals voor het registreren van fouten of het profileren van de prestaties van databasequery’s. Als voorbeeld zul je de logger oproepen vanuit de foutafhandelingsroute. Standaard bevat het `express-generator` pakket al een `404` en `500` foutafhandelingsroute, dus daar zul je mee werken.

Open het `~/myApp/app.js` bestand:

  1. nano ~/myApp/app.js

Zoek het codeblok onderaan het bestand dat er als volgt uitziet:

~/myApp/app.js
...
// foutafhandelaar
app.use(function(err, req, res, next) {
  // locals instellen, alleen fouten verschaffen in ontwikkeling
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // de foutpagina renderen
  res.status(err.status || 500);
  res.render('error');
});
...

Deze sectie is de uiteindelijke foutafhandelingsroute die uiteindelijk een foutreactie terug naar de client zal sturen. Aangezien alle server-side fouten door deze route zullen worden uitgevoerd, is het een goede plek om de `winston` logger op te nemen.

Omdat je nu te maken hebt met fouten, wil je de logniveaus error gebruiken. Beide transports zijn geconfigureerd om berichten op niveau error te loggen, dus je zou de uitvoer in de console- en bestandslogs moeten zien.

Je kunt alles wat je wilt in het logboek opnemen, inclusief informatie zoals:

  • err.status: De HTTP-foutstatuscode. Als deze niet al aanwezig is, standaard naar 500.
  • err.message: Details van de fout.
  • req.originalUrl: De URL die is aangevraagd.
  • req.path: Het padgedeelte van de aanvraag-URL.
  • req.method: HTTP-methode van de aanvraag (GET, POST, PUT, enz.).
  • req.ip: Externe IP-adres van de aanvraag.

Werk de foutafhandelingsroute bij om de winston-logging op te nemen:

~/myApp/app.js
...
// foutafhandelaar
app.use(function(err, req, res, next) {
  // locals instellen, alleen fouten verstrekken bij ontwikkeling
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // winston logging opnemen
  winston.error(
    `${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`
  );

  // de foutpagina renderen
  res.status(err.status || 500);
  res.render('error');
});
...

Opslaan en het bestand sluiten.

Om dit proces te testen, probeer toegang te krijgen tot een niet-bestaande pagina in je project. Toegang tot een niet-bestaande pagina zal een 404-fout veroorzaken. Probeer in je webbrowser de volgende URL te laden: http://jouw_server_ip:3000/foo. Dankzij de boilerplate die is gemaakt door express-generator, is de applicatie ingesteld om te reageren op zo’n fout.

Uw browser zal een foutmelding weergeven zoals deze:

Als u naar de console kijkt in SSH-sessie A, moet er een logboekvermelding voor de fout zijn. Dankzij het toegepaste colorize-formaat zou het gemakkelijk moeten zijn om het te vinden:

Output
[nodemon] starting `node bin/www` error: 404 - Not Found - /foo - GET - ::1 info: ::1 - - [25/Apr/2022:18:08:33 +0000] "GET /foo HTTP/1.1" 404 982 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36" info: ::1 - - [25/Apr/2022:18:08:33 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://localhost:3000/foo" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"

Wat betreft de bestandslogger, het opnieuw uitvoeren van het tail-commando zou u de nieuwe logboekvermeldingen moeten laten zien:

  1. tail ~/myApp/logs/app.log

U ziet een bericht zoals het volgende:

{"level":"error","message":"404 - Not Found - /foo - GET - ::1","timestamp":"2022-04-25T18:08:33.508Z"}

De foutmelding bevat alle gegevens die u specifiek hebt geïnstrueerd winston om te loggen als onderdeel van de foutafhandelaar. Deze informatie omvat de foutstatus (404 – Niet gevonden), de aangevraagde URL (localhost/foo), de aanvraagmethode (GET), het IP-adres dat de aanvraag doet, en het tijdstempel voor wanneer de aanvraag werd gedaan.

Conclusie

In deze zelfstudie hebt u een eenvoudige Node.js-webtoepassing gebouwd en een Winston-logboekoplossing geïntegreerd die zal fungeren als een effectief instrument om inzicht te bieden in de prestaties van de toepassing.

Je kunt veel meer doen om robuuste logging-oplossingen voor je applicaties te bouwen, vooral als je behoeften complexer worden. Voor meer informatie over Winston transports, zie Winston Transports Documentatie. Om je eigen transports te maken, zie Aangepaste Transports Toevoegen. Om een HTTP-eindpunt te maken voor gebruik met de HTTP-kerntransport, zie winstond. Om Winston als profileringsinstrument te gebruiken, zie Profileren.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-winston-to-log-node-js-applications-on-ubuntu-20-04