Skip to content

A wrapper for Winston Logging Node.js library that formats the output on STDOUT as Logstash JSON format.

License

Notifications You must be signed in to change notification settings

zenvia/zenvia-logger-node

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zenvia Logger for Node.js

A wrapper for Winston Logging Node.js library that formats the output on STDOUT as Logstash JSON format.

Main features:

  • Dual Package Support: Native support for both ES Modules (ESM) and CommonJS (CJS).
  • Node 24 Ready: Optimized for native TypeScript execution (Type Stripping).
  • Distributed Tracing: Automated request tracking using cls-rtracer.

License Build Status Coverage Status Dependencies

Twitter Follow

Installation

npm install @zenvia/logger

Environment Variables

The following environment variables can be used for increase the log information:

  • APP_NAME: value to filled the "application" field in the output JSON. If empty, the name attribute on package.json will be used instead.
  • NODE_ENV: value to filled the "environment" field in the output JSON.
  • HOST or HOSTNAME: value to filled the "host" field in the output JSON.
  • LOGGING_LEVEL: set the level of messages that the logger should log. Default to DEBUG.
  • LOGGING_FORMATTER_DISABLED (version 1.1.0 and above): When true, the output logging will not be formatted to JSON. Useful during development time. Default to false.
  • LOGGING_FRAMEWORK_MIDDLEWARE (version 1.5.0 and above): Value that defines which middleware will be used. It is possible to choose between the middlewares: EXPRESS, FASTIFY, HAPI and KOA. If empty, the middleware default is EXPRESS.
  • LOGGING_TRACE_HEADER (version 1.5.0 and above): Value indicating the header name that must be obtained from the traceId value in the request. Default is X-TraceId.

Basic Usage (Express users)

// ES6 or Typescript
import express from 'express';
import logger, { traceMiddleware } from '@zenvia/logger';

const app = express();
app.use(traceMiddleware);

logger.info('some message');

Basic Usage (FASTIFY users)

// ES6 or Typescript
import fastify from 'fastify'
import logger, { traceMiddleware } from '@zenvia/logger';

fastify.register(traceMiddleware);

logger.info('some message');

Basic Usage (KOA users)

// ES6 or Typescript
import Koa from 'koa';
import logger, { traceMiddleware } from '@zenvia/logger';

const app = new Koa();
app.use(traceMiddleware);

logger.info('some message');

Basic Usage (HAPI users)

// ES6 or Typescript
import Koa from 'koa';
import logger, { traceMiddleware } from '@zenvia/logger';

const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: 'localhost'
  });

  await server.register({
    plugin: traceMiddleware
  });
}

logger.info('some message');

Output:

{
  "@timestamp": "2018-06-05T18:20:42.345Z",
  "@version": 1,
  "application": "application-name",
  "message": "some message",
  "level": "INFO",
  "traceID": "123e4567-e32b-12d3-a432-626614174888"
}

Available logging levels

The log levels are as follows.

  • fatal
  • error
  • warn
  • info
  • debug

For backward compatibility purposes, "verbose" and "silly" levels will behave the same as "debug" level.

Adding extra key/value fields

logger.debug('Some text message', { keyA: 'value A', keyB: 'value B' });

Output:

{
  "keyA": "value A",
  "keyB": "value B",
  "@timestamp": "2018-06-05T22:04:42.039Z",
  "@version": 1,
  "application": "application-name",
  "message": "Some text message",
  "level": "DEBUG"
}

Logging errors

logger.error('Ops!', new Error('Something goes wrong'));

Output:

{
  "message": "Ops!: Something goes wrong",
  "@timestamp": "2018-06-05T22:14:09.683Z",
  "@version": 1,
  "application": "application-name",
  "level": "ERROR",
  "stack_trace": "Error: Something goes wrong\n    at repl:1:34\n    at Script.runInThisContext (vm.js:91:20)\n    at REPLServer.defaultEval (repl.js:317:29)\n    at bound (domain.js:396:14)\n    at REPLServer.runBound [as eval] (domain.js:409:12)\n    at REPLServer.onLine (repl.js:615:10)\n    at REPLServer.emit (events.js:187:15)\n    at REPLServer.EventEmitter.emit (domain.js:442:20)\n    at REPLServer.Interface._onLine (readline.js:290:10)\n    at REPLServer.Interface._line (readline.js:638:8)"
}

Due to limitations of winston lib, when a text, an error and extra key/value fields are logged at once, the output message field will contain the text message, the error message and the full stack trace as shown.

logger.fatal('Ops!', { new Error('Something goes wrong'), { keyA: 'value A', keyB: 'value B' } });

Output:

{
  "keyA": "value A",
  "keyB": "value B",
  "@timestamp": "2018-06-05T22:09:22.750Z",
  "@version": 1,
  "application": "application-name",
  "message": "Ops! Error: Something goes wrong\n    at repl:1:34\n    at Script.runInThisContext (vm.js:91:20)\n    at REPLServer.defaultEval (repl.js:317:29)\n    at bound (domain.js:396:14)\n    at REPLServer.runBound [as eval] (domain.js:409:12)\n    at REPLServer.onLine (repl.js:615:10)\n    at REPLServer.emit (events.js:187:15)\n    at REPLServer.EventEmitter.emit (domain.js:442:20)\n    at REPLServer.Interface._onLine (readline.js:290:10)\n    at REPLServer.Interface._line (readline.js:638:8)",
  "level": "FATAL"
}

Using trace logs

From version 1.5.0 it is possible to track logs. To do traceability, the cls-rTrace package is used. To use it, just add the middleware in the framework you are using. In this way it is possible to propagate the traceId received in a request to the logs throughout your project. If no traceId is passed in the request, Zenvia Logger generates a random traceId for the request being processed.

Request example sending traceId:

curl 'http://localhost/your-application' \
--header 'X-TraceId: dbcdd40e-10cd-40a7-b912-1b0a17483d67' \

Log

logger.info('message with traceID');

Log Output

{
  "@timestamp": "2018-06-05T18:20:42.345Z",
  "@version": 1,
  "application": "application-name",
  "message": "message with traceID",
  "level": "INFO",
  "traceID": "dbcdd40e-10cd-40a7-b912-1b0a17483d67'"
}

Request example without sending traceId:

curl 'http://localhost/your-application'

Log

logger.info('message without traceID');

Log Output

{
  "@timestamp": "2018-06-05T18:20:42.345Z",
  "@version": 1,
  "application": "application-name",
  "message": "message with traceID",
  "level": "INFO",
  "traceID": "912c029c-c38f-49e7-9968-e575c5108178'"
}

Contextual Logging (Async Context)

You can use runWithContext and addContext to manage metadata context across asynchronous flows (like Kafka consumers or complex background jobs). This ensures all logs within a specific execution thread share the same context without passing a logger instance manually.

Basic Usage with runWithContext

Useful for isolating logs from different event sources or partitions.

import logger from '@zenvia/logger';

async function handleEvent(event) {
  await logger.runWithContext({
    partition: event.partition,
    topic: 'orders-topic'
  }, async () => {
    // All logs here will include partition and topic automatically
    logger.info('Processing started');
    await processOrder(event.data);
  });
}

Enriching Context with addContext

You can inject new metadata into the current context at any point during the execution.

import logger from '@zenvia/logger';

async function processOrder(data) {
  logger.info('Validating...');

  const provider = await api.getProvider(data.id);

  // Mutates the current context to include providerId in all subsequent logs
  logger.addContext({ providerId: provider.id });

  logger.info('Validation complete');
  // Output JSON will contain: partition, topic AND providerId
}

Nested Contexts

Contexts can be nested. The inner context will inherit metadata from the parent.

await logger.runWithContext({ level1: 'A' }, async () => {
  await logger.runWithContext({ level2: 'B' }, async () => {
    logger.info('Log with A and B');
  });
});

License

MIT

About

A wrapper for Winston Logging Node.js library that formats the output on STDOUT as Logstash JSON format.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published