Introducción
Una solución de registro eficaz es crucial para el éxito de cualquier aplicación. En esta guía, nos centraremos en un paquete de registro llamado Winston, una biblioteca de registro extremadamente versátil y la solución de registro más popular disponible para aplicaciones Node.js, basada en estadísticas de descarga de NPM. Las características de Winston incluyen compatibilidad con múltiples opciones de almacenamiento y niveles de registro, consultas de registro e incluso un generador de perfiles integrado. Este tutorial le mostrará cómo usar Winston para registrar una aplicación Node/Express que crearemos como parte de este proceso. También veremos cómo podemos combinar Winston con otro registrador de middleware de solicitudes HTTP popular para Node.js llamado Morgan para consolidar los registros de datos de solicitudes HTTP con otra información.
Después de completar este tutorial, tendrá un servidor Ubuntu ejecutando una pequeña aplicación Node/Express. También habrá implementado Winston para registrar errores y mensajes en un archivo y en la consola.
Prerrequisitos
Antes de comenzar esta guía necesitarás lo siguiente:
-
Un servidor Ubuntu 16.04 configurado siguiendo la guía de configuración inicial del servidor Ubuntu 16.04, incluido un usuario sudo no root y un firewall.
-
Node.js se instaló usando el PPA oficial, como se explica en Cómo instalar Node.js en Ubuntu 16.04.
Con estos requisitos previos establecidos, podemos crear nuestra aplicación e instalar Winston.
Paso 1: Creación de una aplicación básica de Node/Express
Un uso común de Winston es el registro de eventos de aplicaciones web creadas con Node.js. Para demostrar completamente cómo incorporar Winston, crearemos una aplicación web Node.js simple utilizando el marco Express. Para ayudarnos a poner en funcionamiento una aplicación web básica, utilizaremos express-generator
, una herramienta de línea de comandos para poner en funcionamiento rápidamente una aplicación web Node/Express. Debido a que instalamos el Node Package Manager como parte de nuestros requisitos previos, podremos utilizar el npm
comando para instalar express-generator
. También utilizaremos el -g
indicador , que instala el paquete globalmente para que pueda utilizarse como una herramienta de línea de comandos fuera de un proyecto/módulo Node existente. Instale el paquete con el siguiente comando:
- sudo npm install express-generator -g
Una vez express-generator
instalado, podemos crear nuestra aplicación mediante el express
comando, seguido del nombre del directorio que queremos utilizar para nuestro proyecto. Esto creará nuestra aplicación con todo lo que necesitamos para comenzar:
- express myApp
A continuación, instala Nodemon, que recargará automáticamente la aplicación cada vez que hagamos algún cambio. Una aplicación Node.js debe reiniciarse cada vez que se realizan cambios en el código fuente para que dichos cambios surtan efecto. Nodemon observará automáticamente los cambios y reiniciará la aplicación por nosotros. Y como queremos poder usarlo nodemon
como una herramienta de línea de comandos, lo instalaremos con el -g
indicador:
- sudo npm install nodemon -g
Para finalizar la configuración de la aplicación, cambie al directorio de la aplicación e instale las dependencias de la siguiente manera:
- cd myApp
- npm install
De forma predeterminada, las aplicaciones creadas con express-generator
se ejecutan en el puerto 3000, por lo que debemos asegurarnos de que el firewall no bloquee ese puerto. Para abrir el puerto 3000, ejecute el siguiente comando:
- sudo ufw allow 3000
Ahora tenemos todo lo que necesitamos para iniciar nuestra aplicación web. Para ello, ejecute el siguiente comando:
- nodemon bin/www
Esto inicia la ejecución de la aplicación en el puerto 3000. Podemos comprobar que funciona accediendo a ella desde un navegador web. Debería ver algo como esto:http://your_server_ip:3000
En este punto, es una buena idea iniciar una segunda sesión SSH en el servidor para utilizarla durante el resto de este tutorial, dejando la aplicación web que acabamos de iniciar en ejecución en la sesión original. En el resto de este artículo, nos referiremos a la sesión SSH que hemos estado utilizando hasta ahora y que actualmente ejecuta la aplicación como Sesión A. Utilizaremos la nueva sesión SSH para ejecutar comandos y editar archivos, y nos referiremos a esta sesión como Sesión B. A menos que se indique lo contrario, todos los comandos restantes deben ejecutarse en la Sesión B.
Paso 2: Personalización de la aplicación Node.js
La aplicación predeterminada creada por express-generator
hace un gran trabajo para ayudarnos a comenzar, e incluso incluye el middleware de registro HTTP de Morgan que usaremos para registrar datos sobre todas las solicitudes HTTP. Y dado que Morgan admite flujos de salida, es una buena combinación con el soporte de flujo integrado en Winston, lo que nos permite consolidar los registros de datos de solicitudes HTTP con cualquier otra cosa que elijamos registrar con Winston.
De forma predeterminada, el express-generator
código repetitivo utiliza la variable logger al hacer referencia al morgan
paquete. Dado que utilizaremos morgan
y winston
, que son paquetes de registro, puede resultar confuso llamar a cualquiera de ellos logger . Por lo tanto, cambiemos eso editando el app.js
archivo en la raíz del proyecto y realizando algunos cambios.
Para abrir app.js
y editar, utilice el nano
comando:
- nano ~/myApp/app.js
Encuentre la siguiente línea cerca de la parte superior del archivo:
~/miAplicación/app.js
...var logger = require('morgan');...
Cámbielo a lo siguiente:
~/miAplicación/app.js
...var morgan = require('morgan');...
También necesitamos encontrar dónde se hizo referencia a la variable logger en el archivo y cambiarla a morgan
. Mientras estamos en ello, cambiemos el formato de registro utilizado por el morgan
paquete a combined
, que es el formato de registro estándar de Apache e incluirá información útil en los registros, como la dirección IP remota y el encabezado de solicitud HTTP del agente de usuario.
Para ello, busque la siguiente línea:
~/miAplicación/app.js
...app.use(logger('dev'));...
Cámbielo a lo siguiente:
~/miAplicación/app.js
...app.use(morgan('combined'));...
Estos cambios nos ayudarán a comprender mejor a qué paquete de registro estamos haciendo referencia en cualquier momento después de integrar nuestra configuración de Winston.
Salga y guarde el archivo escribiendo CTRL-X
, luego Y
, y luego ENTER
.
Ahora que nuestra aplicación está configurada, estamos listos para comenzar a trabajar con Winston.
Paso 3: Instalación y configuración de Winston
Ahora estamos listos para instalar y configurar Winston. En este paso, exploraremos algunas de las opciones de configuración que están disponibles como parte del winston
paquete y crearemos un registrador que registrará información en un archivo y en la consola.
Para instalar, winston
ejecute el siguiente comando:
- cd ~/myApp
- npm install winston
A menudo es útil mantener cualquier tipo de archivo de configuración de soporte o utilidad para nuestras aplicaciones en un directorio especial, así que creemos una config
carpeta que contendrá la winston
configuración:
- mkdir ~/myApp/config
Ahora vamos a crear el archivo que contendrá nuestra winston
configuración, al que llamaremos winston.js
:
- touch ~/myApp/config/winston.js
A continuación, cree una carpeta que contendrá sus archivos de registro:
- mkdir ~/myApp/logs
Por último, instalemos app-root-path
, un paquete que resulta útil para especificar rutas en Node.js. Este paquete no está relacionado directamente con Winston, pero resulta de gran ayuda para especificar rutas a archivos en el código de Node.js. Lo utilizaremos para especificar la ubicación de los archivos de registro de Winston desde la raíz del proyecto y evitar la desagradable sintaxis de ruta relativa:
- npm install app-root-path --save
Ya tenemos todo lo que necesitamos para configurar cómo queremos gestionar nuestro registro, así que podemos pasar a definir nuestra configuración. Comience abriendo ~/myApp/config/winston.js
para editar:
- nano ~/myApp/config/winston.js
A continuación, se requieren los paquetes app-root-path
y winston
:
~/miAplicación/config/winston.js
var appRoot = require('app-root-path');var winston = require('winston');
Con estas variables en su lugar, podemos definir los ajustes de configuración para nuestros transportes. Los transportes son un concepto introducido por Winston que se refiere a los mecanismos de almacenamiento/salida utilizados para los registros. Winston viene con tres transportes principales: consola, archivo y HTTP. En este tutorial, nos centraremos en los transportes de consola y archivo: el transporte de consola registrará información en la consola y el transporte de archivo registrará información en un archivo específico. Cada definición de transporte puede contener sus propios ajustes de configuración, como tamaño de archivo, niveles de registro y formato de registro. A continuación, se incluye un breve resumen de los ajustes que utilizaremos para cada transporte:
- nivel – Nivel de mensajes a registrar.
- nombre_de_archivo : el archivo que se utilizará para escribir los datos del registro.
- handleExceptions – Captura y registra excepciones no controladas.
- json – Registra datos de registro en formato JSON.
- maxsize – Tamaño máximo del archivo de registro, en bytes, antes de que se cree un nuevo archivo.
- maxFiles : limita la cantidad de archivos creados cuando se excede el tamaño del archivo de registro.
- colorize – Colorea la salida. Esto puede resultar útil al consultar los registros de la consola.
Los niveles de registro indican la prioridad del mensaje y se denotan con un número entero. Winston utiliza npm
niveles de registro que se priorizan de 0 a 5 (de mayor a menor):
- 0 : error
- 1 : advertir
- 2 : información
- 3 : verboso
- 4 : depuración
- 5 : tonto
Al especificar un nivel de registro para un transporte en particular, se registrará todo lo que se encuentre en ese nivel o en un nivel superior. Por ejemplo, al especificar un nivel de info
, se registrará todo lo que se encuentre en el nivel error
, warn
o info
. Los niveles de registro se especifican al llamar al registrador, lo que significa que podemos hacer lo siguiente para registrar un error: logger.error('test error message')
.
Podemos definir los ajustes de configuración para los transportes file
y console
en la winston
configuración de la siguiente manera:
~/miAplicación/config/winston.js
...var options = { file: { level: 'info', filename: `${appRoot}/logs/app.log`, handleExceptions: true, json: true, maxsize: 5242880, // 5MB maxFiles: 5, colorize: false, }, console: { level: 'debug', handleExceptions: true, json: false, colorize: true, },};
A continuación, cree una instancia de un nuevo winston
registrador con transportes de archivos y consola utilizando las propiedades definidas en la options
variable:
~/miAplicación/config/winston.js
...var logger = new winston.Logger({ transports: [ new winston.transports.File(options.file), new winston.transports.Console(options.console) ], exitOnError: false, // do not exit on handled exceptions});
De manera predeterminada, morgan
solo se envían los resultados a la consola, por lo que definamos una función de flujo que pueda obtener morgan
los resultados generados en los winston
archivos de registro. Usaremos el info
nivel para que ambos transportes (archivo y consola) recojan los resultados:
~/miAplicación/config/winston.js
...logger.stream = { write: function(message, encoding) { logger.info(message); },};
Por último, exporte el registrador para que pueda usarse en otras partes de la aplicación:
~/miAplicación/config/winston.js
...module.exports = logger;
El winston
archivo de configuración completo debería verse así:
~/miAplicación/config/winston.js
var appRoot = require('app-root-path');var winston = require('winston');// define the custom settings for each transport (file, console)var options = { file: { level: 'info', filename: `${appRoot}/logs/app.log`, handleExceptions: true, json: true, maxsize: 5242880, // 5MB maxFiles: 5, colorize: false, }, console: { level: 'debug', handleExceptions: true, json: false, colorize: true, },};// instantiate a new Winston Logger with the settings defined abovevar logger = new winston.Logger({ transports: [ new winston.transports.File(options.file), new winston.transports.Console(options.console) ], exitOnError: false, // do not exit on handled exceptions});// create a stream object with a 'write' function that will be used by `morgan`logger.stream = { write: function(message, encoding) { // use the 'info' log level so the output will be picked up by both transports (file and console) logger.info(message); },};module.exports = logger;
Salir y guardar el archivo.
Ya tenemos configurado nuestro registrador, pero nuestra aplicación aún no lo reconoce ni sabe cómo utilizarlo. Ahora vamos a integrar el registrador con la aplicación.
Paso 4: Integración de Winston con nuestra aplicación
Para que nuestro registrador funcione con la aplicación, debemos informarle express
. Ya vimos en el paso 2 que nuestra express
configuración se encuentra en app.js
, así que importemos nuestro registrador a este archivo. Abra el archivo para editarlo ejecutando:
- nano ~/myApp/app.js
Importe winston
cerca de la parte superior del archivo con las otras declaraciones requeridas:
~/miAplicación/app.js
...var winston = require('./config/winston');...
El primer lugar en el que realmente lo usaremos winston
es con morgan
. Usaremos la stream
opción y la configuraremos en la interfaz de transmisión que creamos como parte de la winston
configuración. Para ello, busque la siguiente línea:
~/miAplicación/app.js
...app.use(morgan('combined'));...
Cámbialo a esto:
~/miAplicación/app.js
...app.use(morgan('combined', { stream: winston.stream }));...
Salir y guardar el archivo.
¡Estamos listos para ver algunos datos de registro! Si vuelves a cargar la página en el navegador web, deberías ver algo similar a lo siguiente en la consola de la sesión SSH A:
Output[nodemon] restarting due to changes...[nodemon] starting `node bin/www`info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
Aquí hay dos entradas de registro: la primera para la solicitud a la página HTML y la segunda para la hoja de estilo que la acompaña. Dado que cada transporte está configurado para manejar info
datos de registro de nivel, también deberíamos ver información similar en el archivo de transporte ubicado en ~/myApp/logs/app.log
. Sin embargo, la salida en el archivo de transporte debería escribirse como un objeto JSON, ya que lo especificamos json: true
en la configuración del archivo de transporte. Puede obtener más información sobre JSON en nuestro tutorial de introducción a JSON. Para ver el contenido del archivo de registro, ejecute el siguiente comando:
- tail ~/myApp/logs/app.log
Deberías ver algo similar a lo siguiente:
{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] "GET / HTTP/1.1"" 304 - ""-"" ""Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTMLNo related posts.