@numbereight/logging
TypeScript icon, indicating that this package has built-in type declarations

4.2.2 • Public • Published

@numbereight/logging

A standardised multi-level logger, and some specialised loggers.

Examples

import logger from '@numbereight/logging';

logger.info("So this is happening", { a: "23", b: 19 });
logger.info("Hey! Something likely bad is happening");
logger.info("", new Error("Its all gone wrong"));
logger.info("Failed to do X", new Error("X Failed"), { x: "rm -rf * --no-preserve-root" });

logger.warn("Hey! Something likely bad is happening");
logger.error(new Error("Its all gone wrong"));
logger.warn({ message: "Failed to do X", error: new Error("X Failed"), meta: { x: "rm -rf * --no-preserve-root" }});
import { webserverLogger } from '@numbereight/logging';
app.use(webserverLogger);
import { tag } from '@numbereight/logging';

export function handleRequest(req, res) {
  const prop = req.headers["specialProperty"];
  if(req.headers["specialProperty"]) {
    tag(req, prop);
  }
  
  await doWork(req);
  
  res.status(200).send('All done');
}

Environment Variables

Variable What does it do?
DISABLE_GLOBAL_ERROR_HANDLING Disables global error handling
DISABLE_NODE_VERSION_CHECK Disables node version check
DISABLE_LOG_SYSTEM_INFORMATION Disables logging of system information
DISABLE_WEBSERVER_SKIP_LOGGING Disables skipping of webserver logs
NODE_ENV When set to production outputs JSON log lines
LOG_FORMAT When set to json outputs JSON log lines
DESIRED_NODE_VERSION The desired node version
NVMRC_PATH Checks this path for a file containing the desired node version. Default .nvmrc
FORCE_COLOR When set to 1, forces colourised output

Input

// info
(message?: string, error?: Error, meta?: JSONishObject, req?: IncomingMessage) => void;

// every other level
(statement: string | Error | { message?: string, meta: any, error?: Error }) => void;
// String form
logger.info("Hello");

// Error form
logger.info(new Error("Hello"));
logger.info(new Error("Hello"), { a: "H" });

// Meta form
logger.info("Hello", { a: "H" });
logger.info(new Error("Hello"), { a: "H" });
logger.info("Hello", new Error("Hello"), { a: "H" });

// Ray forms
logger.info("Hello", req);
logger.info("Hello", new Error("Hello"), req);
logger.info("Hello", { a: "H" }, req);

// Promise forms
Promise.reject().catch(logger.info.reReject("Hello", { a: "H" }));

// Timing
logger.info.time(() => randomTask(), 

Output

When the environment variables NODE_ENV is set to production or LOG_FORMAT is set to json, the output will be a single line of JSON for every log statement.

Otherwise, it is in "human readable" form. In this form FORCE_COLOR can be set to 1 to ensure colours are printed.

The JSON output is structured like so:

{
  message?: string, // The message provided, or if none the message of the error, or if none, nothing.
  error?: string, // The message from any error
  req?: IncomingMessage, // The HTTP Incoming message assciated with the log
  stack?: string, // The stack trace from any error
  path?: string, // A "promise path" showing the chain of promises reaching this point
  meta?: any // Whatever is passed to the meta field
}

Levels

Levels are available at logger.levels; they are:

  • error
  • warn
  • info
  • http
  • verbose
  • debug
  • silly

Tagging

The logger can be tagged so that future calls will include the tagged data in results:

const localLogger = new Logger();
localLogger.tag('start', new Date().getTime())
localLogger.info('start time is included in tags');

Configuration

Desired version of your application can be passed to the logger by using DESIRED_NODE_VERSION. This should be done using semantic versioning.

export DESIRED_NODE_VERSION="v14.14.0"

Error handling

By default the logging package provides 3 functions listening to unexpected and unhandled exceptions. This functionality is enabled by default but can be disabled by setting DISABLE_GLOBAL_ERROR_HANDLING to true

This will provide log messages for:

  • uncaughtException
  • unhandledRejection
  • warning

Startup

When the logger is first imported, it will try to compare the version found in .nvmrc or NVMRC_PATH (if provided) with the running system version. If no file can be found or the versions are not matching, the logger automatically makes a new log line to indicate this.

WebserverLogger

import { webserverLogger, createWebserverLogger } from '@numbereight/logging';

app.use(webserverLogger);
app.use(createWebserverLogger());

The webserver logger adds RayID's and tag buckers to requests.

Ray ID's

A ray ID is a 33 character string composed of two 16 character hex strings seperated by a hyphen; e.g.

deadbeefd15ea5ed-defec8edfacefeed

The first string (deadbeefd15ea5ed in the example) is sourced from the client if the incoming request has the Ray-ID header set; if it is not set it is generated server side. The second string (defec8edfacefeed in the example) is always generated server side. The resultant Ray ID is returned to the client in the Ray-ID header.

Ray ID's are used to correlate many log calls from a single web request. Simply passing the request object to the logger will result in it being included:

import { webserverLogger } from '@numbereight/logging';
app.use(webserverLogger);
app.get('/route', (req, res) => {
  logger.info(`Example`, req);
  logger.warn(`Not implemented`, req);
});

Both of the above requests will contain the same rayID.

Tagging

The webserverLogger allows tagging of requests. When a request passes through a webserverLogger it gains the tags field, which is a mapping from string tags to JSONMember values. These values will be logged when a request completes.

import { webserverLogger } from '@numbereight/logging';
app.use(webserverLogger);
app.get('/route', webserverLogger, (req, res) => {
  logger.tagRequest(req, `FailureReason`, `Not implemented`);
  res.status(500).send();
});

Will result in the webserverLogger logging "FailureReason: Not implemented" when logging the request.

Skipping

Logging of requests can be skipped. By default, in when NODE_ENV is set to production, all responses with a status code of less than 400 are not logged.

To add conditions to skip logging:

import { createWebserverLogger } from '@numbereight/logging';
app.use(createWebserverLogger({
  skip(req, _res) {
    return req.tags[`FailureReason`] === `Not implemented`;
  }
}));
app.get('/route', (req, _res) => {
  logger.tagRequest(req, `FailureReason`, `Not implemented`);
  res.status(500).send();
});

Readme

Keywords

Package Sidebar

Install

npm i @numbereight/logging

Weekly Downloads

53

Version

4.2.2

License

MIT

Unpacked Size

184 kB

Total Files

62

Last publish

Collaborators

  • nechris