quant-beat

**`Quant Beat`** is a simple logger that wraps around [lugg](https://github.com/aexmachina/lugg "lugg") which is in itself, a simple wrapper around [bunyan](https://github.com/trentm/node-bunyan "bunyan") (a powerful logging framework). **`Quant Beat`**

Usage no npm install needed!

<script type="module">
  import quantBeat from 'https://cdn.skypack.dev/quant-beat';
</script>

README

Quant Beat Logger

Quant Beat is a simple logger that wraps around lugg which is in itself, a simple wrapper around bunyan (a powerful logging framework). Quant Beat adds additional super powers to lugg and in effect bunyan by providing Mapper Diagnostic Context (MDC) features using continuation local storage.

According to the documentation of continuation local storage, "Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads. The standard Node convention of functions calling functions is very similar to something called "continuation-passing style" in functional programming, and the name comes from the way this module allows you to set and get values that are scoped to the lifetime of these chains of function calls".

In simple terms, the continuation local storage provides a storage area (i.e. a map) that allows asynchronous calls to store data in the map and to retrieve that data at some later point in another asynchronous call as though the data was added in the local context of the retrieving the asynchronous call. As an example, anything that could be put on the req object so that it can be retrieved later can and should be put in the continuation local storage to stop the req object being passed around unnecessarily or being passed to every method that needs the data that was put on the req object

The continuation local storage allows us to do some very powerful things in logging in an asynchronous system like node. In quant-beat, continuation local storage is used for among other things, storing a traceId that is added to the response headers (as X-TraceId header) if you are using the quant-beat express middleware. The traceId can be used to trace requests through distributed systems such as microservices which perform distributed logging

Install

npm install quant-beat

Usage

Usage

Normal Usage

Note

quant beat depends on lugg which takes inspiration from debug but is not dependent on debug hence if you want to see logs at debug or trace level, you must set the DEBUG environment variable to the name of your application e.g. DEBUG=MYAPP

In the example usage below of the quant-beat logger, traceId will be automatically added to all log lines

'use strict'
const Logger = require('quant-beat').logger
// Notethe we specify the mainMethod option in the options object as we are not using the express middleware
const logger = new Logger({mainMethod: myMainMethod})

// this will automatically add traceId to the log entry if it does not exist already exits
// If traceId exists, it will be used in subseguent log entries as though the traceId was created 
// in the subsequent async call

const logPoint1 = () => {
  // traceId will be created here and added to the log line if does not exist  
  logger.info('some debug message at log point 1') 
   return Promise.resolve("some logging at log point 1")
}

const logPoint2 = () => {
  // traceId will be created here and added to the log line if does not exist  
  logger.info('some debug message at log point 2') 
   return Promise.resolve("some logging at log point 2")
}

const myMainMethod = () => {
    // traceId will be created here and added to the log line if does not exist
    logPoint1()
    // The traceId created at log point 1 will be used here automatically and added to the log line
    .then(logPoint2)
}

Throwing and logging exception

One of the most annoying things (for me at least) about logging is dealing with throwing and logging exception. In most cases, when exceptions are thrown, the exception should be logged before throwing. In most cases, developers do something like below

'use strict'
const Logger = require('quant-beat').logger
const logger = new Logger()

// this will automatically add traceId to the log entry if it does not exist already

const log  = () => {
  try {
      const err = new TypeError("dude!! this is the wrong  type")
      logger.error(err)
      throw err
  } catch (e) {
    // ...
   }
}

With quant-beat, the boiler plate has been made simpler. The code below will log and throw the the error passed to it. traceId will also be automatically added to all log lines

'use strict'
const Logger = require('quant-beat').logger
const logger = new Logger({mainMethod: log})

// this will automatically add traceId to the log entry if it does not exist already

const log  = () => {
      const err = new TypeError("dude!! this is the wrong  type")
      // traceId will also be automatically added to all log lines
      logger.throwing(err)
}

Express Usage

To use the express middleware, simply use loggerExpress middleware of quant-beat as described below The X-TraceId header will be automatically added to the response header. traceId will also be automatically added to all log lines

//service-1.js
const logPoint1 = () => {
  logger.info('some debug message at log point 1') 
   return Promise.resolve("some logging at log point 1")
}

module.exports = logPoint1

//service-2.js

const logPoint2 = () => {
  logger.info('some info message at log point 1') 
   return Promise.resolve("some logging at log point 1")
}

module.exports = logPoint2

// controller.js
'use strict'
const express = require('express')
const router = express()
const middleware = require('quant-beat').loggerExpress
const Logger = require('quant-beat').logger
const service1 = require('./service-1')
const service2 = require('./service-2')

const logger = new Logger({useUuidAsTraceId: true}) // Will set the Java Spring Cloud Sleuth headers`X-B3-TraceId` 

router.use(middleware(logger))

router.get('/hello', function (req, res) {
    
  // traceId will be created here and added to the log line if does not exist  
  service1.logPoint1()
  
  // The traceId created at log point 1 will be used in log point 2 automatically and added to the log line
    .then(service2.logPoint2)
    .then(() => {
      
    // The traceId created at log point 1 will be used here automatically  and to the log line  
      logger.info('logging finished')
      
    // The traceId created at log point 1 will be automatically added to the response headers as
    // 'X-TraceId' when the 'finish', 'close' or 'end' events are called on res (response) object
      res.send('world')
    })
  
})

module.exports = router

Quant Beat API

constructor([options])

  • options.mainMethod - This is main method / entry point method of an application. This is required if you want to use the MDC / continuation local storage in a an application / module that does not use the express middleware (i.e this is optional even in a non-express context because the logger can still be used without the MDC but the traceId will not be automatically propagated). In such applications / modules,
    quant-beat uses the main method specified by options.mainMethod to determine the root of the continuation local storage context. This mainMethod option should not be set if you are using the express middleware

  • options.mdcNamespace - The namespce name that is used when the continuation local storage is created

  • options.name - The name that is passed to lugg and in effect bunyan

  • {boolean} options.useBunyanFormat - if true, uses bunyan-format module to format the output

  • options.bunyanFormatOpts - The options that are passed to bunyan-format module if {boolean} options.useBunyanFormat is true

  • options.useUuid4AsTraceId - if true UUIDV4 will be used as the traceId else otherweise a hexadecimal of length 19 will be generated and used if using the mdc

See bunyan and lugg for more information about options

throwing(Error)

Logs and throws the supplied exception / error

getMdc

Returns the MDC (i.e. continuation local storage) associated with the current call chain. Users can add their own data to this MDC i.e (continuation local storage)

For example

'use strict'
const Logger = require('quant-beat').logger
const logger = new Logger({mainMethod: myMainMethod})

// this will automatically add traceId to the log entry if it does not exist already

const myMainMethod  = () => {
      const mdc = logger.getMdc()
      mdc.set('someKey', 'someValue')
      someMethodThatEventuallyCallsTestMethod()
}

const testMethod = () => {
  const someKeyValue =  logger.getMdc().get('someKey') 
  logger.info('someKey: %s', someKeyValue) 
  return Promise.resolve("retrieved someKeyValue from mdc")
}

const someMethodThatEventuallyCallsTestMethod = () => {
    // Doing lots of processing...
    // This method or one of the methods that someMethodThatEventuallyCallsTestMethod calls can retrieve the value of someKey in from the cls / mdc
    
    return testMethod()
    
}

getLoggingFramework

Returns the underlying logging framework (bunyan in this case). The underlying framework may change in the future but that should not affect the quant-beat interface

Methods trace, debug, info, warn, error, fatal

Calling any of the Level methods will add traceId to the MDC i.e (continuation local storage) associated with logger in the current call chain

See bunyan and lugg for more information

Express Middleware API

options.setSpringCloudSleuthHeaders - if true, the Java spring cloud sleuth headers X-B3-TraceId will be set