当前位置: 首页 > news >正文

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

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

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

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

相关文章:

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

EaseLog(1)基础C日志功能实现 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 注&#xff1a;本简易日志组件代码实现参考了Google …...

epoll_event的概念和使用案例

epoll_event 是 Linux 下 epoll I/O 多路复用机制的核心数据结构&#xff0c;用于描述文件描述符&#xff08;File Descriptor, FD&#xff09;上发生的事件及其关联的用户数据。通过 epoll&#xff0c;可以高效地监控多个文件描述符的状态变化&#xff08;如可读、可写、错误等…...

Leetcode2506:统计相似字符串对的数目

题目描述&#xff1a; 给你一个下标从 0 开始的字符串数组 words 。 如果两个字符串由相同的字符组成&#xff0c;则认为这两个字符串 相似 。 例如&#xff0c;"abca" 和 "cba" 相似&#xff0c;因为它们都由字符 a、b、c 组成。然而&#xff0c;"…...

蓝桥月赛 之 26场

文章目录 好汤圆灯笼猜谜元宵分配摆放汤圆 好汤圆 好汤圆 思路分析&#xff1a;由于2025能够被15整除&#xff0c;所以我们直接输出对应的答案即可 import os import sys# 请在此输入您的代码print(2025//15)灯笼猜谜 灯笼猜谜 思路分析&#xff1a;首先呢&#xff0c;我就考…...

机器学习面试八股文——决战金三银四

大家好&#xff0c;这里是好评笔记&#xff0c;公主 号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记的任务是解读机器学习实践/面试过程中可能会用到的知识点&#xff0c;内容通俗易懂&#xff0c;入门、实习和校招轻松搞定。 公主号合集地址 点击进入优惠地…...

umi: valtio的使用

一、基本用法 import { proxy, useSnapshot } from umijs/max;// 1、定义数据 const state proxy({ count: 33 });export default () > {// 2、使用数据const snap useSnapshot(state);function increaseCount() {state.count 1;}return (<><h1>{snap.count}…...

区块链相关方法-波特五力分析模型

一、定义:波特五力分析模型&#xff08;Porters Five Forces Framework&#xff09;是迈克尔・波特&#xff08;Michael Porter&#xff09;于 1979 年提出的一种用于分析行业竞争态势的工具。它通过考察五种力量的相互作用来评估一个行业的吸引力和竞争环境&#xff0c;这五种…...

纷析云开源版- Vue2-增加字典存储到localStorage

main.js //保存字典数据到LocalStorage Vue.prototype.$api.setting.SystemDictType.all().then(({data}) > {loadDictsToLocalStorage(data) })新增 dictionary.js 放在 Utils文件夹里面 // 获取字典数据 export function getDictByType(dictType) {const dicts JSON.par…...

HTML项目一键打包工具:HTML2EXE 最新版

HTML2EXE 工具可以一键打包生成EXE可执行文件。可以打包任意HTML项目或者是一个网址为单个EXE文件&#xff0c;直接打开即可运行。支持KRPano全景VR项目、WebGL游戏项目、视频播放、,课件打包、网址打包等。 一、功能特点 类别序号功能标题1支持程序图标自定义&#xff08;支持…...

Windows 中的启动项如何打开?管理电脑启动程序的三种方法

在日常使用电脑时&#xff0c;我们经常会发现一些应用程序在开机时自动启动&#xff0c;这不仅会拖慢系统的启动速度&#xff0c;还可能占用不必要的系统资源。幸运的是&#xff0c;通过几个简单的步骤&#xff0c;你可以轻松管理这些开机自启的应用程序。接下来&#xff0c;我…...

在 JavaScript 中接入 Facebook 事件

在 JavaScript 中接入 Facebook 事件 本文档介绍了如何在 JavaScript 中集成 Facebook Pixel 事件&#xff0c;用于跟踪网站的用户行为并提高广告效果。 1. 安装并初始化 Facebook Pixel 在开始接入事件之前&#xff0c;首先需要在你的网页中初始化 Facebook Pixel。Faceboo…...

如何在cursor上使用 deepseek 模型

引言 Cursor 虽提供免费试用&#xff0c;但试用时间有限&#xff0c;且后续使用可能会面临速度限制。不过&#xff0c;用户可以使用自己的 API key 来继续使用。值得一提的是&#xff0c;deepseek 模型使用成本极为低廉&#xff0c;能为使用者带来更多灵活性与经济性。基于此&…...

mysql的字符集和比较规则

mysql的字符集和比较规则 一、字符集&#xff08;Character Set&#xff09;二、比较规则&#xff08;Collation&#xff09;三、客户端与服务器的字符集转换四、注意事项总结 深度解读mysql是怎样运行的 MySQL的字符集和比较规则是其处理字符串存储、传输及比较的核心机制&…...

什么是LoRA微调

LoRA是大模型微调方法的一种&#xff0c;它的特点是只在模型的 部分权重&#xff08;如 QKV 矩阵&#xff09; 上 添加可训练参数 通过 低秩矩阵&#xff08;AB&#xff09; 来优化参数更新 优点&#xff1a; 极大降低显存消耗&#xff08;deepseek 7B 只需 10GB&#xff09; 适…...

热管理系统:新能源汽车的 “温度管家”

在新能源汽车的众多系统中&#xff0c;热管理系统堪称是一位默默守护的 “温度管家”&#xff0c;其重要性不容小觑。传统燃油车的热管理主要围绕发动机、变速箱冷却系统和空调系统&#xff0c;而新能源汽车的热管理则涵盖了电池系统、电机电控、空调系统等绝大部分零部件 &…...

如何修改Windows系统Ollama模型存储位置

默认情况下&#xff0c;Ollama 模型会存储在 C 盘用户目录下的 .ollama/models 文件夹中&#xff0c;这会占用大量 C 盘空间&#xff0c;增加C盘“爆红”的几率。所以&#xff0c;我们就需要修改Ollama的模型存储位置 Ollama提供了一个环境变量参数可以修改Ollama的默认存在位…...

《网络安全入门实战手册》

0经验转行网络安全&#xff0c;个人分享一下学习中总结的文档&#xff0c;以下为目录可以点击标题看对应文章&#xff0c;欢迎评论区讨论&#xff0c;后期会发更多安全相关的学习资料等。希望跟大家一起进步。 第1章&#xff1a;网络安全基础知识 1、什么是网络安全&#xff…...

一文详解U盘启动Legacy/UEFI方式以及GPT/MBR关系

对于装系统的老手而说一直想研究一下装系统的原理&#xff0c;以及面对一些问题时的解决思路&#xff0c;故对以前的方法进行原理上的解释&#xff0c;主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导&#xff0c;我们可以看一下微pe制作的启动盘&#…...

如何查看java的字节码文件?javap?能用IDEA吗?

编译指令&#xff1a; javac YourProject.java 查看字节码文件的指令&#xff1a; javap -c -l YourProject.class 不添加-c指令就不会显示字节码文件&#xff1a; 不添加 -l 就不会显示源代码和字节码文件的对应关系&#xff1a; 添加-l之后多出来这些&#xff1a; IDEA不太…...

加油站(力扣134)

既然每一个加油站都有对应的加油量和耗油量&#xff0c;我们不妨计算一下每个加油站的汽油净增量。如果每个加油站净增量之和不为负数&#xff0c;则说明一定可以找到唯一的起始点。那我们该如何找到这个起始点呢&#xff1f;我们设置最开始的起点为第0个加油站&#xff0c;接着…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...