Skip to content

A formatter for the winston logger compatible with Elastic Common Schema (ECS) πŸš€

License

Notifications You must be signed in to change notification settings

wnqueiroz/ecs-winston

Repository files navigation

@wnqueiroz/ecs-winston

A formatter for the winston logger compatible with Elastic Common Schema (ECS) πŸš€

Table Of Contents

Key Features ✨

  • ECS version 1.12 support.
  • IntelliSense of ECS fields in writing logs.
  • Automatic field conversion when using Express req and res. 😍
  • Overwrite and omit the values of the ECS fields that you find necessary.

Install

With NPM:

npm install @wnqueiroz/ecs-winston

Or using Yarn:

yarn add @wnqueiroz/ecs-winston

πŸ’‘ Make sure you are using the winston@3.

Usage

Basic Usage

import * as winston from 'winston'
import { ecsWinstonFormat, ECSLog, LogMetadata } from '@wnqueiroz/ecs-winston'

const logger = winston.createLogger({
  level: 'info',
  format: ecsWinstonFormat(),
  transports: [new winston.transports.Console()],
})

const ecs: ECSLog = {
  // IntelliSense here 😍
  labels: {
    application: 'foo',
  },
  tags: ['api'],
}

const log: LogMetadata = {
  ecs,
}

logger.info(log)
logger.info('This is amazing!!', log)
# logger.info(log)
{"@timestamp":"2021-10-08T20:56:37.643Z","labels":{"application":"foo"},"message":"","tags":["api"],"log":{"level":"info"},"ecs":{"version":"1.12.0"}}

# logger.info('This is amazing!!', log)
{"@timestamp":"2021-10-08T20:56:37.649Z","labels":{"application":"foo"},"message":"This is amazing!!","tags":["api"],"log":{"level":"info"},"ecs":{"version":"1.12.0"}}

Converting Express req and res

Let's configure a simple express application that, when finalizing a HTTP Request, an access log will be created:

import { ecsWinstonFormat, LogMetadata } from '@wnqueiroz/ecs-winston'
import * as winston from 'winston'
import express, { Request, Response } from 'express'

const app = express()

const logger = winston.createLogger({
  level: 'info',
  format: ecsWinstonFormat(),
  transports: [new winston.transports.Console()],
})

app.get('/', (req: Request, res: Response) => {
  const payload = {
    data: 'foo',
  }

  res.locals.body = payload // will be injected into http.response.body.content

  req.on('end', () => {
    const log: LogMetadata = {
      req,
      res,
    }

    logger.info('access log', log)
  })

  res.json(payload)
})

const port = 3000

app.listen(port, () => {
  logger.info(`app listening at http://localhost:${port}`)
})

When making the HTTP Request GET http://localhost:3000, the log displayed will look something like:

{
  "@timestamp": "2021-10-09T20:31:08.828Z",
  "message": "access log",
  "log": {
    "level": "info"
  },
  "ecs": {
    "version": "1.12.0"
  },
  "http": {
    "version": "1.1",
    "request": {
      "id": "",
      "method": "GET",
      "headers": {
        "user-agent": "Thunder Client (https://www.thunderclient.io)",
        "accept": "*/*",
        "accept-encoding": "gzip, deflate, br",
        "host": "localhost:3000",
        "connection": "close"
      },
      "body": {}
    },
    "response": {
      "status_code": 200,
      "mime_type": "application/json; charset=utf-8",
      "body": {
        "bytes": 14,
        "content": {
          "data": "foo"
        }
      }
    }
  },
  "user_agent": {
    "original": "Thunder Client (https://www.thunderclient.io)"
  }
}

Removing ECS fields from the log

It's possible to configure the formatter to exclude ECS properties by default (from all written logs):

import { ecsWinstonFormat, LogMetadata } from '@wnqueiroz/ecs-winston'
import * as winston from 'winston'

const logger = winston.createLogger({
  level: 'info',
  format: ecsWinstonFormat({
    exclude: ['http.request.headers.authorization'], // will remove this property from all logs by default.
  }),
  transports: [new winston.transports.Console()],
})

Or delete the property from that log only:

const log: LogMetadata = {
  ecs: {
    message: 'exclude example',
    labels: {
      application: 'foo', // will be removed
      foo: 'bar',
    },
  },
  exclude: ['labels.application'],
}

logger.info(log)
// {"@timestamp":"2021-10-11T14:40:35.163Z","labels":{"foo":"bar"},"message":"exclude example","log":{"level":"info"},"ecs":{"version":"1.12.0"}}

πŸ’‘ It is possible to combine the two strategies: excluding properties by default (at configuration) and from a specific log.

Roadmap 🚧

License

This software is licensed under the MIT license.