跳到主要内容

Logger - 日志记录

日志记录是调试程序的重要手段,EasyBot 提供了简单的日志记录功能,该接口是对 Serilog 的简单封装。

logger 命名空间

info()

记录信息级别日志。

语法:

  • logger.info(message: string): void
  • logger.info(message: string, ...args: any[]): void

参数:

  • message - 日志消息或格式化字符串
  • args - 格式化参数(可选)

示例:

// 简单信息日志
logger.info("插件已启动");

// 带格式化参数的日志
logger.info("用户 {0} 发送了消息: {1}", "张三", "Hello World");

// 使用变量
const userName = "李四";
const messageCount = 5;
logger.info("用户 {0} 今天发送了 {1} 条消息", userName, messageCount);

warning()

记录警告级别日志。

语法:

  • logger.warning(message: string): void
  • logger.warning(message: string, ...args: any[]): void

参数:

  • message - 日志消息或格式化字符串
  • args - 格式化参数(可选)

示例:

// 简单警告日志
logger.warning("配置文件缺少某些选项,使用默认值");

// 带格式化参数的警告
logger.warning("用户 {0} 尝试执行未授权操作: {1}", "恶意用户", "删除文件");

// 性能警告
const responseTime = 5000;
logger.warning("API响应时间过长: {0}ms", responseTime);

error()

记录错误级别日志。

语法:

  • logger.error(message: string): void
  • logger.error(message: string, ...args: any[]): void

参数:

  • message - 日志消息或格式化字符串
  • args - 格式化参数(可选)

示例:

// 简单错误日志
logger.error("数据库连接失败");

// 带格式化参数的错误
logger.error("处理用户 {0} 的请求时发生错误: {1}", "user123", "网络超时");

// 异常处理中的错误日志
try {
// 一些可能出错的操作
const result = await someAsyncOperation();
} catch (error) {
logger.error("操作失败: {0}", error.message);
}

日志级别说明

Info(信息)

  • 用途: 记录程序正常运行的信息
  • 场景: 启动消息、用户操作、状态变更等
  • 颜色: 通常为白色或蓝色

Warning(警告)

  • 用途: 记录可能的问题或异常情况
  • 场景: 配置问题、性能警告、用户权限不足等
  • 颜色: 通常为黄色

Error(错误)

  • 用途: 记录严重错误和异常
  • 场景: 系统错误、网络故障、数据处理失败等
  • 颜色: 通常为红色

格式化字符串

EasyBot 的日志系统支持 .NET 风格的字符串格式化:

// 使用 {0}, {1}, {2} 等占位符
logger.info("用户 {0} 在群 {1} 中发送了 {2} 条消息", userName, groupId, count);

// 占位符按参数顺序对应
logger.warning("操作 {0} 耗时 {1}ms,超过阈值 {2}ms", operation, time, threshold);

实用示例

插件生命周期日志

// 插件启动
logger.info("EasyBot插件 v1.0.0 正在启动...");

// 配置加载
logger.info("正在加载配置文件: {0}", configPath);

// 初始化完成
logger.info("插件初始化完成,监听 {0} 个事件", eventCount);

// 插件停止
logger.info("插件正在停止...");

用户操作日志

bus.on("group_message_event", (event) => {
logger.info("群 {0} 中用户 {1} 发送消息: {2}",
event.PeerId,
event.SenderName,
event.RawMessage
);

if (event.RawMessage.startsWith("/")) {
logger.info("用户 {0} 执行命令: {1}", event.SenderName, event.RawMessage);
}
});

错误处理日志

bus.on("group_message_event", async (event) => {
try {
if (event.RawMessage === "/weather") {
const weather = await getWeatherData();
logger.info("成功获取天气数据");
event.Context.Reply(new MessageChain().Text(weather));
}
} catch (error) {
logger.error("获取天气数据失败: {0}", error.message);
event.Context.Reply(new MessageChain().Text("天气服务暂时不可用"));
}
});

性能监控日志

async function processUserRequest(userId, request) {
const startTime = Date.now();
logger.info("开始处理用户 {0} 的请求: {1}", userId, request);

try {
const result = await handleRequest(request);
const duration = Date.now() - startTime;

if (duration > 1000) {
logger.warning("请求处理耗时过长: {0}ms", duration);
} else {
logger.info("请求处理完成,耗时: {0}ms", duration);
}

return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.error("请求处理失败,耗时: {0}ms,错误: {1}", duration, error.message);
throw error;
}
}

数据库操作日志

// 数据查询
async function getUserData(userId) {
logger.info("查询用户数据: {0}", userId);

try {
const userData = await db.GetPlayerByUuid(userId);
if (userData) {
logger.info("成功获取用户 {0} 的数据", userId);
} else {
logger.warning("用户 {0} 的数据不存在", userId);
}
return userData;
} catch (error) {
logger.error("查询用户 {0} 数据失败: {1}", userId, error.message);
throw error;
}
}

// 数据更新
async function updateUserScore(userId, newScore) {
logger.info("更新用户 {0} 积分: {1}", userId, newScore);

try {
await db.UpdatePlayer(userId, { score: newScore });
logger.info("用户 {0} 积分更新成功", userId);
} catch (error) {
logger.error("更新用户 {0} 积分失败: {1}", userId, error.message);
throw error;
}
}

定时任务日志

// 定期清理任务
setInterval(() => {
logger.info("开始执行定期清理任务");

try {
const cleanedCount = cleanupExpiredData();
logger.info("清理任务完成,清理了 {0} 条过期数据", cleanedCount);
} catch (error) {
logger.error("清理任务执行失败: {0}", error.message);
}
}, 3600000); // 每小时执行一次

网络请求日志

async function fetchApiData(url) {
logger.info("发起API请求: {0}", url);

try {
const response = await network.get(url);
logger.info("API请求成功,状态码: {0}", response.status);
return response.data;
} catch (error) {
logger.error("API请求失败: {0},错误: {1}", url, error.message);
throw error;
}
}

最佳实践

1. 合理使用日志级别

// ✅ 正确使用
logger.info("用户登录成功"); // 正常信息
logger.warning("配置项缺失,使用默认值"); // 潜在问题
logger.error("数据库连接失败"); // 严重错误

// ❌ 错误使用
logger.error("用户发送了消息"); // 这不是错误
logger.info("系统崩溃"); // 这应该是错误

2. 提供足够的上下文信息

// ✅ 好的日志
logger.error("用户 {0} 在群 {1} 中执行命令 {2} 失败: {3}",
userId, groupId, command, error.message);

// ❌ 不好的日志
logger.error("命令执行失败");

3. 避免敏感信息

// ✅ 安全的日志
logger.info("用户 {0} 登录成功", userId);

// ❌ 不安全的日志
logger.info("用户 {0} 使用密码 {1} 登录", userId, password);

4. 使用格式化字符串

// ✅ 推荐方式
logger.info("处理了 {0} 个请求,耗时 {1}ms", count, duration);

// ❌ 不推荐方式
logger.info("处理了 " + count + " 个请求,耗时 " + duration + "ms");

5. 在关键位置添加日志

function criticalOperation() {
logger.info("开始执行关键操作");

try {
// 关键操作代码
logger.info("关键操作执行成功");
} catch (error) {
logger.error("关键操作失败: {0}", error.message);
throw error;
}
}

注意事项

  1. 性能影响: 过多的日志会影响性能,特别是在高频操作中
  2. 日志文件大小: 注意日志文件的大小,避免占用过多磁盘空间
  3. 敏感信息: 不要在日志中记录密码、令牌等敏感信息
  4. 格式化参数: 确保格式化参数的数量与占位符匹配
  5. 异常处理: 在 catch 块中记录错误日志有助于调试