Skip to content

Logger

ListrLogger is a common interface that enables the renderers to have a certain output format.

ListrLogger is used for every renderer to a certain degree. ListrLogger is a middleware between the console outputs of the renderers and handles the output streams stdout and stderr through ProcessOutput, and it might take full control of those streams while the renderer is rendering to avoid something else trying to write to the terminal. ListrLogger also handles the styling and formatting of to be dumped data to the terminal depending on their level, which includes the colors and styling.

Log Levels

Log levels for the ListrLogger are dynamically injected while creating an instance of ListrLogger and will affect the styling section of the instance.

By default, ListrLogLevels is used for text-based renderers. For renderers like DefaultRenderer styling require more cases than usual compared to the text-based renderers, therefore custom log levels ListrDefaultRendererListrLogLevels are injected.

Style

The style of ListrLogger can be customized through ListrLoggerOptions. Since every renderer in some form is using ListrLogger this functionality can be used to customize the renderers directly without implementing your renderer.

Icons and Colors

v6.0.0 #613

ListrLogger can be customized for any renderer through the exposed fields on your renderer inside the respective renderer options that use the ListrLogger.

The icon and color section of the supported renderer is in the form of ListrLoggerStyleMap. By injecting new style options into the ListrLogger you can change the icons and colors of every possible task.

Code Example
ts
import { color, Listr, ListrDefaultRendererLogLevels } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: (ctx, task): void => {
        task.output = 'Some data output.'
      },
      rendererOptions: { persistentOutput: true }
    }
  ],
  {
    rendererOptions: {
      icon: {
        [ListrDefaultRendererLogLevels.COMPLETED]: 'hey completed!'
      },
      color: {
        [ListrDefaultRendererLogLevels.COMPLETED]: (data): string => color.bgGreen(color.black(data)),
        [ListrDefaultRendererLogLevels.OUTPUT]: color.cyan
      }
    }
  }
)

await tasks.run()

Fields

v6.0.0

ListrLogger can have fields for each entry in the form of prefixes and suffixes.

Please refer to the presets section for how to use this with the renderers.

Presets

v6.0.0

The Preset mechanism is used for displaying additional data like timestamps or timers. This also gives flexibility to making things dynamic with a conditional display of fields or conditional styling.

Different renderers support different presets, depending on whether it fits in the style of the selected renderer. Presets are not directly tied with the ListrLogger itself but it is mostly leveraging the mechanism to have fields in the sense of prefixes and suffixes in the logging entry.

You can either pass the predefined preset to a given field or override a preset's behavior by overwriting the fields of the preset since it is designed as an object.

Preset Timer

#646

This preset can be used to show how much time has elapsed for a given thing to happen.

This preset is available for the DefaultRenderer, VerboseRenderer, and SimpleRenderer on both Listr and Task level options.

Code Example
ts
import type { LoggerFormat } from 'listr2'
import { delay, color, Listr, PRESET_TIMER } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(500)
      }
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(2000)
      }
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(100)
      }
    }
  ],
  {
    concurrent: true,
    rendererOptions: {
      timer: {
        ...PRESET_TIMER,
        condition: (duration): boolean => duration > 250,
        format: (duration): LoggerFormat => {
          if (duration > 1000) {
            return color.red
          }

          return color.green
        }
      }
    }
  }
)

await tasks.run()

Preset Timestamp

This preset can be used to stamp log entries with the current timestamp.

This preset is available for the VerboseRenderer, SimpleRenderer on Listr level options.

Code Example
ts
import { Listr, PRESET_TIMESTAMP, delay } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(500)
      }
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(2000)
      }
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {
        await delay(100)
      }
    }
  ],
  {
    concurrent: true,
    renderer: 'simple',
    rendererOptions: {
      timestamp: PRESET_TIMESTAMP
    }
  }
)

await tasks.run()

Custom Logger

You can use your custom ListrLogger whenever the underlying renderer is using it directly. This argument is always true for the provided renderers that output to the console.

To do this you must expand the original ListrLogger implementation since it is again a stateful class.

Code Example
ts
import type { ListrLogLevels, LoggerFormat } from 'listr2'
import { Listr, ListrLogger, PRESET_TIMESTAMP, color } from 'listr2'

class MyLogger extends ListrLogger<ListrLogLevels> {
  constructor (useIcons: boolean) {
    super({ useIcons, fields: { suffix: [ { field: 'task', format: (): LoggerFormat => color.magenta } ] } })
  }

  public wrap (message: string, options?: { format?: LoggerFormat }): string {
    message = `|${message}|`

    if (options?.format) {
      return options.format(message)
    }

    return message
  }
}

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {}
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {}
    },

    {
      title: 'This task will execute.',
      task: async (): Promise<void> => {}
    }
  ],
  {
    concurrent: true,
    renderer: 'simple',
    rendererOptions: {
      timestamp: PRESET_TIMESTAMP,
      logger: new MyLogger(false)
    }
  }
)

await tasks.run()

After a couple of attempts with v6.0.0, this turned out to be the most flexible solution for modifying the logger, where it is shared with multiple different implementations of renderers, and they do not use it the same way.

WARNING

Some options for the ListrLogger are forcefully injected through the selected renderer. These are the options like global field options like timestamp or icon and style options, due to me wanting to expose these options without creating a custom logger since this falls into the more advanced use cases.