功能:
一、将loggger和ctx作为pair放到步书写器AsyncLogWrite的list中:List<std::pair<LogContextPtr, Logger *> > _pending;
1.InfoL << "测试std::cout风格打印:";
#define InfoL WriteL(::toolkit::LInfo)
--->#define WriteL(level) ::toolkit::LogContextCapture(::toolkit::getLogger(), level, __FILE__, __FUNCTION__, __LINE__)
实例化LogContextCapture:
参数:::toolkit::getLogger():全局获取Logger对象
level:传递进来的值:LInfo
__FILE__, __FUNCTION__, __LINE__:系统自带的默认值
-->LogContextCapture(Logger &logger, LogLevel level, const char *file, const char *function, int line, const char *flag = "");
调用构造函数:同时构造LogContextCapture和LogContextPtr(ctx,是LogContextCapture的内部私有成员-全局)两个对象:
LogContextCapture::
LogContextCapture(Logger &logger, LogLevel level, const char *file, const char *function, int line, const char *flag) :
_ctx(new LogContext(level, file, function, line, s_module_name.c_str(), flag)), _logger(logger) {
}
--> 需要构造LogContext:new LogContext(level, file, function, line, s_module_name.c_str(), flag)
将data输出到_ctx(LogContext)
【2.将ostream(现在_ctx 输出<<)】
LogContextCapture &LogContextCapture::operator<<(ostream &(*f)(ostream &))
if (!_ctx) {
return *this;
}
###3.关键代码:调用了_logger.write
_logger.write(_ctx);-->3.关键代码:调用了_logger.write
_ctx.reset();
return *this;
}
--> ###3.1 关键代码:调用异步书写器的write函数
void Logger::write(const LogContextPtr &ctx) {
if (_writer) {
_writer->write(ctx, *this); 3.1 关键代码:调用异步书写器的write函数
} else {
writeChannels(ctx);
}
}
-->###3.1.1 将loggger和ctx作为pair放到异步书写器AsyncLogWrite的list中,此时还在list中
_writer->write(ctx, *this);
--->void AsyncLogWriter::write(const LogContextPtr &ctx, Logger &logger) {
{
lock_guard<mutex> lock(_mutex);
/*list从尾部插入元素*/
_pending.emplace_back(std::make_pair(ctx, &logger));3.1.1 将loggger和ctx作为pair放到异步书写器的list中:List<std::pair<LogContextPtr, Logger *> > _pending;
}
_sem.post();
}
至此已经 将loggger和ctx作为pair放到异步书写器AsyncLogWriter的_pending(list)中,等待AsyncLogWriter::flushAll()
其中ctx(LogContext的智能指针):包含了日志要输出的data
// liu 实际的日志的内容
std::string _content;
------
二、等待flush
异步书写器开辟了线程:
两种情况下会flushAll()
构造函数:
AsyncLogWriter::AsyncLogWriter() : _exit_flag(false) {
_thread = std::make_shared<thread>([this]() { this->run(); });
}
情况1.没有遇到_exit_flag 为true情况下,会一直flushAll,默认构造AsyncLogWriter时_exit_flag是false
void AsyncLogWriter::run() {
setThreadName("async log");
while (!_exit_flag) {
_sem.wait();
flushAll();
}
}
情况2.析构调用时:
遇到_exit_flag 为true情况下,
会在AsyncLogWriter的析构函数中设置_exit_flag为true,并且后面调用flushAll,输出最后缓存中的内容;
AsyncLogWriter::~AsyncLogWriter() {
_exit_flag = true;
_sem.post();
_thread->join();
flushAll();
}
===============================
void AsyncLogWriter::flushAll() {
// liu 获取对象或表达式的类型
decltype(_pending) tmp;
{
lock_guard<mutex> lock(_mutex);
/**
*liu 注意交换后迭代器与引用保持与原来的元素关联,
*/
tmp.swap(_pending);
}
// 挨个遍历List的中的pair,依次调用logger.writeChannels函数,LogContextPrt(_ctx) 是参数,其中包含了log的日志数据data,放到了osstringstream的stringbuff中;
tmp.for_each([&](std::pair<LogContextPtr, Logger *> &pr) {
pr.second->writeChannels(pr.first);
});
}
作者重新封装了List
template<typename FUNC>
void for_each(FUNC &&func) {
for (auto &t : *this) {
func(t);
}
}
============================
void Logger::writeChannels(const LogContextPtr &ctx) {
if (ctx->_line == _last_log->_line && ctx->_file == _last_log->_file && ctx->str() == _last_log->str()) {
//重复的日志每隔500ms打印一次,过滤频繁的重复日志
++_last_log->_repeat;
if (timevalDiff(_last_log->_tv, ctx->_tv) > 500) {
ctx->_repeat = _last_log->_repeat;
writeChannels_l(ctx);
}
return;
}
if (_last_log->_repeat) {
writeChannels_l(_last_log);
}
writeChannels_l(ctx);
}
继续调用writeChannels_l:
void Logger::writeChannels_l(const LogContextPtr &ctx) {
for (auto &chn : _channels) {
//关键:调用channels的write函数
chn.second->write(*this, ctx);
}
_last_log = ctx;
_last_log->_repeat = 0;
}
实际是:【a.1】logger->writeChannels
---【a.1.1】调用_channels->write
实际是:LogChannel->write
实际是logger的成员变量_channels(std::map<std::string, std::shared_ptr<LogChannel> > _channels;)
父类LogChannel调用write,多态转换为ConsoleChannel
实际是:
--【a.1.1.1】void ConsoleChannel::write(const Logger &logger, const LogContextPtr &ctx) {
=============================
继续解析:
void ConsoleChannel::write(const Logger &logger, const LogContextPtr &ctx) {
if (_level > ctx->_level) {
return;
}
......
//linux/windows日志启用颜色并显示日志详情
format(logger, std::cout, ctx);
#endif
}
接着调用:
format(logger, std::cout, ctx);
/**
*
* 函数调用:
* InfoL << "测试std::cout风格打印:";
* 打印的信息:
* 2023-12-16 15:18:30.658 I [test_logger.exe] [1188-14240] test_logger.cpp:40 main | 测试std::cout风格打印:
*
* @param logger
* @param ost
* @param ctx
* @param enable_color
* @param enable_detail
*/
void LogChannel::format(const Logger &logger, ostream &ost, const LogContextPtr &ctx, bool enable_color, bool enable_detail) {
if (!enable_detail && ctx->str().empty()) {
// 没有任何信息打印
return;
}
if (enable_color) {
// color console start
#ifdef _WIN32
SetConsoleColor(LOG_CONST_TABLE[ctx->_level][1]);
#else
ost << LOG_CONST_TABLE[ctx->_level][1];
#endif
}
// print log time and level
#ifdef _WIN32
ost << printTime(ctx->_tv) << " " << (char)LOG_CONST_TABLE[ctx->_level][2] << " ";
#else
ost << printTime(ctx->_tv) << " " << LOG_CONST_TABLE[ctx->_level][2] << " ";
#endif
if (enable_detail) {
// tag or process name
ost << "[" << (!ctx->_flag.empty() ? ctx->_flag : logger.getName()) << "] ";
// pid and thread_name
ost << "[" << printf_pid() << "-" << ctx->_thread_name << "] ";
// source file location
ost << ctx->_file << ":" << ctx->_line << " " << ctx->_function << " | ";
}
// log content
// liu 输出实际的log的内容“测试std::cout风格打印:” 到ost的缓存
ost << ctx->str();
if (enable_color) {
// color console end
#ifdef _WIN32
SetConsoleColor(CLEAR_COLOR);
#else
ost << CLEAR_COLOR;
#endif
}
if (ctx->_repeat > 1) {
// log repeated
ost << "\r\n Last message repeated " << ctx->_repeat << " times";
}
// flush log and new line
ost << endl;
}
类图
标签:分析,const,log,level,ctx,write,testlogger,logger From: https://www.cnblogs.com/Oztaking/p/17904958.html