简述
本次主要针对ros_log_sink.h
和ros_log_sin.cc
文件进行讲解。在这个类中主要完成了对于Google glog
消息的封装,使用ROS自带的日志消息输出宏来输出使用Google glog
中消息宏接受的消息。关于Google glog
的详细介绍请参考我的另外一篇文章google glog使用指南。
ros_log_sink.h
#ifndef CARTOGRAPHER_ROS_CARTOGRAPHER_ROS_ROS_LOG_SINK_H
#define CARTOGRAPHER_ROS_CARTOGRAPHER_ROS_ROS_LOG_SINK_H
#include <ctime>
#include "glog/logging.h"
namespace cartographer_ros {
// Makes Google logging use ROS logging for output while an instance of this
// class exists.
/**
* @brief 自定义的输出日志的方式: 使用ROS_INFO进行glog消息的输出
*/
class ScopedRosLogSink : public ::google::LogSink {
public:
ScopedRosLogSink();
~ScopedRosLogSink() override;
void send(::google::LogSeverity severity, const char* filename,
const char* base_filename, int line, const struct std::tm* tm_time,
const char* message, size_t message_len) override;
void WaitTillSent() override;
private:
bool will_die_;
};
} // namespace cartographer_ros
#endif // CARTOGRAPHER_ROS_CARTOGRAPHER_ROS_ROS_LOG_SINK_H
这里主要是定义了ScopedRosLogSink
类,他继承于::google::LogSink
类,这个类的源代码的注释如下:
用于将日志发送到其他类型的目标
用户应该继承 LogSink 并覆盖 send 以执行任何他们想做的事情。
实现必须是线程安全的,因为将从运行 LOG(XXX) 行的任何线程调用共享实例。
ros_log_sink.cc
在这个文件中对头文件中重载的两个函数send
和WaitTillSent
进行了实现,首先定义了一个GetBasename
用于在给定文件路径的情况下获取文件的基础文件名。
/**
* @brief 根据给定的文件全路径名, 获取文件名
*
* @param[in] filepath
* @return const char* 返回文件名
*/
const char* GetBasename(const char* filepath) {
// 找到 '/' 最后一次在filepath中出现的位置
const char* base = std::strrchr(filepath, '/');
// 找到'/',就将'/'之后的字符串返回;找不到'/', 就将整个filepath返回
return base ? (base + 1) : filepath;
}
} // namespace
strchr
这个函数主要是为了寻找一个字符ch
在给定字符串str
中最后一次出现的位置,如果找到,则返回指向ch
的指针,如果找不到则返 回NULL
接下来是构造和析构函数
/**
* @brief 在构造函数中调用AddLogSink(), 将ScopedRosLogSink类注册到glog中
*/
ScopedRosLogSink::ScopedRosLogSink() : will_die_(false) { AddLogSink(this); }
ScopedRosLogSink::~ScopedRosLogSink() { RemoveLogSink(this); }
这里的
AddLogSink
和RemoveLogSink
是glog
中实现的函数,主要的功能是添加或移除一个LogSink
作为日志数据的使用者。 同时这个函数是线程安全的。直观理解就是将日志数据转发给ScopedRosLogSink
类。
之后就是这个类要重载的核心函数send
/**
* @brief 重载了send()方法, 使用ROS_INFO进行glog消息的输出
*
* @param[in] severity 消息级别
* @param[in] filename 全路径文件名
* @param[in] base_filename 文件名
* @param[in] line 消息所在的文件行数
* @param[in] tm_time 消息的时间
* @param[in] message 消息数据本体
* @param[in] message_len 消息长度
*/
void ScopedRosLogSink::send(const ::google::LogSeverity severity,
const char* const filename,
const char* const base_filename,
const int line,
const struct std::tm* const tm_time,
const char* const message,
const size_t message_len) {
const std::string message_string = ::google::LogSink::ToString(
severity, GetBasename(filename), line, tm_time, message, message_len);
switch (severity) {
case ::google::GLOG_INFO:
ROS_INFO_STREAM(message_string);
break;
case ::google::GLOG_WARNING:
ROS_WARN_STREAM(message_string);
break;
case ::google::GLOG_ERROR:
ROS_ERROR_STREAM(message_string);
break;
case ::google::GLOG_FATAL:
ROS_FATAL_STREAM(message_string);
will_die_ = true;
break;
}
}
这里的
::google::LogSink::ToString
函数主要是将输入的glog
日志数据整合成为一个普通的文本字符串消息用于输出。
这个函数的主要目的就是将传入的日志数据整合成一个普通字符串并根据日志的
严重性等级
调用对应的ROS
日志宏进行输出。
最后是一个重载基类的函数WaitTillSent
// WaitTillSent()会在每次send后调用, 用于一些异步写的场景
void ScopedRosLogSink::WaitTillSent() {
if (will_die_) {
// Give ROS some time to actually publish our message.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
我们可以在源文件中找到对他的描述
重新定义这个来实现等待接收器的日志逻辑来完成。它将在每次 send() 返回后调用,但在 LogMessage 退出或崩溃之前。默认情况下,此函数不执行任何操作。
配合这个说明以及只有在FATAL
级别的消息的情况情况下才将will_die_
设置为true
以执行休眠,我们可以推断出这个函数的主要目的是在接收到FATAL
级别的消息后等候一段时间,以便ROS
消息可以在程序崩溃之前发送出去。