日志记录

Asciidoctor.js 中的所有警告和错误消息都通过日志记录器进行处理。与输入文档相关的消息还会包括有关源位置的上下文信息(如文件名、文件目录、文件路径、行号),这对集成和工具开发非常有用。

使用 API 时,您可以将自定义日志记录器传递给 LoggerManager,以捕获、路由或观察这些消息。

日志记录器

默认日志记录器

默认的日志记录器会将所有警告和错误消息输出到 stderr(标准错误输出流)。

根据您的 JavaScript 环境,stderr 将解析为:

  • 在浏览器环境中是 console.warn 函数

  • 在 Node.js 环境中是 process.stderr 属性

内存日志记录器

除了默认日志记录器外,Asciidoctor.js 还提供了一个内存日志记录器 MemoryLogger。这个日志记录器不会将消息输出到 stderr,而是将其存储在内存中。

首先,您需要使用 create 函数实例化该日志记录器:

const memoryLogger = asciidoctor.MemoryLogger.create()

一旦实例化了日志记录器,您需要告诉 Asciidoctor 处理器使用它:

asciidoctor.LoggerManager.setLogger(memoryLogger)

上述代码将用内存日志记录器替换默认的日志记录器。

错误和警告消息

内存日志记录器会存储 Asciidoctor.js 处理器生成的每条警告和错误消息。处理完成后,您可以使用 getMessages 函数获取所有这些消息:

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

asciidoctor.convert('input')

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

对于每条消息,您可以获取以下信息:

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 返回严重性(ERROR 或 WARNING)
2 返回错误或警告的文本消息
3 返回源位置的上下文(可能为 undefined)
4 返回与消息关联的源代码行号
5 返回与消息关联的文件名(如果是从字符串转换,则为 undefined)
6 返回源文件父目录的绝对路径,或者当从字符串转换时为执行路径
7 返回与消息关联的路径(如果是从字符串转换,则为 <stdin>

日志记录器实例

Asciidoctor.js 处理器只能配置一个日志记录器。如果您想在替换后恢复默认的日志记录器,应该保存一个引用:

const loggerManager = asciidoctor.LoggerManager
const defaultLogger = loggerManager.getLogger() (1)
try {
  const memoryLogger = asciidoctor.MemoryLogger.create()
  loggerManager.setLogger(memoryLogger) (2)
  // convert a document then do something with the in-memory logger
} finally {
  loggerManager.setLogger(defaultLogger) (3)
}
1 保存默认日志器
2 用内存日志器替换默认日志器
3 恢复默认日志器

自定义日志记录器

在本节中,我们将说明如何使用流行的日志库 Winston 来替换默认的日志记录器。要实例化一个新的日志记录器,我们可以使用 LoggerManagernewLogger 函数:

const winston = require('winston') (1)
const winstonLogger = asciidoctor.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 = asciidoctor.LoggerSeverity.get(severity).toLowerCase() (2)
    logger.log({
      level: level,
      message: message.getText()
    }) (3)
  }
})
1 导入 Winston 库(必须安装 winston 包:npm install winston
2 将严重性(数字)转换为日志级别(如 'error', 'warn' 等)
3 将消息发送到 Winston 日志器

日志记录器格式化器

默认日志记录器格式化器

默认的格式化器将以以下人类可读的格式输出消息:

asciidoctor: ${severity}: ${message}

消息可以包含关于源位置的上下文信息。

以下是一个使用默认格式化器的示例:

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

自定义日志记录器格式化器

在本节中,我们将演示如何替换默认格式化器,以将消息输出为 JSON 格式。为此,我们将使用 newFormatter 函数实例化一个新的格式化器,并使用 setFormatter 函数替换日志记录器的默认格式化器:

const loggerManager = asciidoctor.LoggerManager
const defaultLogger = loggerManager.getLogger()
const jsonFormatter = asciidoctor.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)

结果如下:

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