c++_基于多种设计模式下的同步&异步日志器系统

1.项目介绍
本项目就是一个日志器系统,目的就是方便程序员在代码开发中进行日志信息的获取,调试代码定位错误排查问题
这个项目主要支持以下几个功能:
多级别日志消息(可以控制日志输出等级)
同步于异步日志
多种落地方向 (如标准输出,指定文件,以大小自动切换的文件) 可扩展
支持多线程写日志
用到的核心技术:
分模块叙述:

项目源代码
2.开发环境
• ubuntu 22
• vscode
• g++/gdb
• Makefile
3.核心技术

  1. 类层次设计(继承与多态的应用)
  2. c++11(auto,智能指针,右值引用等)
  3. 双缓冲区
  4. 生产消费模型
  5. 多线程
  6. 设计模式(单例模式,工厂模式,建造者模式,模板模式,代理模式)

4.日志器模块
1.日志等级模块:定义日志的等级 等级有大小区分 可以做到限制等级输出 开发者根据日志等级可以针对性的解决和定位问题

本日志器共分为五个等级
DEBUG, 调试消息
INFO, 消息
WARNING, 警告
ERROR, 发生错误
FATAL, 发生致命错误
OFF 关闭所以日志输出

2.日志消息模块:一条日志所需要的的各种信息总和 类
类中含有 日志等级 时间 线程id 文件名 行号 日志器名称 消息主体

3.日志器消息格式模块:对日志器的日志消息输出格式,得到字符串类型的整体日志消息 我们也缺省提供了一个默认的格式

格式化流程: 我们对每种消息都有对应的格式控制(还是需要使用者看一下使用方法)
%+字符对应一种类型信息 我们对每个格式都有对应的函数 以将日志消息中的对应信息转换为字符串(这里大量使用了继承和多态) 使用时需要传入对应格式的字符串, 我们会对其进行切割 按顺序将对应的格式对应处理方法放到一个vector管理起来 在输出时只需要遍历vector调用方法就可以得到对应的字符串

各种字符代表的消息
%d ⽇期 %T 缩进 %t 线程id %p ⽇志级别 %c ⽇志器名称
%f ⽂件名 %l ⾏号 %m ⽇志消息 %n 换⾏

4.日志器落地模块:将日志消息落地到对应的文件 本模块默认支持三种落地模式 可扩展
采用到了简单工厂模式

将日志器消息格式模块得到的字符串数据写到文件中
默认支持三种方式
标准输出 指定文件 根据文件大小滚动切换文件

5.异步线程模块:异步日志器需要这个模块
异步模式 主线程将得到的字符串交给异步线程 由异步线程调用落地函数

这里的消息传递采用双缓冲区的生产消费模型 实现了读写分离
一个写入缓冲区 由主线程写入 一个落地缓冲区 异步线程将数据取出并写入文件

双缓冲区的应用:
双缓冲区的好处:
提高了并发读写的效率 在多线程情况下 读写操作存在竞争的 读写分离减少了竞争 仅在两个缓冲区交数据的情况下存在线程互斥
提高了数据安全性 避免了读写操作交叉 从而保证了安全性
提高系统稳定性 减少读写阻塞 提高系统稳定性

相较于环形队列
环形队列同时读取 需要相对较平衡的生产和消费 才能高效 节约了空间 内存反复使用
双缓冲区适用于双方速率不一致 消耗空间
当写缓冲区所以数据都写入完毕就交换讲个缓冲区 这样只有两个缓冲区交换时才需要加锁

6.日志器模块:对以上模块的整合
采用建造者模式
日志器分为同步,异步(安全,非安全) 这些日志器有分为 本地,全局
得到日志消息 建造日志消息对象 得到日志消息的字符串 再调用落地模块

异步模式需要调用异步线程模块
实现流程:由工作线程将日志信息交给异步线程 由异步线程实现落地

全局模式下的日志器都要放进日志器管理模块

7.日志器管理模块:管理全局的日志器
采用了单例模式
将全局模式的日志器都记录下来 再上层调用的时候根据日志器名调用对应日志器

8.缓冲区模块
与异步线程的信息交换
维护一片缓冲区 拥有数据写入和取出方法 安全模式下不扩容 极限测试模式下可以进行扩容

9.任志宏&全局接口模块
为了方便用户使用 还需要使用宏封装一个默认的日志器 根据等级直接落地日志
采用代理模式简化用户使用函数的成本 用户只需要传入日志消息本体就行

在这里插入图片描述
测试
环境
在云服务器下 1G内存 ubuntu 22 系统
写入五十万条日志 每条日志内容为50kb

单线程情况下
同步日志器模式 耗时1.7秒 平均29万条每秒
异步日志器安全模式 耗时2.06秒 大约24.2万条每秒
异步日志器极限测试模式 耗时2.65秒 大约18.8万条每秒
在这里插入图片描述
十个线程情况下
同步日志器模式 耗时1.3秒 平均38.2万条每秒
异步日志器安全模式 耗时1.04秒 大约48万条每秒
异步日志器极限测试模式 耗时1.04秒 大约48万条每秒
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
log4cplus是C++编写的开源的日志系统,功能非常全面,用到自己开发的工程中会比较专业的,:),本文介绍了log4cplus基本概念,以及如何安装,配置。 ### 简介 ### log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统.受Apache Software License保护。作者是Tad E. Smith。log4cplus具有线程安全、灵活、以及多粒度控制的特点,通过将信息划分优先级使其可以面向程序调试、运行、测试、和维护等全生命周 期; 你可以选择将信息输出到屏幕、文件、 NT event log、甚至是远程服务;通过指定策略对日志进行定期备份等等。 ### 下载 ### 最新的log4cplus可以从以下网址下载 http://log4cplus.sourceforge.net本文使用的版本为:1.0.2 ### 安装 ### 1. linux下安装 tar xvzf log4cplus-x.x.x.tar.gz cd log4cplus-x.x.x ./configure --prefix=/where/to/install make make install 这里我采用缺省安装路径:/usr/local,下文如无特别说明,均以此路径为准。 2. windows下安装 不需要安装,有一个msvc6存放包括源代码和用例在内的开发工程(for VC6 only),使用之前请先编译 "log4cplus_dll class"工程生成dll,或者编译"log4cplus_static class"工程生成lib. ### 使用前的配置 ### 1. linux下的配置 确保你的Makefile中包含 /usr/local/lib/liblog4cplus.a(静态库)或 -llog4cplus(动态库)即可, 头文件在/usr/local/include/log4cplus目录下。对于动态库,要想正常使用,还得将库安装路径加入到 LD_LIBRARY_PATH 中,我一般是这样做的:以管理员身份登录,在/etc/ld.so.conf中加入安装路径,这里 是/usr/local/lib,然后执行ldconfig使设置生效即可。 2. windows下的配置 将"log4cplus_dll class"工程或"log4cplus_static class"工程的dsp 文件插入到你的工程中,或者直接 把两个工程编译生成的库以及头文件所在目录放到你的工程的搜索路径中,如果你使用静态库,请在你的工程中 "project/setting/C++"的preprocessor definitions中加入LOG4CPLUS_STATIC。 ### 构成要素介绍 ### 虽然功能强大,应该说log4cplus用起来还是比较复杂的,为了更好地使用它,先介绍一下它的基本要素。 Layouts :布局,控制输出消息的格式. Appenders :挂接,与布局紧密配合,将特定格式的消息输出到所挂接的设备终端 (如屏幕,文件等等)。 Logger :记录,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行 记录时,就需要生成一个logger。 Categories :分类,层次化(hierarchy)的结构,用于对被记录信息的分类,层次中 每一个节点维护一个logger的所有信息。 Priorities :优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。 本文介绍了log4cplus基本概念,以及如何安装,配置,下一篇将通过例子介绍如何使用log4cplus。 (二) 本文介绍了使用log4cplus有六个步骤,并提供了一些例子引导你了解log4cplus的基本使用。 ### 基本使用 ### 使用log4cplus有六个基本步骤: 1. 实例化一个appender对象 2. 实例化一个layout对象 3. 将layout对象绑定(attach)到appender对象 4. 实例化一个logger对象,调用静态函数:log4cplus::Logger::getInstance("logger_name") 5. 将appender对象绑定(attach)到logger对象,如省略此步骤,标准输出(屏幕)appender对象会绑定到logger 6. 设置logger的优先级,如省略此步骤,各种有限级的消息都将被记录 下面通过一些例子来了解log4cplus的基本使用。 〖例1〗 cpp 代码 /* 严格实现步骤1-6,appender输出到屏幕, 其中的布局格式和LogLevel后面会详细解释。*/ #include #include #include using namespace log4cplus; using namespace log4cplus::helpers; int main(){ /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr _layout(new PatternLayout(pattern)); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* step 6: Set a priority for the logger */ _logger.setLogLevel(ALL_LOG_LEVEL); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } 输出结果: 10/14/04 09:06:24 - This is the FIRST log message... [main.cpp:31] 10/14/04 09:06:25 - This is the SECOND log message... [main.cpp:33] 〖例2〗 /* 简洁使用模式,appender输出到屏幕。 */ #include #include using namespace log4cplus; using namespace log4cplus::helpers; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } 输出结果: DEBUG - This is the FIRST log message... WARN - This is the SECOND log message... 〖例3〗 /* iostream模式,appender输出到屏幕。 */ #include #include #include /* 其实这个东东还是放到log4cplus头文件中比较合适些,个人意见:) */using namespace log4cplus; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_TRACE(_logger, "This is" << " just a t" << "est." << std::endl) LOG4CPLUS_DEBUG(_logger, "This is a bool: " << true) LOG4CPLUS_INFO(_logger, "This is a char: " << 'x') LOG4CPLUS_WARN(_logger, "This is a int: " << 1000) LOG4CPLUS_ERROR(_logger, "This is a long(hex): " << std::hex << 100000000) LOG4CPLUS_FATAL(_logger, "This is a double: " << std::setprecision(15) << 1.2345234234) return 0; } 输出结果: DEBUG - This is a bool: 1 INFO - This is a char: x WARN - This is a int: 1000 ERROR - This is a long(hex): 5f5e100 FATAL - This is a double: 1.2345234234 〖例4〗 /* 调试模式,通过loglog来控制输出调试、警告或错误信息,appender输出到屏幕。 */ #include #include using namespace log4cplus::helpers; void printMsgs(void) { std::cout << "Entering printMsgs()..." << std::endl; LogLog::getLogLog()->debug("This is a Debug statement..."); LogLog::getLogLog()->warn("This is a Warning..."); LogLog::getLogLog()->error("This is a Error..."); std::cout << "Exiting printMsgs()..." << std::endl << std::endl; } int main() { /* LogLog类实现了debug, warn, error 函数用于输出调试、警告或错误信息, 同时提供了两个方法来进一步控制所输出的信息,其中: setInternalDebugging方法用来控制是否屏蔽输出信息中的调试信息,当输入 参数为false则屏蔽,缺省设置为false。 setQuietMode方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽, 缺省设置为false。 LogLog::getLogLog()->setInternalDebugging(false); */ printMsgs(); std::cout << "Turning on debug..." << std::endl; LogLog::getLogLog()->setInternalDebugging(true); printMsgs(); std::cout << "Turning on quiet mode..." << std::endl; LogLog::getLogLog()->setQuietMode(true); printMsgs(); return 0; } 输出结果: Entering printMsgs()... log4cplus:WARN This is a Warning... log4cplus:ERROR This is a Error... Exiting printMsgs()... Turning on debug... Entering printMsgs()... log4cplus: This is a Debug statement... log4cplus:WARN This is a Warning... log4cplus:ERROR This is a Error... Exiting printMsgs()... Turning on quiet mode... Entering printMsgs()... Exiting printMsgs()... 需要指出的是,输出信息中总是包含"log4cplus:"前缀,有时候会感觉不爽,这是因为LogLog在实现时候死定了要这么写: LogLog::LogLog() : mutex(LOG4CPLUS_MUTEX_CREATE), debugEnabled(false), quietMode(false), PREFIX( LOG4CPLUS_TEXT("log4cplus: ") ), WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN ") ), ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR ") ) { } 你可以把这些前缀换成自己看着爽的提示符号,然后重新编译,hihi。除非万不得已或者实在郁闷的不行,否则还是不要这样干。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值