This documentation covers a prerelease version of the software. Follow this link to view the documentation for the stable version (3.0) instead.

Logging

All warning and error messages in Asciidoctor.js are routed through a logger. Messages related to the input document also include context about the source location (file name, file directory, file path, line number), which can be useful for integrations and tooling.

When using the API, you can feed your own logger to the LoggerManager to capture, route, or observe the messages.

Logger

Default logger

The default logger will output all warning and error messages to stderr (the standard error output stream).

Depending on your JavaScript environment, stderr will be resolved to:

In-memory logger

In addition to the default logger, Asciidoctor.js provides a built-in in-memory logger named MemoryLogger. This logger won’t output messages to stderr but instead they will be stored in-memory.

Import and instantiate the logger using the create function:

import { MemoryLogger } from '@asciidoctor/core'

const memoryLogger = MemoryLogger.create()

Once the logger is instantiated, tell the processor to use it via LoggerManager:

import { LoggerManager } from '@asciidoctor/core'

LoggerManager.setLogger(memoryLogger)

The above code will effectively replace the default logger with the in-memory logger.

Error and warning messages

The in-memory logger stores every warning and error message generated by the processor. Once processing is done, retrieve all messages using the getMessages function:

import { MemoryLogger, LoggerManager, convert } from '@asciidoctor/core'

const memoryLogger = MemoryLogger.create()
LoggerManager.setLogger(memoryLogger)

await convert('input')

memoryLogger.getMessages() // returns an array of Message

For every message, you can get the following information:

const message = memoryLogger.getMessages()[0]
console.log(message.getSeverity()) (1)
console.log(message.getText()) (2)
const sourceLocation = message.getSourceLocation() (3)
if (sourceLocation) {
  console.log(sourceLocation.getLineNumber()) (4)
  console.log(sourceLocation.getFile()) (5)
  console.log(sourceLocation.getDirectory()) (6)
  console.log(sourceLocation.getPath()) (7)
}
1 returns the severity (e.g. 'ERROR', 'WARN', 'INFO')
2 returns the error or warning text message
3 returns the context about the source location (can be undefined)
4 returns the source line number associated to the message
5 returns the file name associated to the message (or undefined when converting from a String)
6 returns the absolute path to the source file parent directory, or the execution path when converting from a String
7 returns the path associated to the message (or '<stdin>' when converting from a String)

Logger instances

The Asciidoctor.js processor can only have one logger configured. If you want to restore the default logger after replacing it, save a reference first:

import { MemoryLogger, LoggerManager, convert } from '@asciidoctor/core'

const defaultLogger = LoggerManager.getLogger() (1)
try {
  const memoryLogger = MemoryLogger.create()
  LoggerManager.setLogger(memoryLogger) (2)
  // convert a document then do something with the in-memory logger
} finally {
  LoggerManager.setLogger(defaultLogger) (3)
}
1 save the default logger
2 replace the default logger with the in-memory logger
3 restore the default logger

Custom logger

In this section, we will explain how to replace the default logger with the popular logging library Winston. To instantiate a new logger, use the newLogger function on the LoggerManager:

import winston from 'winston' (1)
import { LoggerManager } from '@asciidoctor/core'

const winstonLogger = LoggerManager.newLogger('WinstonLogger', {
  postConstruct: function () {
    this.logger = winston.createLogger({
      level: 'warning',
      format: winston.format.json(),
      transports: [
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
      ]
    })
  },
  add: function (severity, _, message) {
    const level = String(severity).toLowerCase() (2)
    this.logger.log({
      level: level,
      message: message.getText()
    }) (3)
  }
})
1 Import the winston library (winston package must be installed: npm install winston)
2 Convert the severity value to a logger level string ('error', 'warn'…​)
3 Send messages to the Winston logger

Logger formatter

Default logger formatter

The default formatter will output messages using the following human readable format:

asciidoctor: ${severity}: ${message}
The message can include context about the source location.

Here’s an example using the default formatter:

asciidoctor: ERROR: <stdin>: line 8: invalid part, must have at least one section (e.g., chapter, appendix, etc.)

Custom Logger formatter

In this section, we will demonstrate how to replace the default formatter to output messages as JSON. Use the newFormatter function to instantiate a new formatter, then setFormatter to apply it to a logger:

import { LoggerManager } from '@asciidoctor/core'

const defaultLogger = LoggerManager.getLogger()
const jsonFormatter = LoggerManager.newFormatter('JsonFormatter', {
  call: function (severity, time, programName, message) {
    const text = message['text']
    const sourceLocation = message['source_location']
    return JSON.stringify({
      programName: programName,
      message: text,
      sourceLocation: {
        lineNumber: sourceLocation.getLineNumber(),
        path: sourceLocation.getPath()
      },
      severity: severity
    }) + '\n'
  }
})
defaultLogger.setFormatter(jsonFormatter)

Here’s the result:

{"programName":"asciidoctor","message":"invalid part, must have at least one section (e.g., chapter, appendix, etc.)","sourceLocation":{"lineNumber":8,"path":"<stdin>"},"severity":"ERROR"}