首页 > 其他分享 >testlogger分析

testlogger分析

时间:2023-12-16 16:23:20浏览次数:28  
标签:分析 const log level ctx write testlogger logger

功能:
一、将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

相关文章

  • MongoDB 7.0 分片键分析助手--analyzeShardKey()
    分片键是群集的关键组成部分,因为它决定了数据在分片中的分布。 分片集群的大部分问题都与错误的分片键选择有关;对于一个好的分片键,必须注意以下几点:·分片键的cardinality·分片键值出现的频率·潜在分片键值是否单调增长·分片查询模式在老版本中,分片键是不可变的,但现在......
  • 铺先生:门店选址需要分析这些,看看你的方向对了没
    做门店选址的时候,你分析的要素越多,你避免风险的概率就越大。选址最终的目的都是为了店铺能够更好的经营,而分析问题的意义就在于此。门店选址需要分析这些,你都知道几个呢?下面小编就来跟大家说一下吧。1. 分析商圈在门店选址的诸多因素中,商圈的影响绝对是很大的,商圈的定位不同,适合的......
  • 信号量、事件组、任务通知:异同及替代应用分析(超细)
    在实时嵌入式系统中,信号量、事件组和任务通知是常用的同步与通信机制,它们在不同场景下有着各自的优势与适用性。本文将深入探讨这三种机制的异同,分析它们的特点及何时可以相互替代,并通过详细的代码演示展示它们的具体应用。1.信号量(Semaphore)1.1特点计数型:信号量是一种计数型的......
  • Qualcomm LTE Packets log 分析
    QualcommLTEPacketslog分析来源 https://blog.csdn.net/qq_35427437/article/details/118078115来源 https://blog.51cto.com/u_13355654/6246809 1.涉及的PacketsPSS主同步信号数据捕获(InitialAcquisition)信令说明[0xB113][LL1] LTELL1PSSResults主......
  • 软件测试-边界值分析
    一、什么是边界值分析法(What?)     边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法,通常作为对等价类划分法的补充,其测试用例来自等价类的边界。所谓边界值,是指相对于输入等价类和输出等价类而言,稍高于边界或稍低于边界的一些特定情况。 二、为什么使......
  • spring boot启动耗时分析-spring-startup-analyzer使用
    github地址:https://github.com/linyimin0812/spring-startup-analyzer1、安装curl-sShttps://raw.githubusercontent.com/linyimin0812/spring-startup-analyzer/main/bin/install.sh|sh 2、maven<parent><groupId>io.github.linyimin0812</groupI......
  • 《需求分析与系统设计》读书笔记1
    第一章讲了软件过程,从总体生描述了软件开发过程中的策略问题,介绍了支撑现代软件开发的过程和方法,认到了软件工程的本质是软件固有的复杂性,一致性,可变性和不可见性的产物。软件工程的偶然因素分为3类,即投入者,过程和建模语言和工具;投入者指那些与软件项目之间存在着利害关系的人,即客......
  • 使用VisualVM浏览分析堆转储
    堆转储是Java虚拟机(JVM)堆中所有对象在某个时间点的快照。JVM为堆中所有类实例和数组的对象分配内存。当不再需要某个对象并且没有对该对象的引用时,垃圾回收器会回收堆内存。通过VisualVM检查堆,您可以找到对象的创建位置,并在源中找到对这些对象的引用。如果JVM软件无法从堆中删......
  • 2024年视频监控行业发展趋势预测及EasyCVR视频分析技术应用
    随着技术的改进,视频监控领域在过去十年迅速发展。与此同时,该行业正在通过先进创新技术(如人工智能和云计算等技术)的积极商业化,获得了新的增长机会。视频监控系统不再仅仅用于记录图像,而是已经成为全球企业改善运营、提高生产力和增强安全性的重要工具。传统视频安防解决方案转变为......
  • 2024年值得关注的11款工单管理系统——完整比较分析
    享11款主流的工单管理系统,比如:1.PingCode:IT服务管理(ITSM)系统;2.纷享销客:一站式客服工单管理方案;3.Udesk:客户支持或服务台系统;4.OTRS:海外ITSM工单系统;5.Worktile:项目管理系统;6.osTicket:开源客户支持或服务台系统等。一、工单管理系统的类型工单管理系统可以根据不同的应用场景和......