C++简易日志系统:打造高效、线程安全的日志记录工具

目录

引言:

1.日志的基本概念

1.1.什么是日志?

1.2.我们为什么需要日志?

2.自己实现一个简易日志

2.1.日志的等级

2.2日志的格式

2.3.获取时间的方法

2.4.日志的主体实现

参数:

代码解析:

问题:写日志的时候,为什么也要保证线程安全?

一、避免数据竞争和不一致

二、确保日志的完整性和可读性

三、防止资源冲突和死锁

四、提高系统的稳定性和可靠性

3.日志的使用

3.1.代码解析:

3.2.实际效果

引言:

今天给大家带来的是用C++语言编写的一个简易日志系统。

1.日志的基本概念

1.1.什么是日志?

在当前的数字化时代,日志分析已经成为了云安全的重要组成部分,日志文件记录了系统、应用程序和网络的各种活动,通过分析这些日志,我们可以发现潜在的安全问题,预防和应对各种安全威胁

1.2.我们为什么需要日志?

在Linux系统下,日志的作用非常关键,它们记录了系统运行过程中的各种事件和信息,对于系统管理、故障排查、安全审计、性能分析和合规性记录等方面都具有重要作用。

  1. 记录系统事件
    • 日志文件记录了系统启动、运行和关闭过程中的各种事件,包括系统错误、警告、信息性和调试信息等。
    • 这些信息有助于管理员了解系统的整体运行状况,及时发现并解决问题。
  2. 故障排查
    • 当系统或应用程序出现问题时,日志文件可以帮助管理员快速定位问题的根源。
    • 通过分析日志文件,管理员可以了解问题发生的时间、原因和影响范围,从而采取相应的措施进行修复。
  3. 安全审计
    • 安全相关的日志文件记录了用户登录、权限变更、系统访问等安全事件。
    • 这些信息对于检测和防范未授权访问或安全威胁至关重要,有助于管理员及时发现并应对潜在的安全风险。
  4. 性能监控
    • 应用程序和中间件的日志文件可以提供性能指标和资源使用情况的信息。
    • 通过分析这些信息,管理员可以优化系统和应用程序的性能,提高系统的运行效率。
  5. 合规性记录
    • 在某些行业和法规要求下,日志文件作为合规性记录的一部分,用于证明系统操作的合法性和合规性。
    • 这些记录有助于企业满足相关的法律法规要求,避免潜在的法律风险。
  6. 用户行为分析
    • 通过分析日志文件,管理员还可以了解用户在系统中的行为模式。
    • 这有助于管理员进行相应的管理和维护,确保系统的安全和稳定运行。

2.自己实现一个简易日志

2.1.日志的等级

我们使用一个枚举成员来枚举日志等级,枚举的使用使得在代码中引用日志级别时,可以使用更具描述性的名称(如 Level::ERROR),而不是直接使用数字(如 4),从而提高代码的可读性和可维护性。

下面我们来主要讲解每个枚举成员。

  • DEBUG = 1:调试级别的日志。这通常用于开发过程中,记录详细的调试信息,帮助开发者定位和解决问题。这里明确地将 DEBUG 赋值为 1,意味着枚举值是从 1 开始的。
  • INFO:信息级别的日志。用于记录程序的正常运行信息,比如程序的启动和关闭、接收到的请求等。由于枚举值默认递增,INFO 的值会自动设为 2。
  • WARNING:警告级别的日志。用于记录潜在的有害情况,但不一定立即需要采取行动。其值会自动设为 3。
  • ERROR:错误级别的日志。用于记录程序运行时发生的错误,这些错误需要被关注,但程序可能仍然能够继续运行。其值会自动设为 4。
  • FATAL:致命级别的日志。表示程序遇到了无法恢复的错误,程序将无法正常继续运行。其值会自动设为 5。
// 1、日志是有等级的
// 让枚举成员默认为整型,并且可以在创建时进行初始化
enum Level
{DEBUG = 1,INFO,WARNING,ERROR,FATAL
};

2.2日志的格式

日志等级 时间 代码所在的文件名/行数 日志的内容

并且参数是可变参数

2.3.获取时间的方法

我们可以封装一个GetTimeString的函数,方便我们使用,提高代码的可读性。

这个函数旨在获取当前时间并将其格式化为一个字符串。

代码解析:

  1. 获取当前时间:

time_t curr_time = time(nullptr);

这行代码使用 time 函数获取当前的系统时间(自1970年1月1日以来的秒数),并将其存储在 time_t 类型的变量 curr_time 中。time 函数的参数是 nullptr,表示不需要将时间存储在提供的 time_t 对象中(因为我们已经有了 curr_time 来存储它)。

2.将时间转换为本地时间

struct tm *format_time = localtime(&curr_time);

这行代码使用 localtime 函数将 curr_time(UTC时间)转换为本地时间,并将结果存储在 struct tm 类型的指针 format_time 指向的结构体中。struct tm 是一个结构体,包含了年、月、日、小时、分钟、秒等信息。

3.错误检查

if (format_time == nullptr)  

        return "None";

如果 localtime 函数返回 nullptr,这通常意味着转换失败(尽管在实际应用中,这种情况非常罕见)。在这种情况下,函数会返回一个字符串 "None"。

4.格式化时间字符串

        snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",  

             format_time->tm_year + 1900,  

             format_time->tm_mon + 1,  

             format_time->tm_mday,  

             format_time->tm_hour,  

             format_time->tm_min,  

             format_time->tm_sec);

这段代码首先定义了一个字符数组 time_buffer,用于存储格式化后的时间字符串。然后,使用 snprintf 函数将时间格式化为 "YYYY-MM-DD HH:MM:SS" 的形式,并存储在 time_buffer 中。注意,tm_year 是从1900年开始计数的,所以需要加1900来得到当前的年份;tm_mon 是从0开始计数的,所以需要加1来得到当前的月份

代码:

std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}

2.4.日志的主体实现

参数:

这个函数接受多个参数,包括文件名、行号、是否保存日志的标志、日志级别、格式化字符串以及可变数量的参数(用于格式化字符串)

代码解析:

使用了C的可变参数列表(varargs)功能来构建一个格式化字符串。

 va_list 是一个用于访问可变参数列表的类型,

 va_start 宏用于初始化这个列表,

 vsnprintf 函数用于将格式化后的字符串写入到指定的缓冲区中,

 而 va_end 宏则用于清理与可变参数列表相关的资源。

问题:写日志的时候,为什么也要保证线程安全?

写日志时保证线程安全是至关重要的,这主要基于以下几个原因:

一、避免数据竞争和不一致

在多线程环境中,多个线程可能会同时尝试写入日志。如果没有适当的同步机制,就可能出现数据竞争,导致日志记录不完整、混乱或丢失。例如,一个线程可能正在写入日志的一部分,而另一个线程突然插入其日志记录,从而造成日志内容的交错和混乱。

二、确保日志的完整性和可读性

日志是系统调试、监控和故障排查的重要工具。如果日志记录不完整或混乱,将严重影响其可读性和实用性。保证线程安全可以确保每个日志记录都是完整和独立的,从而便于后续的分析和排查。

三、防止资源冲突和死锁

在多线程写入日志时,如果没有正确的同步机制,还可能导致资源冲突和死锁问题。例如,两个线程可能同时尝试获取对日志文件的写入权限,从而造成资源冲突和阻塞。如果这种情况得不到妥善处理,甚至可能导致系统崩溃或死锁。

四、提高系统的稳定性和可靠性

保证日志记录的线程安全可以大大提高系统的稳定性和可靠性。在并发环境下,系统需要能够正确地处理和记录所有事件和状态变化。如果日志记录出现问题,将可能导致系统状态无法准确追踪和恢复,从而影响系统的整体性能和可靠性。

综上所述,写日志时保证线程安全是非常重要的。这不仅可以避免数据竞争和不一致,确保日志的完整性和可读性,还可以防止资源冲突和死锁问题,提高系统的稳定性和可靠性。因此,在多线程环境中进行日志记录时,必须采取适当的同步机制来确保线程安全。

void LogMessage(std::string filename, int line, bool ssave, int level, const char *format, ...)
{std::string levelstr = LevelToString(level);std::string timestr = GetTimeString();pid_t selfid = getpid();char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;LockGuard lockguard(&lock);//保证日志的线程安全if (!issave){std::cout << message;}else{SaveFile(logname, message);}
}

3.日志的使用

3.1.代码解析:

do-while(0)结构:宏体被包裹在一个do { ... } while (0)结构中。

这是一种常见的技巧,用于确保宏在使用时能够正确地处理分号(;)和避免潜在的语法错误。

这种结构确保了无论宏体内部有多少语句,宏的使用都像是一个单独的语句一样。

 ##__VA_ARGS__:这是一个GCC扩展,用于处理可变数量的参数。

 ##操作符在这里的作用是,如果__VA_ARGS__为空(即没有提供额外的参数),则前面的逗号会被移除,避免语法错误。

#define LOG(level, format, ...)                                               \do                                                                        \{                                                                         \LogMessage(__FILE__, __LINE__, issave, level, format, ##__VA_ARGS__); \} while (0)

所以我们最终在使用日志的时候,第一个参数传递的就是日志的等级,接着就是我们想要打印的可变参数。

3.2.实际效果

就像上面这样使用。正常运行的效果如下图:

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

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

相关文章

【ssh 密钥生成】添加后提示无权限

记录下又一个感觉自己是菜菜的事件&#xff1b; 我根据教程在新电脑生成了 ssh 密匙&#xff0c;也在页面添加了ssh密匙&#xff1b; 但是下载代码的时候就提示权限不对&#xff0c;下载不下来&#xff1b; 百度了一堆办法&#xff0c;还是不行&#xff0c;然后大佬来&#…

solid wrok笔记记录

1.平移 crtl鼠标滚轮 2.放大缩小 鼠标滚轮滚动 3.旋转 按住鼠标滚轮 4.正视 右键 选择 5。多选 ctel 框选。。。 6. 选项卡 右上角空白处右键 7.草图&#xff0c;剪多余的线段(T),鼠标含住滑动去除 8.草图的线段掰直 选中线段后&#xff0c;左侧窗口的几…

【Scala入门学习】匹配模式match

1. match匹配 match 语句用在当需要从多个分支中进行选择的场景&#xff0c;类似于java 中的switch 语句。 语法&#xff1a; 变量 match{case "值" > 语句块1 // 语句块后不用加breakcase "值2" > 语句块2case _ > 语句块N // 类似于jav…

5、JavaScript(五)

28.jquery&#xff1a;js库 简化版本的js&#xff0c;封装了现成功能的js代码。 jquery就是一些封装好了的现成的方法&#xff0c;供我们直接使用。 jquery能实现的js都能实现。 在使用 记得先引入jquery&#xff1a;在菜鸟教程上直接用jquery的绝对路径引入&#xff0c;jq…

Gin框架操作指南03:HTML渲染

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;本教程采用工作区机制&#xff0c;所以一个项目下载了Gin框架&#xff0c;其余项目就无需重复下载&#xff0c;想了解的读者可阅读第一节&#xff1a;Gin操作指南&#…

java游戏网站源码

题目&#xff1a;java游戏网站源码 编号B22A390 主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Mysql|大数据|SSM|SpringBoot|Vue|Jsp|MYSQL等)、学习资料、JAVA源码、技术咨询 文末联系获取 感兴趣可以先收藏起来&#xff0c;以防走丢&#xff0c;有任何选题、文档编…

什么是 BloomFilter

什么是 BloomFilter 布隆过滤器&#xff08;英语&#xff1a;Bloom Filter&#xff09;是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。主要用于判断一个元素是否在一个集合中。 通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景&a…

Cocos Creator导出obj文件用于后端寻路

Cocos Creator 3.8.0 用这个扩展插件 【杨宗宝】两年前写的网格工具&#xff0c;今天将它开源了。 - Creator 3.x - Cocos中文社区carlosyzy_extensions_mesh: Cocos Creator 3.x mesh插件&#xff0c;负责网格数据的导出。合并&#xff0c;拆封等一系列操作 (gitee.com) 下…

C++ 标准库:功能与应用解析

C++ 标准库:功能与应用解析 引言 C++ 是一种广泛使用的编程语言,以其高性能和灵活性而著称。C++ 标准库(C++ Standard Library)是 C++ 语言的核心组成部分,提供了一系列预定义的类和函数,用于简化编程任务。本文将深入探讨 C++ 标准库的主要功能和应用,帮助读者更好地…

Shell脚本备份文件

需求&#xff1a;原文件备份&#xff0c;新文件覆盖掉源文件 vue项目打包自动化部署使用 假设已经将打包后得文件复制到了需要覆盖得目录得同级下 #!/bin/bashdst_folder"/home/compose/nginx/html"# 创建备份文件夹 backup_folder"$dst_folder/backup"# …

分类任务中评估模型性能的核心指标

在机器学习尤其是分类任务中&#xff0c;Accuracy&#xff08;准确率&#xff09;、Precision&#xff08;精确率&#xff09;、Recall&#xff08;召回率&#xff09;和F1 Score&#xff08;F1分数&#xff09;是评估模型性能的四个核心指标。每个指标都有其独特的含义和用途&…

【艾思科蓝】Imagen:重塑图像生成领域的革命性突破

【连续七届已快稳ei检索】第八届电子信息技术与计算机工程国际学术会议&#xff08;EITCE 2024&#xff09;_艾思科蓝_学术一站式服务平台 更多学术会议请看 学术会议-学术交流征稿-学术会议在线-艾思科蓝 目录 引言 一、Imagen模型的技术原理 1. 模型概述 2. 工作流程 …

PHP-FPM和FastCGI

文章目录 前言一. FastCGI1.定义2.工作方式3.协议4.架构5.工作原理&#xff08;请求生命周期&#xff09; 二. PHP-FPM1.定义&#xff1a;2.特性3.进程管理模式4.工作流程 三.关系与应用四.配置示例五.性能优化六.配置选项七.常见问题及解决方案 前言 PHP-FPM 是基于 FastCGI …

排序基础方法

逆序&#xff08;inversion&#xff09; 一个序列中存在元素对&#xff0c;顺序与理想顺序相反 注意事项 算法的空间复杂度&#xff0c;即便graph本身要花费VE&#xff0c;但是DFS是V&#xff0c;只考虑自身要用的。 Selection Sort&#xff08;选择排序) 方法 不断选择最…

牛客编程初学者入门训练——BC53 判断是元音还是辅音

BC53 判断是元音还是辅音 描述 KiKi开始学习英文字母&#xff0c;BoBo老师告诉他&#xff0c;有五个字母A(a), E(e), I(i), O(o),U(u)称为元音&#xff0c;其他所有字母称为辅音&#xff0c;请帮他编写程序判断输入的字母是元音&#xff08;Vowel&#xff09;还是辅音&#x…

vue3+ElementPlus+Table+Fixed属性导致行背景颜色失效

1.修改表格偶数行多选框背景颜色&#xff08;静态&#xff09; :deep(.el-table__body-wrapper tr:nth-child(even) td.el-table-fixed-column--left) {background: #1d2745; } 2.修改表格偶数行背景颜色&#xff08;静态&#xff09; :deep(.el-table__body-wrapper tr:nth…

如何在算家云搭建Video-Infinity(视频生成)

一、模型介绍 Video-Infinity是一个先进的视频生成模型&#xff0c;使用多个 GPU 快速生成长视频&#xff0c;无需额外训练。它能够基于用户提供的文本或图片提示&#xff0c;创造出高质量、多样化的视频内容。 二、模型搭建流程 1.大模型 Video-Infinity 一键使用 基础环境…

Axure使用echarts详细教程

本次使用的axure版本为rp9,下面是效果图。 接下来是详细步骤 【步骤1】在axure上拖一个矩形进来&#xff0c;命名为myChart(这个根据实际情况来,和后面的代码对应就好) 【步骤2】 点击交互->选择加载时->选择打开链接->链接外部地址 点击fx这个符号 【步骤3】在弹…

【GIT】.cr、.gitattributes 、 .gitignore和.git各文件夹讲解介绍

在 Git 项目中&#xff0c;.cr、.gitattributes 和 .gitignore 文件分别用于不同的配置和管理功能。下面分别解释这些文件的作用和用途&#xff1a; 1. .gitignore 文件 作用&#xff1a; .gitignore 文件用于指定哪些文件或目录应该被 Git 忽略&#xff0c;不会被追踪或提交…

通过API进行Milvus实例配置

更新Milvus各个组件的配置参数。 调试 您可以在OpenAPI Explorer中直接运行该接口&#xff0c;免去您计算签名的困扰。运行成功后&#xff0c;OpenAPI Explorer可以自动生成SDK代码示例。 ​编辑调试 授权信息 下表是API对应的授权信息&#xff0c;可以在RAM权限策略语句的…