日志系统
在编写机器人时,日志通常是最早会用到、也最容易被忽略的功能之一。我们可能希望知道某个插件是否已经成功加载、某个指令为什么没有触发、或者在调用外部服务失败时记录下具体的错误信息。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);
}配置 logHandler
默认情况下,Fraq 不会把日志直接输出到终端。你需要在创建 Context 时传入 logHandler,Fraq 会把所有日志消息交给这个函数:
import { Context } from '@fraqjs/fraq';
const ctx = Context.fromUrl('<替换成你的 Milky 协议端地址>', {
logHandler(message) {
console.log(`[${message.level}] [${message.module}] ${message.message}`);
},
});
ctx.logger.info('Bot is starting');
ctx.start();传给 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';
Context.create({
logHandler: createColoredLogHandler({
minLevel: 'info', // 只输出 info 及以上级别的日志
}),
});你也可以传入一些其他配置项,例如日期时间格式、不同等级的日志颜色等。值得一提的是,@fraqjs/color-log 使用 chalk 来实现日志着色,因此在配置项中你可以直接使用 chalk 支持的颜色和样式。
使用多个日志处理器
Fraq 提供了一个函数 combineLogHandlers,可以让你同时使用多个日志处理器:
import { combineLogHandlers } from '@fraqjs/fraq';
Context.create({
logHandler: combineLogHandlers(
createColoredLogHandler({ minLevel: 'info' }),
(message) => {
// 发送到远程服务器或者写入文件
},
),
});在插件中使用日志
插件中也可以直接使用 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',
{
content: param.str(),
},
(session, { content }) => {
ctx.logger.debug(`Received echo command: ${content}`);
session.reply(msg`You said: ${content}`);
},
);
},
});如果这个插件输出了一条日志,那么 logHandler 收到的 module 字段会是 echo,而不是默认的 root。这对于排查插件初始化顺序、依赖注入问题,或者区分不同功能模块的运行状态都很有帮助。