【Linux】简易日志系统

目录

一、概念

二、可变参数

三、日志系统


一、概念

一个正在运行的程序或系统就像一个哑巴,一旦开始运行我们很难知晓其内部的运行状态。

但有时在程序运行过程中,我们想知道其内部不同时刻的运行结果如何,这时一个日志系统可以有效的帮助我们监控程序的运行状态。

如果系统或程序发生了错误或存在bug,通过日志的内容我们也可以很快的知道故障的原因并定位错误的位置

一个成熟的日志至少需要包含以下信息:

  • 日志时间
  • 日志等级

根据情况可将日志划分为不同的等级,例如常规信息、警告信息、严重错误、致命错误、调试信息

  • 日志内容
  • 文件名称或行号


二、可变参数

日志的内容需要我们指定格式并传参,而参数的个数是不确定的。因此在学习编写日志系统之前,我们先了解一下可变参数的用法

以下是对可变参数进行操作时需要用到的函数/宏

#include <stdarg.h>void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);

我们以一个可以同时累加多个变量的函数为例:

int sum(int n, ...)
{}

形参在实例化时会从右向左进行压栈,也就是说多个参数在函数栈帧中是连续的,因此我们可以通过地址的偏移来依次访问到所有的参数

首先:

int sum(int n, ...)
{va_list s;va_start(s, n);
}

其中va_list实际上就是char*, 而va_start可以让s指向参数n的下一个参数,也就是可变参数的第一个参数的位置。此时我们就有了获取第一个参数内容的前提

这也是为什么printf等支持可变参数的函数中必须至少要有一个确定的参数,有了该参数才能找到可变参数的起始地址

int sum(int n, ...)
{va_list s;va_start(s, n);int sum = 0;while(n--){sum += va_arg(s, int);}va_end(s);return sum;
}

其中,va_arg传入s和可变参数的类型,用于提取s指向的参数,并且移动s到下一个参数的位置

va_end将s置为空

测试效果:

拓展问题:如果可变参数中,不同参数有不同的类型怎么办?

这也是为什么printf的第一个参数需要传入一个用于控制格式的字符串,通过遍历字符串就能知道可变参数中有哪些类型了


三、日志系统

本文实现的日志系统具备以下功能: 

  • 包含日志等级、日志时间、日志内容
  • 将日志功能封装成类,并重载了函数调用运算符
  • 可以选择将日志输出到终端、输出到同一文件或按照日志等级分类输出到不同文件
  • 用户可自定义日志内容格式

如果要让日志包含文件名和行号,则可以通过宏定义__FILE__和__LINE__获取文件名和行号

接下来是完整代码(附注释)

#pragma once#include <iostream>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define SIZE 1024 // 缓冲区大小// 日志的输出方式
#define Screen 1    // 输出到显示器
#define Same_file 2 // 输出到同一文件
#define Diff_file 3 // 按照等级输出到不同文件#define Filename "log.txt"class Log
{
public:Log(){_method = Screen; // 默认输出到显示器}void output(int method) // 更改输出方式{_method = method;}std::string level2string(int level) // 日志等级转换字符串{switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void operator()(int level, const char *format, ...){va_list s;va_start(s, format); // s指向可变参数messagehandle(level, format, s);}void messagehandle(int level, const char *format, va_list s) // 整合日志字符串{time_t t = time(nullptr);         // 获取时间戳struct tm *ctime = localtime(&t); // 将时间戳转换为时间char levelAndtime[SIZE];          // 日志等级和时间部分snprintf(levelAndtime, sizeof(levelAndtime), "[%s][%d-%d-%d %02d:%02d:%02d]", level2string(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);char content[SIZE]; // 用户自定义的内容部分vsnprintf(content, sizeof(content), format, s);va_end(s);char message[SIZE * 2]; // 整合所有部分snprintf(message, sizeof(message), "%s %s\n", levelAndtime, content);OutputLog(level, message); // 将整合后的日志输出}void OutputLog(int level, const std::string &logmessage){switch (_method) // 根据输出方式进行调整{case Screen: // 输出到显示器std::cout << logmessage << std::endl;break;case Same_file: // 输出到同一文件SamefileOutput(Filename, logmessage);break;case Diff_file: // 输出到不同文件DiffileOutput(level, logmessage);break;default:break;}}void SamefileOutput(const std::string &filename, const std::string &logmessage){int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); //打开文件if(fd < 0) //打开失败return;write(fd, logmessage.c_str(), logmessage.size()); //写入日志close(fd); //关闭文件描述符}void DiffileOutput(int level, const std::string &logmessage){std::string filename = Filename;filename += ".";filename += level2string(level); //根据日志等级调整文件名SamefileOutput(filename, logmessage); //复用SamefileOutput函数}~Log(){}private:int _method; // 输出方式
};

测试:

向显示器输出日志(n%5用于模拟不同日志等级)

向同一文件中输出日志

向不同文件中输出日志

完.

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

软考无损连接判断

如何判断是否为无损连接&#xff0c;要看能否还原回最开始的关系模式 最开始的关系模式 U{A&#xff0c;B&#xff0c;C} 函数连接 F{A -> B}&#xff0c;这个函数连接的意思就是A可以推导出B 首先从P1开始判断&#xff0c;{ AB&#xff0c;BC } C不能通过函数依赖推导出来…

数据结构之线性表——LeetCode:328. 奇偶链表,86. 分隔链表,24. 两两交换链表中的节点

328. 奇偶链表 题目描述 328. 奇偶链表 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推。…

头条|司法部公法局局长访谈:推进高水平公立鉴定机构建设!加快推进司法鉴定立法!

主持人&#xff1a;大家好&#xff0c;我是司法部AI主播司政轩。为切实做好党的二十届三中全会精神学习宣传贯彻&#xff0c;积极反映司法部及地方司法行政机关学习全会精神的体会收获和贯彻落实举措&#xff0c;我们推出了“学习宣传贯彻党的二十届三中全会精神--司法行政微访…

Elasticsearch 检索优化:停用词的应用

Elasticsearch 检索优化&#xff1a;停用词的应用 场景描述 目前在 Elasticsearch 集群中存储约 1.5 亿篇文章数据&#xff0c;随着数据量的增加&#xff0c;检索性能问题逐渐显现。在列表检索和聚合操作中&#xff0c;CPU 消耗飙升至 100%&#xff0c;并且检索耗时较长&…

私域电商:自主发展新路径与创新模式融合

摘要&#xff1a;本文深入探讨了私域电商相较于传统电商在自主权方面的优势&#xff0c;并结合 AI 智能名片、链动 21 模式以及商城小程序等创新元素&#xff0c;阐述了私域电商如何为商家提供更大的发展空间和自主权&#xff0c;以及这些创新模式在私域电商中的应用价值&#…

口碑最好的头戴式耳机是哪些?高品质头戴式耳机对比测评揭晓

头戴式耳机以其出色的音质表现和舒适的佩戴体验&#xff0c;成为了音乐爱好者和日常通勤用户的热门选择。而在众多品牌和型号中&#xff0c;口碑最好的头戴式耳机是哪些&#xff1f;面对市场上丰富的选择&#xff0c;找到一款音质优良、佩戴舒适且性价比高的耳机并不容易。今天…

ESP8266+DHT11+Python制作一个物联网温湿度传感器

ESP8266是一款低功耗、高集成度的Wi-Fi SOC&#xff08;System on Chip&#xff0c;系统级芯片&#xff09;&#xff0c;这款芯片专为物联网&#xff08;IoT&#xff09;应用而设计&#xff0c;常见开发ESP8266的环境可以使用Arduino或者ESP8266 RTOS SDK、NodeMCU&#xff0c;…

【JavaScript】数据结构之链表(双指针、滑动窗口)

什么是链表&#xff1f; 多个元素存储的列表链表中的元素在内存中不是顺序存储的&#xff0c;而是通过“next”指针联系在一起的&#xff0c;这个“next”可以自定义。JS中的原型链原理就是链表结构&#xff0c;是通过__proto__指针联系在一起的。 双指针形式 对撞指针&am…

你还在为试衣间排队烦恼吗?AI魔法绘,让虚拟试衣触手可及!

我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 大家好&#xff01;今天我要跟大家聊聊一个超级有趣又实用的新…

施耐德EcoStruxure Machine SCADA Expert(EMSE)报警记录进阶(十六)

针对某些特殊行业&#xff08;诸如医药行业&#xff09;的设备生产需要符合GMP相关规定&#xff0c;这就导致需要数据溯源。 EMSE可以通过与sql的连接实现报警历史记录的永久存储。 1.EMSE打开相关配置 2.sql创建表单 用于报警历史数据的存储容器 3.EMSE内选择sql表单 4.现在…

mistune,一个神奇的 Python 库!

大家好&#xff0c;今天为大家分享一个神奇的 Python 库 - mistune。 Github地址&#xff1a;https://github.com/lepture/mistune Markdown 是一种轻量级的标记语言&#xff0c;以其简洁的语法和可读性广泛应用于文档编写、博客发布和在线内容管理系统中。Python 作为一门灵活…

【ESP32】ESP-IDF开发 | UART通用异步收发传输器+串口收发例程

1. 简介 UART可以说是开发者使用得最多的外设之一了&#xff0c;打印log几乎都是使用串口来实现的。UART是一种异步全双工的通信方式&#xff0c;异步传输的特性使得它仅需2根线就可以完成全双工的传输&#xff0c;但这也要求发送端和接收端的速率、停止位、奇偶校验位等都要相…

分布式计算技术是什么?在数据集成值得作用?

数据是现代科技技术的基础&#xff0c;面对爆炸性数据的增长&#xff0c;要求计算能力要求更高、数据整合和处理更有效&#xff0c;如何应对数据集成带来的挑战&#xff1f;本文将探讨分布式计算技术在数据集成中的优化作用。 一 分布式计算技术。 定义&#xff1a;分布式计算…

笔记:将WPF中可视化元素(Visual)保存为图像,如PNG,JPEG或BMP的方法简介

一、目的&#xff1a;将WPF中可视化元素&#xff08;Visual&#xff09;保存为图像&#xff0c;如PNG,JPEG或BMP的方法简介 BitmapEncoder 是 WPF 中用于将图像数据编码为特定格式的基类。它提供了将 BitmapSource 对象保存为各种图像格式&#xff08;如 PNG、JPEG、BMP 等&…

Android Choreographer 监控应用 FPS

Choreographer 是 Android 提供的一个强大的工具类&#xff0c;用于协调动画、绘制和视图更新的时间。它的主要作用是协调应用的绘制过程&#xff0c;以确保流畅的用户体验。Choreographer 也可以帮助我们获取帧时间信息&#xff0c;从而为性能监测和优化提供重要的数据支持。 …

C++—vector的常见接口与用法(正式进入STL)

目录 0.提醒 1.介绍 2.构造 1.正常构造 2.默认值构造 3.调用默认构造函数构造 3.遍历 1.迭代器 2.范围for 3.下标访问 4.容量 1.capacity&#xff1a;返回当前容器的容量 2.reserve&#xff1a;如果传的k比当前容量大&#xff0c;则扩容到比k大或者等于k的数&…

车载软件调试工具系列---Trace32简介UI界面简介

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

二叉搜索树的使用及其详细解析

1.概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结点的值都⼤于等于根结点的值 • 它…

JDK如何下载源码?

文章目录 JDK如何下载源码&#xff1f;JDK源码介绍下载JDK源码idea配置源码路径 JDK如何下载源码&#xff1f; JDK&#xff08;Java Development Kit&#xff09;是开发Java应用程序的基础工具包&#xff0c;包含了编译、运行和调试Java应用程序所需的所有工具。JDK源码主要指…

2024年中国研究生数学建模竞赛D题大数据驱动的地理综合问题

2024年中国研究生数学建模竞赛D题 大数据驱动的地理综合问题 地理系统是自然、人文多要素综合作用的复杂巨系统[1-2]&#xff0c;地理学家常用地理综合的方式对地理系统进行主导特征的表达[3]。如以三大阶梯概括中国的地形特征&#xff0c;以秦岭—淮河一线和其它地理区划的方…