tars日志库源码分析
tars的日志库的类图关系很绕,很容易忘,记录一下备用
源码分析基于tag v3.0.0
首先介绍一下会用到的主要代码路径和功能
1 | TarsCpp/util/include/util/tc_logger.h 日志库头文件 |
taf的日志库从流程上,分初始化,滚动日志,按天日志三部分,暂时只分析了滚动日志相关逻辑
初始化
先看类图
classDiagram
direction TB
class LocalRollLogger {
TC_Logger~RollWriteT, TC_RollBySize~ _logger;
TC_LoggerThreadGroup _local;
}
LocalRollLogger --> TC_Logger
LocalRollLogger --> TC_LoggerThreadGroup
class TC_LoggerThreadGroup {
set~TC_LoggerRollPtr, KeyComp~ logger_set;
}
class TC_Logger["TC_Logger<WriteT, template<class> class RollPolicy>"] {
LoggerBuffer _buffer;
std::ostream _stream;
}
class RollWrapperBase["RollWrapperBase<TC_RollBySize<RollWriteT>>"] {
}
class RollWrapperI["RollPolicy<WriteT>::RollWrapperI"] {
+void init(const string &path) : 转发_roll->init(...)
}
class RollWrapperBase["RollWrapperBase<RollPolicy<WriteT>>"] {
RollPolicy<WriteT>> _roll;
}
TC_Logger --|> RollWrapperI
RollWrapperI --|> RollWrapperBase
TC_Logger --> LoggerStream
class TC_RollBySize["TC_RollBySize<WriteT>"] {
-WriteT _t;
+void init(const string &path)
}
class TC_RollByTime["TC_RollByTime<WriteT>"] {
-WriteT _t;
+void init(const string &path)
}
RollWrapperBase --> TC_RollBySize
RollWrapperBase --> TC_RollByTime
namespace WriteT定义写操作 {
class RollWriteT {
+void operator() 写本地
}
class TimeWriteT {
-TC_Logger~RemoteTimeWriteT, TC_RollByTime~ _pRemoteTimeLogger
+void operator() 写本地,并通过_pRemoteTimeLogger再异步写远程
}
class RemoteTimeWriteT {
+void operator() 写远程
}
}
TimeWriteT --> RemoteTimeWriteT
TimeWriteT --> TC_Logger
其中TC_Logger的实现较为复杂,通过CRTP继承了好几次,但其实这几次CRTP的继承实际上只是为了组合,而不是为了抽象接口,所以其实有个简化版理解
classDiagram
direction TB
class TC_Logger["TC_Logger<WriteT, template<class> class RollPolicy>"] {
-LoggerBuffer _buffer;
-std::ostream _stream;
-RollPolicy<WriteT>> _roll;
+void init(const string &path) : 转发_roll->init(...)
}
class TC_RollBySize["TC_RollBySize<WriteT>"] {
-WriteT _t;
+void init(const string &path)
}
class TC_RollByTime["TC_RollByTime<WriteT>"] {
-WriteT _t;
+void init(const string &path)
}
TC_Logger --> TC_RollBySize
TC_Logger --> TC_RollByTime
初始化的使用方式位于RemoteLogger.h
1 | TarsRollLogger::getInstance()->setLogInfo(ServerConfig::Application, ServerConfig::ServerName, ServerConfig::LogPath, ServerConfig::LogSize, ServerConfig::LogNum, _communicator, ServerConfig::Log); |
看下大致看下流程(可以结合时序图看)
时序图
sequenceDiagram
LocalRollLogger ->>+ TC_LoggerThreadGroup : start()<br>new thread(run, this);
TC_LoggerThreadGroup ->>- LocalRollLogger : return
participant RollLogger as TC_Logger<RollWriteT,TC_RollBySize>
participant TC_LoggerRoll as TC_RollBySize<RollWriteT>
loop run
TC_LoggerThreadGroup ->>+ TC_LoggerThreadGroup : flush()
TC_LoggerThreadGroup ->>+ RollLogger : logger_set::iterator it = _logger.begin()<br>while (it != logger.end()) {<br>it->get()->flush()<br>}
RollLogger ->>+ TC_LoggerRoll : TC_CasQueue qt<br>_buffer.swap(qt)<br>roll(qt)
TC_LoggerRoll ->>+ RollWriteT : lock(*this)<br>_t(_of, buffer)
RollWriteT ->>+ RollWriteT : 实际写文件
RollWriteT ->>- TC_LoggerRoll : return
TC_LoggerRoll ->>- RollLogger : return
RollLogger ->>- TC_LoggerThreadGroup : return
end
LocalRollLogger ->>+ LocalRollLogger : sync
LocalRollLogger ->>+ RollLogger : _logger.setupThread(&_local)
RollLogger ->>+ TC_LoggerRoll : _roll->setupThread(ltg)
TC_LoggerRoll ->>+ TC_LoggerThreadGroup : _pThreadGroup->registerLogger(self);
TC_LoggerThreadGroup ->>+ TC_LoggerThreadGroup : _logger.insert(l);
TC_LoggerRoll ->>- RollLogger : return
RollLogger ->>- LocalRollLogger : return
首先在setLogInfo中,调用
1 | _local.start(1); |
TC_LoggerThreadGroup::start开启了新线程
1 | void TC_LoggerThreadGroup::start(size_t iThreadNum) |
在新线程中,运行TC_LoggerThreadGroup::run
1 | void TC_LoggerThreadGroup::run() |
run函数中死循环定时唤醒执行TC_LoggerThreadGroup::flush函数
1 | void TC_LoggerThreadGroup::flush() |
flush函数遍历logger_set,并对每一项调用TC_LoggerRoll::flush
1 | void TC_LoggerRoll::flush() |
flush函数从_buffer里取出所有内容,调用TC_LoggerRoll::roll
对于TC_RollBySize<RollWriteT>
来说,TC_RollBySize::roll是这样的
1 | void TC_RollBySize::roll(const deque<pair<size_t, string> > &buffer) { |
回到LocalRollLogger::setLogInfo
继续执行
1 | sync(false) |
在LocalRollLogger::sync中
1 | void LocalRollLogger::sync(bool bSync) |
执行了TC_Logger<RollWriteT, TC_RollBySize>::setupThread
,它继承自
TC_RollBySize<RollWriteT>::RollWrapperI::setupThread
,而它又继承自
RollWrapperBase<TC_RollBySize<RollWriteT>>::setupThread
1 | void setupThread(TC_LoggerThreadGroup *ltg) { _roll->setupThread(ltg); } |
这里调用了TC_RollBySize<RollWriteT>::setupThread
方法,它继承自TC_LoggerRoll::setupThread
1 | void TC_LoggerRoll::setupThread(TC_LoggerThreadGroup *pThreadGroup) |
这里调用了TC_LoggerThreadGroup::registerLogger
1 | void TC_LoggerThreadGroup::registerLogger(TC_LoggerRollPtr &l) |
因此TC_LoggerThreadGroup的flush函数中可以获取注册到的TC_RollBySize<RollWriteT>
实例
小结
初始化主要是在LocalRollLogger的sync开始的流程,将TC_RollBySize<RollWriteT>
实例注册给TC_LoggerThreadGroup的flush函数用
然后在start开始的流程中,新起线程定时将TC_CasQueue的数据实际落磁盘
那么TC_CasQueue的数据来自哪里呢?
滚动日志
1 | #define LOG (LocalRollLogger::getInstance()->logger()) |
滚动日志库的使用方式是LOG宏,位于RemoteLogger.h,因此从这里切入(同样可以结合时序图来看)
时序图
sequenceDiagram
participant RollLogger as TC_Logger<RollWriteT,TC_RollBySize>
RollLogger ->>+ LoggerStream : debug()
LoggerStream ->>- RollLogger : return
LoggerStream ->>+ LoggerBuffer : LoggerStream(TC_Logger._stream)<br>operator<<<br>TC_Logger._stream.flush()<br>_buffer.sync()
LoggerBuffer ->>+ TC_RollBySize : _roll->write()
TC_RollBySize ->>+ TC_CasQueue :_buffer.push_back()
TC_CasQueue ->>- TC_RollBySize : return
TC_RollBySize ->>- LoggerBuffer : return
LoggerBuffer ->>- LoggerStream : return
首先在TC_Logger中
1 | LoggerStream debug() { return stream(DEBUG_LOG_LEVEL); } |
生成了一个临时变量LoggerStream,当LoggerStream析构的时候会_stream->flush()
1 | ~LoggerStream() |
flush中会调用LoggerBuffer::sync方法(具体调用的代码这里见调用LoggerBuffer的sync的测试的解释)
1 | int LoggerBuffer::sync() |
sync方法调用了TC_LoggerRoll::write
1 | void TC_LoggerRoll::write(const pair<std::size_t, string> &buffer) |
这里_buffer
是一个无锁队列TC_CasQueue<pair<std::size_t, string>> _buffer
小结
滚动日志通过LoggerStream的析构,将数据写入TC_CasQueue
调用LoggerBuffer的sync的测试
1 |
|
总结
日志打印时写入一个TC_CasQueue无锁队列,然后由一个线程定时落磁盘
参考资料
-
2023-11-23
Here's something encrypted, password is required to continue reading.