easelog(1)基础C++日志功能实现

EaseLog(1)基础C++日志功能实现


Author: Once Day Date: 2025年2月22日

一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…

漫漫长路,有人对你微笑过嘛…

注:本简易日志组件代码实现参考了Google Chrome Log和moduo Log(作者陈硕)源码


全系列文章可参考专栏: Linux实践记录_Once-Day的博客-CSDN博客

参考文章:

  • Google Code Archive - Long-term storage for Google Code Project Hosting.
  • emilk/loguru: A lightweight C++ logging library
  • OnceDay11/easelog

文章目录

  • EaseLog(1)基础C++日志功能实现
        • 1. 功能定义
        • 2. 实现分析
        • 3. 实际测试
        • 4. 源码文件

1. 功能定义

(1) 支持日志级别分类:DEBUG、INFO、WARNING、ERROR,可选支持FATAL。

(2) 输出信息类别:时间戳、进程ID、线程ID、函数名、代码行号和日志信息(支持不定参数)。

(3) 支持多线程:要求时间戳不能乱序,性能方面没有特别要求。

2. 实现分析

一般常见的日志是Debug日志,或者说程序错误日志,典型代表是Linux环境下的syslog接口,其满足上述的要求。多线程依靠锁来避免时序问题,性能一般,但拓展性和可读性很好,以文本的形式保存和呈现。

还有一类是写到数据库的日志,具有严格定义的字段和值说明,对性能要求极高,在多线程场景下,需要通过Per-Core数据结构和无锁操作(原子指令)来优化性能,但相应的代码会复杂很多。

这里实现的日志库为普通的程序Debug日志,通过互斥锁来避免并发时序问题。虽然看起来互斥锁在多线程环境下性能一般,但是程序Debug日志是文本类日志,其文本格式化本身需要消耗较大性能,日志量也不可能太大。对于性能敏感型的多线程应用,一般会使用数据库日志来记录相关信息。

在实现上,一般采用分层设计,如下所示:

在这里插入图片描述

这里面最核心的部分就是日志格式化处理,一般提供的API函数只有一个,然后通过宏包装扩展到各式各样的日志接口。

程序debug日志底层接口基本都支持自定义的回调函数,然后回调函数里再写入到syslog中,同时也可以直接输出到标准输出或者标准错误(STDOUT/STDERR)。程序很少会自己写日志文件,像rsyslog这类标准库更适合拿来就有,毕竟整理和打包大量应用的日志文件,是一件复杂的事情。

Chromemuduo里面的日志组件代码,写文件的时候会上锁,其他输出方式则都是无锁。这点很有意思,它们在格式化时间戳时都存在时序问题,也就是时间戳乱序,但开发者似乎并不在意。

syslog接口输出的日志时间戳不会乱序(至少rsyslog如此),syslog日志文件里面的时间戳并不在程序Log函数中格式化,而是rsyslogd进程收集到所有日志消息后统一格式化。

本日志库实现要求中,需要在程序Log函数里格式化时间戳,这意味着在格式化日志时就需要上锁,性能会存在一些影响。这种实现方式比较简单,如下所示:

在这里插入图片描述

这个日志组件实现的问题在于锁的粒度太大了,并发线程较多的情况下,debug日志会互相堵塞,拖慢程序执行。一种可行的优化方式是通过多生产者+单消费者的无锁环形队列配合互斥锁实现更小的锁粒度,如下:

在这里插入图片描述

通过无锁队列,可以将普通参数信息的格式化剥离出来,但是所有线程仍然会去抢锁写入日志,正常情况是不同线程轮流负责日志写入,串行化写入可以保证时间戳获取点和写入点的顺序一致,从而避免乱序。

想再提高性能,最好的方式是异步日志(上面有一定异步化,但不够彻底),直接使用单独的日志线程,这个实现起来更加简单,而且无需互斥锁,直接通过无锁队列实现。

本日志组件最终实现两种模式:

  • 低并发度下采取上述的互斥锁方法,这样节省线程资源,性能相对也会更好(减少线程切换)。
  • 高并发度下采取单独日志线程的方法,优先保证业务的并发处理能力,避免日志堵塞,全局效果更优。
3. 实际测试

目前只实现了基础功能:低并发度下采取互斥锁,更上层的复杂日志宏API暂未实现。

测试方面通过创建三个线程来模拟并发日志写入,为了更容易触发乱序,引入随机Sleep操作,在日志格式化和实际写入操作之间,如下所示:

// 用于构造并发时序, 随机等待 10-50ms
void RandomSleep()
{if (g_log_enable_random_sleep) {std::this_thread::sleep_for(std::chrono::milliseconds(10 + rand() % 40));}
}// 创建的线程重复20次输出日志
std::thread t1([]() {pthread_setname_np(pthread_self(), "thread1");for (int i = 0; i < REPEAT_TIMES; i++) {LOG(INFO) << "log message test: " << i;}
});// 没有锁保护下的直接日志写入
if (ShouldLogToStderr(severity_)) {// 生成时间戳std::string timestamp;LogSyslogPrefixTimestamp(log_settings, timestamp);// 引入随机延迟RandomSleep();// 写入日志信息WriteToFd(STDERR_FILENO, timestamp.data(), timestamp.size());WriteToFd(STDERR_FILENO, str_newline.data(), str_newline.size());
}

运行后,可以在输出日志里发现明显的乱序情况,如下:

在这里插入图片描述

引入互斥锁后,可以避免乱序:

if (ShouldLogToStderr(severity_)) {std::lock_guard< std::mutex > lock(g_log_mutex);// 生成时间戳std::string timestamp;LogSyslogPrefixTimestamp(log_settings, timestamp);RandomSleep();// 写入日志信息WriteToFd(STDERR_FILENO, timestamp.data(), timestamp.size());WriteToFd(STDERR_FILENO, str_newline.data(), str_newline.size());
}

运行截图如下:

在这里插入图片描述

但对性能影响较大,整体运行时间较没有上锁,增加了2倍,因为线程需要互相等待对方sleep结束才能拿到锁。

不过,实际运行的程序很少会出现这么久的锁内延迟时间,这毕竟只是一个测试模拟情况。

下一步准备实现第二种模式:高并发度下采取单独日志线程

4. 源码文件

代码已经开源,请在Github上查看:OnceDay11/easelog
下一步准备实现第二种模式:高并发度下采取单独日志线程







Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22653.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue面试2

1.跨域问题以及如何解决跨域 跨域问题&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是指在浏览器中&#xff0c;当一个资源试图从一个不同的源请求另一个资源时所遇到的限制。这种限制是浏览器为了保护用户安全而实施的一种同源策略&#xff08;Same-origin p…

MongoDB应用设计调优

应用范式设计 什么是范式 数据库范式概念是数据库技术的基本理论&#xff0c;几乎是伴随着数据库软件产品的推出而产生的。在传统关系型数据库领域&#xff0c;应用开发中遵循范式是最基本的要求。但随着互联网行业的发展&#xff0c;NoSQL开始变得非常流行&#xff0c;在许多…

Mac安装配置Tomcat 8

一、官网下载 Index of /disthttps://archive.apache.org/dist/ 1、进入界面如下&#xff1a; 2、我们找到Tomcat文件夹并进入 3、找到Tomcat 8并打开 4、找到对应的版本打开 5、打开bin 6、找到“apache-tomcat-8.5.99.tar.gz”并下载 二、配置Tomcat 1、解压已经下载好的…

【论文精读】VLM-AD:通过视觉-语言模型监督实现端到端自动驾驶

论文地址&#xff1a; VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision 摘要 人类驾驶员依赖常识推理来应对复杂多变的真实世界驾驶场景。现有的端到端&#xff08;E2E&#xff09;自动驾驶&#xff08;AD&#xff09;模型通常被优化以模仿…

百度搜索,能否将DeepSeek变成“内功”?

最近&#xff0c;所有的云平台和主流APP都在努力接入DeepSeek。其中&#xff0c;搜索类APP与搜索引擎更是“战况激烈”。那么问题来了&#xff0c;接入DeepSeek已经变成了标准配置&#xff0c;到底应该如何做出差异化&#xff1f;接入DeepSeek这件事能不能实现11大于2的效果&am…

Flask实现高效日志记录模块

目录 一. 简介&#xff1a; 1. 为什么需要请求日志 二. 日志模块组成 1. 对应日志表创建&#xff08;包含日志记录的关键字段&#xff09; 2. 编写日志记录静态方法 3. 在Flask中捕获请求日志 4. 捕获异常并记录错误日志 5. 编写日志接口数据展示 6. 写入数据展…

25轻化工程研究生复试面试问题汇总 轻化工程专业知识问题很全! 轻化工程复试全流程攻略 轻化工程考研复试真题汇总

轻化工程复试心里没谱&#xff1f;学姐带你玩转面试准备&#xff01; 是不是总觉得老师会问些刁钻问题&#xff1f;别焦虑&#xff01;其实轻化工程复试套路就那些&#xff0c;看完这篇攻略直接掌握复试通关密码&#xff01;文中有重点面试题可直接背~ 目录 一、这些行为赶紧避…

查看已经安装的Python库,高效管理自己的Python开发环境

在日常的Python开发中&#xff0c;掌握如何管理和查看已经安装的库是非常重要的。这不仅能帮助你了解当前项目的依赖关系&#xff0c;还能避免出现版本冲突等问题。在这篇文章中&#xff0c;我们将详细介绍查看已安装Python库的方法&#xff0c;并提供一些实用的工具和技巧。 …

Selenium实战案例1:论文pdf自动下载

在上一篇文章中&#xff0c;我们介绍了Selenium的基础用法和一些常见技巧。今天&#xff0c;我们将通过中国科学&#xff1a;信息科学网站内当前目录论文下载这一实战案例来进一步展示Selenium的web自动化流程。 目录 中国科学&#xff1a;信息科学当期目录论文下载 1.网页内…

Visual Studio Code 2025 安装与高效配置教程

一、软件简介与下载 1. Visual Studio Code 是什么&#xff1f; Visual Studio Code&#xff08;简称VS Code&#xff09;是微软推出的免费开源代码编辑器&#xff0c;支持 智能代码补全、Git集成、插件扩展 等功能&#xff0c;适用于前端开发、Python、Java等多种编程场景。…

工业路由器和工业交换机,打造高效稳定的工业网络?

工业路由器和工业交换机各有千秋&#xff0c;但如何将它们完美结合&#xff0c;构建稳定高效的工业网络&#xff1f;答案就在这里&#xff01; 工业物联网&#xff08;IIoT&#xff09;是高效、稳定的工业网络成为智慧工厂、工业自动化和远程监控等场景的基础支撑。工业路由器…

DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

TSMaster【第七篇:千机百变——面板设计艺术】

武侠场景导入&#xff1a;唐门暗器阁的启示 江湖传言&#xff0c;唐门暗器之所以独步天下&#xff0c;全凭其「千机匣」中七十二种机关变化。TSMaster面板设计恰似打造暗器机关——控件如同飞镖、机簧、毒针&#xff0c;组合方式不同则威力迥异。昔日某新势力车型因仪表盘刷新…

提升 AI 服务的稳定性:Higress AI 网关的降级功能介绍

在使用 LLM 服务时&#xff0c;服务的稳定性和可用性至关重要。然而&#xff0c;由于网络问题、服务器故障或其他不可控因素&#xff0c;LLM 服务可能会暂时不可用。为了保障用户体验和业务连续性&#xff0c;Higress AI 网关提供了强大的模型降级和令牌降级功能。本文将介绍这…

提升C++项目编译速度

目录 一、问题背景 二、代码规范方面的解决方案 2.1 拆分头文件 2.2 拆分巨型类 2.3 使用前置声明 2.4 避免在头文件中包含实现 2.5 避免头文件重复包含 2.6 将常用且变动较少的独立到一个文件 三、代码业务重构方面经验 3.1 使用PIMPL&#xff08;Pointer to Imple…

【学术投稿-第四届材料工程与应用力学国际学术会议(ICMEAAE 2025】材料工程与应用力学的探讨

重要信息 官网&#xff1a;www.icmeaae.com 时间&#xff1a;2025年3月7-9日 地点&#xff1a;中国西安 简介 第四届材料工程与应用力学&#xff08;ICMEAAE 2025&#xff09;将于2025年3月7日至9日在中国西安召开。本次会议将重点讨论材料科学、应用力学等领域的最新研究进…

抓包工具(三)Wireshark代理抓包Java程序的HTTPS请求

目录 一、需求背景二、操作步骤2.1 jSSLKeyLog 工具下载2.2 jSSLKeyLog工具使用2.3 将sslkeylog导入Wireshark2.4 测试Demo2.5 测试结果1&#xff09;使用工具解密HTTPS前&#xff1a;2&#xff09;实用工具解密HTTPS后&#xff1a; 三、补充&#xff1a;如果出现未解密成功的情…

[VSCode]彻底卸载和重装,并搭建Java开发环境

VSCode彻底卸载 由于当初是朋友帮忙装的&#xff0c;所以准备卸载,自己装一遍 从控制面板找到 vscode 将其卸载。 此时仅仅是删除了应用软件 删除安装插件 在图示路径中找到 .vscode 文件夹&#xff0c;将其删除&#xff0c;即可彻底清除安装的插件 C:\Users\user\.vscode …

泛微OA编写后端Rest接口

泛微OA编写后端Rest接口 前言 具体实现 运行结果 注意要点 总结 前言 在泛微E9中&#xff0c;可以通过注解的方式来编写对外的接口&#xff0c;之前的版本都是通过编写servlet类&#xff0c;然后在web.xml文件中将这个类和访问路径进行编辑之后才好在浏览器中通过输入对应…

041集——封装之:新建图层(CAD—C#二次开发入门)

如图所示&#xff1a;增加一个图层“新图层”&#xff0c;颜色为红&#xff08;1&#xff09;&#xff0c;当图层颜色定义为黄&#xff08;2&#xff09;时&#xff0c;直接覆盖之前图层颜色&#xff0c;图层名不变。 代码如下&#xff1a; /// </summary>/// <param …