日志系统
在编写机器人时,日志通常是最早会用到、也最容易被忽略的功能之一。我们可能希望知道某个插件是否已经成功加载、某个指令为什么没有触发、或者在调用外部服务失败时记录下具体的错误信息。Fraq 在 Context 上提供了一个轻量的 logger,用来帮助你把这些信息统一地交给自己的日志系统处理。
logger 对象
ctx.logger 提供了四个常用的方法,分别对应不同的日志级别:
ctx.logger.debug('Loaded local cache');
ctx.logger.info('Bot is ready');
ctx.logger.warn('Retrying request');
ctx.logger.error('Failed to send message');对于 warn 和 error,你还可以额外传入一个错误对象或其他需要保留的错误详情:
try {
await doSomething();
} catch (error) {
ctx.logger.error('Failed to do something', error);
}订阅日志
默认情况下,Fraq 不会把日志直接输出到终端。你可以通过 ctx.logBus 订阅所有日志:
import { Context } from '@fraqjs/fraq';
const ctx = Context.fromUrl('<替换成你的 Milky 协议端地址>');
ctx.logBus.on('log', (message) => {
console.log(`[${message.level}] [${message.module}] ${message.message}`);
});
ctx.logger.info('Bot is starting');
ctx.start();logBus 在 root context 和所有子 context 之间共享。你也可以注册多个监听器,分别把日志输出到终端、文件或监控系统。
旧版本的 logHandler 选项仍然可以使用,Fraq 会自动把它注册到 logBus,但新代码建议直接使用事件订阅。
传给 logHandler 的日志消息是一个普通对象,包含这些字段:
level:日志级别,可能的值有debug、info、warn和error。module:日志来源的模块名称,默认为root,但在插件里会自动设置为插件名称。message:日志内容字符串。error:可选字段,如果日志是由一个错误对象触发的,这里会包含这个错误对象。time:日志生成的时间戳,通过Date.now()获取,单位是毫秒。
在这个例子中,我们只是简单地把日志格式化之后输出到了终端。实际项目中,你也可以在 logHandler 里把日志发送到文件、数据库、监控系统,或者传递给你已经在使用的日志库来处理。
使用 color-log
Fraq 提供了一个官方的日志处理工具 @fraqjs/color-log,它可以让你的日志在终端输出时带上颜色,方便阅读和区分不同级别的日志。你可以使用包管理器安装它。
然后在创建 Context 的时候使用它:
import { createColoredLogHandler } from '@fraqjs/color-log';
const ctx = Context.fromUrl('http://localhost:30001/');
ctx.logBus.on(
'log',
createColoredLogHandler({
minLevel: 'info', // 只输出 info 及以上级别的日志
}),
);你也可以传入一些其他配置项,例如日期时间格式、不同等级的日志颜色等。值得一提的是,@fraqjs/color-log 使用 chalk 来实现日志着色,因此在配置项中你可以直接使用 chalk 支持的颜色和样式。
在插件中使用日志
插件中也可以直接使用 ctx.logger。插件在 apply 方法中拿到的 logger 会自动使用插件的 name 作为 module,这样多个插件的日志就可以自然地区分开:
import { definePlugin } from '@fraqjs/fraq';
export const EchoPlugin = definePlugin({
name: 'echo',
apply(ctx) {
ctx.logger.info('Echo plugin loaded');
ctx.router
.command('echo')
.arg('content', param.str())
.execute((session, { content }) => {
ctx.logger.debug(`Received echo command: ${content}`);
session.reply(`You said: ${content}`);
});
},
});如果这个插件输出了一条日志,那么 logHandler 收到的 module 字段会是 echo,而不是默认的 root。这对于排查插件初始化顺序、依赖注入问题,或者区分不同功能模块的运行状态都很有帮助。