testlogger分析
功能:
一、将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;
}
类图
vs2022中使用该库(静态库)
新建工程:
复制include和lib
在项目根目录新建include和lib文件夹:
include:放头文件,头文件是将src目录下的所有文件直接复制进来,然后按照“类型排序”,删除其他文件,只保留".h"文件
lib:放lib库文件,lib库文件原始路径:ZLToolKit\lib\Debug
目录树如下:
testLogger.sln │ ├─include │ ├─Network │ │ Buffer.h │ │ BufferSock.h │ │ Server.h │ │ Session.h │ │ Socket.h │ │ Socket_ios.mm │ │ sockutil.h │ │ TcpClient.h │ │ TcpServer.h │ │ UdpServer.h │ │ │ ├─Poller │ │ EventPoller.h │ │ Pipe.h │ │ PipeWrap.h │ │ SelectWrap.h │ │ Timer.h │ │ │ ├─Thread │ │ semaphore.h │ │ TaskExecutor.h │ │ TaskQueue.h │ │ threadgroup.h │ │ ThreadPool.h │ │ WorkThreadPool.h │ │ │ ├─Util │ │ base64.h │ │ CMD.h │ │ File.h │ │ function_traits.h │ │ List.h │ │ local_time.h │ │ logger.h │ │ MD5.h │ │ mini.h │ │ NoticeCenter.h │ │ onceToken.h │ │ ResourcePool.h │ │ RingBuffer.h │ │ SHA1.h │ │ SpeedStatistic.h │ │ SqlConnection.h │ │ SqlPool.h │ │ SSLBox.h │ │ SSLUtil.h │ │ strptime_win.h │ │ TimeTicker.h │ │ util.h │ │ uv_errno.h │ │ │ └─win32 │ getopt.h │ tailor.h │ ├─lib │ └─Debug │ ZLToolKit.lib │ ZLToolKit.pdb │ ├─testLogger │ │ testLogger.cpp │ │ testLogger.vcxproj │ │ testLogger.vcxproj.filters │ │ testLogger.vcxproj.user │ │ │ └─x64 │ └─Debug │ │ testLogger.exe.recipe │ │ testLogger.ilk │ │ testLogger.log │ │ testLogger.obj │ │ vc143.idb │ │ vc143.pdb │ │ │ └─testLogger.tlog │ CL.command.1.tlog │ Cl.items.tlog │ CL.read.1.tlog │ CL.write.1.tlog │ link.command.1.tlog │ link.read.1.tlog │ link.write.1.tlog │ testLogger.lastbuildstate │ └─x64 └─Debug testLogger.exe testLogger.pdb PS E:\26_zlmediakit\11_ZLToolKit\__code\_testLogger-vs22\testLogger>
设置项目属性
代码调用
1 // testLogger.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 2 // 3 4 #include <iostream> 5 #include "Util/logger.h" 6 7 8 using namespace std; 9 using namespace toolkit; 10 11 int main() 12 { 13 Logger::Instance().add(std::make_shared<ConsoleChannel>()); 14 Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>()); 15 16 17 InfoL << "测试std::cout 风格的日志输出打印:"; 18 19 DebugL << "DebugL..."; 20 21 std::cout << "Hello World!\n"; 22 }
运行结果
centos 使用 (动态库)
新建clion项目,选择可执行文件、支持c++11
cmake源码
1 cmake_minimum_required(VERSION 3.21) 2 project(_testLogger) 3 4 set(CMAKE_CXX_STANDARD 11) 5 6 #指定库的目录变量 7 set(LIBZLM_LIB /_workdir/_08_selfCode/_testLogger/lib/libZLToolKit.so) 8 9 #指定头文件搜索路径 10 include_directories("${PROJECT_SOURCE_DIR}/include") 11 12 add_executable(_testLogger main.cpp) 13 14 target_link_libraries(_testLogger ${LIBZLM_LIB})
main源码
#include <iostream> #include "Util/logger.h" using namespace std; using namespace toolkit; // cmake https://blog.csdn.net/Vincent20111024/article/details/129327542 int main() { Logger::Instance().add(std::make_shared<ConsoleChannel>()); Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>()); InfoL << "测试std::cout 风格的日志输出打印:"; DebugL << "DebugL..."; std::cout << "Hello, World!" << std::endl; return 0; }
运行结果:
文件树目录:
1 . 2 ├── cmake-build-debug 3 │ ├── build.ninja 4 │ ├── .cmake 5 │ │ └── api 6 │ │ └── v1 7 │ │ ├── query 8 │ │ │ ├── cache-v2 9 │ │ │ ├── cmakeFiles-v1 10 │ │ │ ├── codemodel-v2 11 │ │ │ └── toolchains-v1 12 │ │ └── reply 13 │ │ ├── cache-v2-8e8881062be1748bf551.json 14 │ │ ├── cmakeFiles-v1-a684896424473d278502.json 15 │ │ ├── codemodel-v2-e48b31e71f5adb5d5705.json 16 │ │ ├── directory-.-Debug-f5ebdc15457944623624.json 17 │ │ ├── index-2023-12-18T11-43-16-0332.json 18 │ │ ├── target-_testLogger-Debug-7820f096840015a86989.json 19 │ │ └── toolchains-v1-c006aca650fa415857a6.json 20 │ ├── CMakeCache.txt 21 │ ├── CMakeFiles 22 │ │ ├── 3.21.1 23 │ │ │ ├── CMakeCCompiler.cmake 24 │ │ │ ├── CMakeCXXCompiler.cmake 25 │ │ │ ├── CMakeDetermineCompilerABI_C.bin 26 │ │ │ ├── CMakeDetermineCompilerABI_CXX.bin 27 │ │ │ ├── CMakeSystem.cmake 28 │ │ │ ├── CompilerIdC 29 │ │ │ │ ├── a.out 30 │ │ │ │ ├── CMakeCCompilerId.c 31 │ │ │ │ └── tmp 32 │ │ │ └── CompilerIdCXX 33 │ │ │ ├── a.out 34 │ │ │ ├── CMakeCXXCompilerId.cpp 35 │ │ │ └── tmp 36 │ │ ├── clion-environment.txt 37 │ │ ├── clion-log.txt 38 │ │ ├── cmake.check_cache 39 │ │ ├── CMakeOutput.log 40 │ │ ├── CMakeTmp 41 │ │ ├── feature_tests.bin 42 │ │ ├── feature_tests.cxx 43 │ │ ├── rules.ninja 44 │ │ ├── TargetDirectories.txt 45 │ │ └── _testLogger.dir 46 │ ├── cmake_install.cmake 47 │ ├── .ninja_deps 48 │ ├── .ninja_log 49 │ └── Testing 50 │ └── Temporary 51 │ └── LastTest.log 52 ├── CMakeLists.txt 53 ├── .idea 54 │ ├── .gitignore 55 │ ├── misc.xml 56 │ ├── modules.xml 57 │ ├── _testLogger.iml 58 │ └── workspace.xml 59 ├── include 60 │ ├── Network 61 │ │ ├── Buffer.h 62 │ │ ├── BufferSock.h 63 │ │ ├── Server.h 64 │ │ ├── Session.h 65 │ │ ├── Socket.h 66 │ │ ├── Socket_ios.mm 67 │ │ ├── sockutil.h 68 │ │ ├── TcpClient.h 69 │ │ ├── TcpServer.h 70 │ │ └── UdpServer.h 71 │ ├── Poller 72 │ │ ├── EventPoller.h 73 │ │ ├── Pipe.h 74 │ │ ├── PipeWrap.h 75 │ │ ├── SelectWrap.h 76 │ │ └── Timer.h 77 │ ├── Thread 78 │ │ ├── semaphore.h 79 │ │ ├── TaskExecutor.h 80 │ │ ├── TaskQueue.h 81 │ │ ├── threadgroup.h 82 │ │ ├── ThreadPool.h 83 │ │ └── WorkThreadPool.h 84 │ ├── Util 85 │ │ ├── base64.h 86 │ │ ├── CMD.h 87 │ │ ├── File.h 88 │ │ ├── function_traits.h 89 │ │ ├── List.h 90 │ │ ├── local_time.h 91 │ │ ├── logger.h 92 │ │ ├── MD5.h 93 │ │ ├── mini.h 94 │ │ ├── NoticeCenter.h 95 │ │ ├── onceToken.h 96 │ │ ├── ResourcePool.h 97 │ │ ├── RingBuffer.h 98 │ │ ├── SHA1.h 99 │ │ ├── SpeedStatistic.h 100 │ │ ├── SqlConnection.h 101 │ │ ├── SqlPool.h 102 │ │ ├── SSLBox.h 103 │ │ ├── SSLUtil.h 104 │ │ ├── strptime_win.h 105 │ │ ├── TimeTicker.h 106 │ │ ├── util.h 107 │ │ └── uv_errno.h 108 │ └── win32 109 │ ├── getopt.h 110 │ └── tailor.h 111 ├── lib 112 │ ├── libZLToolKit.a 113 │ └── libZLToolKit.so 114 └── main.cpp 115 116 24 directories, 90 files