Formatters
The primary purpose of Adze is to give the user simple ways of shaping their logs to fit their needs. Adze comes with four formatters out of the box.
Formatter | Value | Description |
---|---|---|
Pretty | "pretty" | The default formatter. This prints logs in a pretty, human-readable format. |
JSON | "json" | This formatter prints logs as machine-readable JSON objects that are compatible with the Bunyan CLI. |
Standard | "standard" | This formatter prints human-readable logs for a simple stdout format for terminals or files. |
Common | "common" | This formatter prints logs according to the Common Log Format. |
Pretty Formatter
The Pretty formatter is the default formatter used by Adze. It formats logs in a pretty, human-readable manner.
To use it, set the User Configuration format
value to be "pretty"
.
import adze, { setup } from 'adze';
setup({
format: 'pretty',
});
Browser and Server Output
JSON Formatter
The JSON formatter is used to generate machine-readable JSON logs. The logs that are generated are compatible with the Bunyan CLI for parsing logs to be human-readable or for filtering them based on their values.
To use it, set the User Configuration format
value to be "json"
.
import adze, { setup } from 'adze';
setup({
format: 'json',
});
JsonLogFormatMeta Interface
JSON formatted logs require some meta data to be compatible with the Bunyan JSON Schema. To apply these values you must include them when calling the meta modifier or apply them in the setup function.
Usage Example
To get TS type checking for these values, Adze exports an interface called JsonLogFormatMeta
that can be passed to the meta modifier as a generic type.
The minimum required fields are name
and hostname
.
import adze, { setup, type JsonLogFormatMeta } from 'adze';
setup({
level: 'debug',
format: 'json',
});
const logger = adze.meta<JsonLogFormatMeta>({ name: 'myApp', hostname: 'localhost' }).seal();
export default logger;
// --- OR ALTERNATIVELY
setup<JsonLogFormatMeta>({
level: 'debug',
format: 'json',
meta: {
name: 'myApp',
hostname: 'localhost',
},
});
If you want to later apply extra optional JSON log meta fields, you can use the JsonLogOptionalFields
interface.
// ./elsewhere.ts
import { type JsonLogOptionalFields } from 'adze';
import logger from './logger';
logger.meta<JsonLogOptionalFields>({ latency: 400 }).debug('Logging the latency of our app.');
Interface
interface JsonLogFormatMeta {
/**
* The name of the application or logger that is generating the log.
*/
name: string;
/**
* The hostname of the machine that generated the log.
*/
hostname: string;
/**
* The name of the log level.
*/
levelName?: string;
/**
* Optional. Object giving log call source info. This is added automatically by Bunyan if the
* "src: true" config option is given to the Logger. Never use in production as this is really
* slow.
*/
src?: string;
/**
* A caught JS exception. This will be added anytime adze detects an Error object in the log
* terminator arguments. You may also manually serialize an error with the `serializeError`
* function from adze.
*/
err?: JsonLogError;
/**
* A request identifier. Including this field in all logging tied to handling a particular request
* to your server is strongly suggested. This allows post analysis of logs to easily collate all
* related logging for a request. This really shines when you have a SOA with multiple services
* and you carry a single request ID from the top API down through all APIs.
*/
req_id?: string;
/**
* An HTTP server request. This can be generated with the `serializeRequest` function from adze.
*/
req?: JsonLogHttpRequest;
/**
* An HTTP server response. This can be generated with the `serializeResponse` function from adze.
*/
res?: JsonLogHttpResponse;
/**
* The latency of the logged request in milliseconds.
*/
latency?: number;
/**
* Any additional meta data that you want to include in the log.
*/
meta?: Record<string, unknown>;
}
/**
* Type for a JSON Log error on the `err` property.
*/
export interface JsonLogError {
message: string;
name: string;
stack?: string;
}
/**
* Type for declaring a JSON log HTTP request object.
*
* This can be generated from the `serializeRequest` function from adze.
*/
export interface JsonLogHttpRequest {
method: HttpMethod;
url: string;
headers: Record<string, string>;
body?: string;
remoteAddress: string;
remotePort?: number;
username?: string;
}
/**
* Type for declaring a JSON log HTTP response object.
*
* This can be generated from the `serializeResponse` function from adze.
*/
export interface JsonLogHttpResponse {
statusCode: HttpStatus;
header: string;
}
JsonLogFormatMeta Serializer Functions
For the JSON log meta properties of err
, req
, and res
Adze provides a set of serializer functions to make it much simpler to apply these meta data values.
serializeError
This function accepts a JavaScript Error object and returns a serialized meta data object that is compatible with the JsonLogFormatMeta interface.
Example
import adze, { serializeError, type JsonLogOptionalFields } from 'adze';
// Doing some stuff and an error occurs!
const errMsg = 'An error occurred! UH OH!';
adze
.meta<JsonLogOptionalFields>({
err: serializeError(new Error(errMsg)),
})
.error(errMsg);
Example Output
serializeRequest
This function accepts a JavaScript Request object and returns a serialized meta data object that is compatible with the JsonLogFormatMeta interface.
Example
import adze, { serializeRequest, type JsonLogOptionalFields } from 'adze';
const request = new Request('https://example.com/login', {
method: 'POST',
headers: {
'x-hi': 'Mom',
connection: 'close',
Authorization: 'Basic ' + btoa('username:password'),
},
body: JSON.stringify({ foo: 'bar' }),
});
// The Request serializer returns a promise so it must be awaited.
adze
.meta<JsonLogOptionalFields>({
req: await serializeRequest(request),
})
.log('Made a request!');
Example Output
serializeResponse
This function accepts a JavaScript Response object and returns a serialized meta data object that is compatible with the JsonLogFormatMeta interface.
Example
import adze, { serializeResponse, type JsonLogOptionalFields } from 'adze';
// We'll make a fake response just for this example
const response = new Response('hello world!', {
status: 200,
statusText: 'OK',
headers: { boop: 'beep' },
});
Object.defineProperty(response, 'url', { value: 'https://example.com/login' });
// The Response serializer returns a promise so it must be awaited.
adze
.meta<JsonLogOptionalFields>({
res: await serializeResponse(response),
})
.log('Received a response!');
Example Output
Standard Formatter
The Standard formatter is for generating human-readable logs in a stdout environment. This is useful for writing human-readable logs to a file or for transporting them to other platforms. The Standard formatter does not require you to specify that you want timestamps as it is a requirement for all logs of this type.
To use it, set the User Configuration format
value to be "standard"
.
import adze, { setup } from 'adze';
setup({
format: 'standard',
});
adze.warn('This is a standard warn log.');
Example Output
Common Formatter
The Common formatter is for generating human-readable logs according to the Common Log Format. This is primarily used for tracking network communications on servers.
To use it, set the User Configuration format
value to be "common"
.
CommonLogFormatMeta
The Common formatter requires some extra meta data to fill in various properties within a log formatted to the Common Log standard. Adze exports the CommonLogFormatMeta
interface to assist you with filling in the necessary data. The Common formatter does not require you to specify that you want timestamps as it is a requirement for all logs of this type.
Usage Example
import adze, { setup, type CommonLogFormatMeta } from 'adze';
setup<CommonLogFormatMeta>({
format: 'common',
meta: {
hostname: 'localhost',
ident: 'user-identifier',
user: 'joe',
},
});
adze.log('This is a standard warn log.');
Interface
interface CommonLogFormatMeta {
hostname: string;
ident?: string;
user?: string;
}
Example Output
Creating Third-party Formatters
If the formatters provided by Adze do not meet your requirements, you can create a custom formatter by extending the Formatter class.
Formatter Class
The Formatter class is the base for all built-in formatters and third-party formatters. It abstracts some of the basic logic away from the formatter in regards to the visibility, setting an apex timestamp formatter override method, and routing the log to the appropriate formatter method depending on the environment.
Interface
class Formatter {
// Properties
protected cfg: Configuration;
protected level: LevelConfiguration;
protected timestampFormatFunction: (date: Date) => string;
// Constructor
constructor(cfg: Configuration, level: LevelConfiguration) {}
// Methods
public get timestampFormatter(): (date: Date) => string;
protected abstract formatBrowser(
data: ModifierData,
timestamp: string,
args: unknown[]
): unknown[];
protected abstract formatServer(
data: ModifierData,
timestamp: string,
args: unknown[]
): unknown[];
}
Properties
Property | Type | Description |
---|---|---|
cfg | Configuration | The configuration of the log that is being formatted. |
level | LevelConfiguration | The configuration for the log level that this log is being terminated as. |
timestampFormatFunction | (date: Date) => string | An optional override function for the formatting the timestamp of the formatted log. |
timestampFormatter getter
This method returns the most relevant timestampFormatter
function based on the hierarchy of the default function for formatting ISO-8601, then the timestampFormatter override from the UserConfiguration
, then the timestamp formatter function defined by the Formatter class, with the Formatter class taking the highest priority.
formatBrowser
This method is called when in a browser environment for formatting the provided log arguments to be output in a browser developer console.
This method is provided the modifier data for all modifiers applied to the log, the timestamp according to the timestampFormatter getter, and the raw arguments for the log provided by the user.
After applying formatting to the arguments, the formatBrowser
method should return the formatted arguments as an array in the same order they were provided.
Example
import { Formatter } from 'adze';
class HelloFormatter extends Formatter {
constructor(cfg: Configuration, level: LevelConfiguration) {
super(cfg, level);
}
protected formatBrowser(data: ModifierData, timestamp: string, args: unknown[]): unknown[] {
// We'll make the first arg printed always the word "Hello".
return ['Hello', ...args];
}
}
formatServer
This method is called when in a backend environment for formatting the provided log arguments to be output to the server terminal.
This method is provided the modifier data for all modifiers applied to the log, the timestamp according to the timestampFormatter getter, and the raw arguments for the log provided by the user.
After applying formatting to the arguments, the formatServer
method should return the formatted arguments as an array in the same order they were provided.
Example
import { Formatter } from 'adze';
class HelloFormatter extends Formatter {
constructor(cfg: Configuration, level: LevelConfiguration) {
super(cfg, level);
}
protected formatServer(data: ModifierData, timestamp: string, args: unknown[]): unknown[] {
// We'll make the first arg printed always the word "Hello".
return ['Hello', ...args];
}
}
Using a Third-party Formatter
To include a third-party formatter for use with Adze you must add it to the formatters
object property of the User Configuration as a key/value pair.
Example
import adze, { setup } from 'adze';
import HelloFormatter from './hello-formatter.ts';
setup({
formatters: {
hello: HelloFormatter,
},
});
// And to use the HelloFormatter, simply use the key as the value for the `format` property.
setup({
format: 'hello',
formatters: {
hello: HelloFormatter,
},
});