A standardised multi-level logger, and some specialised loggers.
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');
}
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 |
// 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(),
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 are available at logger.levels
; they are:
- error
- warn
- info
- http
- verbose
- debug
- silly
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');
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"
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
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.
import { webserverLogger, createWebserverLogger } from '@numbereight/logging';
app.use(webserverLogger);
app.use(createWebserverLogger());
The webserver logger adds RayID's and tag buckers to requests.
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.
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.
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();
});