Universal Logging for Typescript/JavaScript
Getting Started
Reference Manual
Plugins
FAQ's
v1.x
GitHub
Getting Started
Reference Manual
Plugins
FAQ's
v1.x
GitHub
  • Reference Manual
    • Introduction & Lifecycle
    • Log Class
    • Data Getters
    • Modifiers
    • Terminators
    • Global Store
    • Tools
    • Configuration
    • Middleware
    • Formatters
    • Unit Testing

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.

FormatterValueDescription
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

example of browser and server output for the pretty format


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

example of a json log with a serialized error


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

example of a json log with a serialized request


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

example of a json log with a serialized response


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

example of server output for the standard format


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

example of server output for the common format


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

PropertyTypeDescription
cfgConfigurationThe configuration of the log that is being formatted.
levelLevelConfigurationThe configuration for the log level that this log is being terminated as.
timestampFormatFunction(date: Date) => stringAn 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 { Configuration, Formatter, type LevelConfiguration, type ModifierData } from 'adze';

class HelloFormatter extends Formatter {
  constructor(cfg: Configuration, level: LevelConfiguration) {
    // This constructor isn't necessary, but is here for illustrative purposes.
    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];
  }

  /*
  ...a TypeScript error will be shown because we have not yet implemented 
  the formatServer() method. We will add it in the next section.
  */
}

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 { Configuration, Formatter, type LevelConfiguration, type ModifierData } from 'adze';

class HelloFormatter extends Formatter {
  constructor(cfg: Configuration, level: LevelConfiguration) {
    super(cfg, level);
  }

  // ...formatBrowser method should be here from the previous section.

  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,
  },
});
Edit this page
Last Updated:
Contributors: Andrew Stacy, Andrew Stacy
Prev
Middleware
Next
Unit Testing