8. 日志 Log
autolink 日志分两层:应用宏(autolink/common/log.hpp)与 异步落盘(autolink/logger/)。Init() 在 init.cpp 中初始化 glog、安装 AsyncLogger,并将写盘线程注册为 Scheduler 内部线程 async_log。对标 ROS 2 RCLCPP_*。
本文 §8 |
相关文档 |
|---|---|
宏、落盘、绑核 |
源码 |
职责 |
|---|---|
|
|
|
双缓冲异步写盘(生产路径) |
|
按模块名分文件的同步 Logger(测试 / 定制) |
|
单模块日志文件、滚动 |
|
从日志行解析 |
|
|
8.1 日志架构
AsyncLogger::Write 入队即返回;写盘由 Scheduler 线程 async_log 批量完成
模块名解析:宏在消息头写入 [module];FindModuleName()(logger_util.hpp)解析后路由到对应 LogFileObject。默认 MODULE_NAME 为 autolink::binary::GetName()(Init(argv[0]) 时设为二进制名)。
落盘路径(AsyncLogger::FlushBuffer):
$GLOG_log_dir/{module}.log.INFO.{timestamp}.{pid}
未设置 GLOG_log_dir 时写入当前工作目录;同时可通过 GLOG_stderrthreshold 复制到终端。
与原生 glog 差异:WARNING 及以上不再同步 flush 到磁盘,而由 async_log 线程批量写入;FATAL 仍会刷缓冲后退出。缓冲区满时生产者可能阻塞,防止内存失控(见 async_logger.hpp 注释)。
8.2 宏
autolink/common/log.hpp:AINFO 等级别宏、[MODULE_NAME] 前缀、RETURN_IF_* 早返回守卫。
#include "autolink/autolink.hpp" // 间接包含 log.hpp
AINFO << "PlannerServer started.";
AWARN << "service may not ready.";
AERROR << "CreateService failed.";
ADEBUG << "debug detail"; // VLOG(4),需 GLOG_v≥4
终端一行格式:I{date} {time} {pid} {file}:{line}] [module] message。
宏 |
glog |
用途 |
|---|---|---|
|
VLOG(4) |
调试;默认不可见 |
|
INFO |
状态变更、低频事件 |
|
WARNING |
可恢复异常 |
|
ERROR |
错误;须伴随 |
|
FATAL |
不可恢复;终止进程 |
|
— |
条件打印 |
|
LOG_EVERY_N |
每 n 次打印(回调采样) |
|
CHECK |
断言失败 abort |
守卫宏(log.hpp,早返回并打 AWARN):
宏 |
行为 |
|---|---|
|
|
|
同上,返回 |
|
条件成立 → AWARN + return |
|
同上,返回 |
自定义模块名:在 #include "autolink/common/log.hpp" 之前定义:
#define MODULE_NAME "planning"
#include "autolink/common/log.hpp"
Autonomy 应用层等价封装见 autonomy/common/logging.hpp(默认 MODULE_NAME "autonomy"),Server 代码可与 autolink 宏混用。
8.3 使用案例
Binary:Talker 稳频发布
autolink/examples/cpp/talker.cpp — 循环内记录发送序号:
autolink::Init(argv[0]);
// ...
while (autolink::OK()) {
talker->Write(msg);
AINFO << "talker sent a message! No. " << seq;
++seq;
rate.Sleep();
}
Channel:Listener 回调
listener.cpp — 每条消息 INFO(仅示例;生产环境应采样,见 §8.6):
void MessageCallback(const std::shared_ptr<Chatter>& msg) {
AINFO << "Received message seq-> " << msg->seq();
}
Service:创建失败
service.cpp:
if (server == nullptr || client == nullptr) {
AERROR << "failed to create service/client.";
return 1;
}
Action:超时与拒绝
action_talker.cpp:
opts.goal_response_callback = [](std::shared_ptr<GoalHandle> gh) {
if (!gh) AWARN << "Goal rejected by server";
};
// ...
if (accepted_future.wait_for(kAcceptTimeout) == std::future_status::timeout) {
AERROR << "Timeout waiting for goal acceptance";
return 1;
}
Record:读包失败
record.cpp:
if (!reader.ReadMessage(&msg)) {
AERROR << "read msg[" << i << "] failed";
}
查看落盘文件
export GLOG_log_dir=/tmp/autolink_logs
./autolink_example_talker &
./autolink_example_listener
ls /tmp/autolink_logs/ # autolink_example_talker.log.INFO.* 等
8.4 GLOG 配置
变量 / Flag |
作用 |
|---|---|
|
日志文件目录( |
|
VLOG 级别; |
|
最低级别: |
|
复制到 stderr 的最低级别 |
|
单文件上限(MB),默认见 glog |
export GLOG_log_dir=/tmp/autolink_logs
export GLOG_v=4
export GLOG_stderrthreshold=2 # ERROR 及以上同时打终端
./autolink_example_talker
8.5 async_log 线程
Init() 内(init.cpp):
async_logger = new logger::AsyncLogger(google::base::GetLogger(FLAGS_minloglevel));
google::base::SetLogger(FLAGS_minloglevel, async_logger);
async_logger->Start();
scheduler::Instance()->SetInnerThreadAttr("async_log", async_logger->LogThread());
Scheduler 配置中为 async_log 单独绑核,避免磁盘 I/O 与 Reader / Proc 抢 CPU(§12 classic):
threads: [{
name: "async_log"
cpuset: "1"
policy: "SCHED_OTHER"
prio: 0
}]
8.6 回调内日志
Reader 回调与 Component::Proc 运行在 Scheduler 线程;无条件高频 AINFO 会拉长回调、引发队列堆积(§3 · §12)。
场景 |
建议 |
|---|---|
传感器 / 控制环 |
|
可恢复错误 |
|
不可恢复 |
|
录包联调 |
日志时间与消息 |
// 不推荐:每帧 INFO(listener 示例仅用于学习)
void OnChatter(const std::shared_ptr<Chatter>& msg) {
AINFO << "seq=" << msg->seq();
}
// 推荐
AINFO_EVERY(100) << "chatter stream alive";
if (msg->seq() == 0) AWARN << "unexpected seq=0";
导航:← §7 插件 Plugin · §0 指南 · §9 启动 Launch →