【Linux】写一个日志类

文章目录

  • 1. 源代码
  • 2. 函数功能概览
  • 3. 代码详细解释
    • 3.1 头文件和宏定义
    • 3.2 Log类定义
    • 3.3 打印日志的方法
    • 3.4 操作符重载和析构函数
    • 3.5 可变参数函数的原理
  • 4. 测试用例

在这里插入图片描述

1. 源代码

下面代码定义了一个 Log 类,用于记录日志信息。这个类支持将日志信息输出到屏幕、单个文件或者按日志级别分类的多个文件。

主要功能

  • 日志级别转换:将日志级别从整数转换为字符串。
  • 日志输出方式:支持屏幕输出、单个文件输出和分类文件输出。
  • 日志格式化:支持格式化日志信息,包括时间戳和自定义日志内容。
  • 文件操作:使用系统调用进行文件操作,确保日志写入文件中。
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(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 printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){// ./log/ + log.txtstd::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";// log.txt.Debug/Warning/Fatalfilename += levelToString(level);printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式: 默认部分 + 自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);printLog(level, logtxt);}private:int printMethod;std::string path;
};

2. 函数功能概览

  1. 宏定义

    • SIZE: 定义缓冲区大小为1024。
    • Info, Debug, Warning, Error, Fatal: 定义日志级别。
    • Screen, Onefile, Classfile: 定义日志输出方式。
    • LogFile: 定义默认日志文件名为 “log.txt”。
  2. 类的构造函数 Log()

    • 初始化日志输出方式为 Screen(输出到屏幕)。
    • 设置日志文件路径为 “./log/”。
  3. Enable 函数

    • 设置日志输出方式。
  4. levelToString 函数

    • 将日志级别转换为对应的字符串表示。
  5. printLog 函数

    • 根据设置的输出方式调用对应的日志打印函数(ScreenOnefileClassfile)。
  6. printOneFile 函数

    • 将日志写入单个文件。文件名为传入的 logname,实际路径为 path + logname
  7. printClassFile 函数

    • 将日志按级别分类写入不同的文件,文件名为 “log.txt.日志级别”。
  8. 析构函数 ~Log()

    • 没有特殊操作。
  9. 重载的 operator()

    • 使用可变参数处理日志信息,格式化后调用 printLog 打印日志。

3. 代码详细解释

3.1 头文件和宏定义

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"
  • #pragma once:确保头文件只被编译一次。
  • #include:引入了标准库和系统头文件。
  • #define:定义了一些常量,用于日志级别和输出方式。

3.2 Log类定义

class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(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";}}
  • Log():构造函数,初始化日志输出方式为屏幕输出(Screen),并设置日志文件路径为./log/
  • Enable(int method):设置日志输出方法。
  • levelToString(int level):将日志级别转换为字符串。

3.3 打印日志的方法

    void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level);printOneFile(filename, logtxt);}
  • printLog(int level, const std::string &logtxt):根据不同的输出方式打印日志。
  • printOneFile(const std::string &logname, const std::string &logtxt):将日志写入单个文件。
  • printClassFile(int level, const std::string &logtxt):根据日志级别写入分类文件。

3.4 操作符重载和析构函数

    ~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);printLog(level, logtxt);}

operator():重载了函数调用操作符,使得Log类的对象可以像函数一样被调用。

  • time_t t = time(nullptr); 获取当前时间。
  • struct tm *ctime = localtime(&t); 将时间转换为本地时间结构。
  • snprintf()格式化时间和日志级别。
  • va_list s; 定义一个可变参数列表。
  • va_start(s, format); 初始化可变参数列表。
  • vsnprintf(rightbuffer, sizeof(rightbuffer), format, s); 将可变参数格式化为字符串。
  • va_end(args); 结束可变参数处理。
  • leftbufferrightbuffer 拼接到 logtxt 中。
  • printLog(level, logtxt)输出日志。

3.5 可变参数函数的原理

C++ 的可变参数函数使用 stdarg.h 中定义的一组宏来处理任意数量的参数。这些宏包括:

  • va_list: 声明一个变量来存储参数列表。
  • va_start: 初始化参数列表。
  • va_arg: 访问参数列表中的下一个参数。
  • va_end: 结束参数列表的访问。

4. 测试用例

下面是一些测试用例,用于测试多参数输入和不同的日志输出方式。

#include "log.hpp"int main()
{Log logger;// 测试屏幕输出logger.Enable(Screen);logger(Info, "This is an info log with no parameters");logger(Warning, "This is a warning log with one parameter: %d", 42);logger(Error, "This is an error log with two parameters: %s and %d", "error_code", 404);// 测试单文件输出logger.Enable(Onefile);logger(Debug, "Debug log to one file with one parameter: %f", 3.14);logger(Fatal, "Fatal log to one file with no parameters");// 测试分类文件输出logger.Enable(Classfile);logger(Info, "Info log to class file");logger(Warning, "Warning log to class file with two parameters: %s and %d", "test", 123);return 0;
}

这些测试用例将测试以下功能:

  1. 不同日志级别的信息输出。
  2. 带有不同数量参数的日志格式化。
  3. 日志输出到屏幕、单文件和分类文件。

注意:要保证日志目录存在(默认为 ./log/ ),日志文件才会自动创建在该目录内。


END

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

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

相关文章

T检验——单样本t检验/两独立样本t检验/配对样本t检验

T检验——单样本t检验/两独立样本t检验/配对样本t检验 1.单样本t检验1.1 适用范围 2. &#xff08; 独立样本t检验&#xff09;两独立样本t检验3.ANOVA多组样本显著性检验&#xff08;2组以上&#xff09;4. 配对样本T检验 1.单样本t检验 1.1 适用范围 单样本t检验:即已知样本…

virtualbox虚拟机、centos7安装增强工具

文章目录 1. virtualBox语言设置2. 设置终端启动快捷键3. 添加virtualbox 增强工具4. 设置共享文件夹 1. virtualBox语言设置 virtualbox -> file -> perferences -> language ->选择对应的语言 -> OK virtualbox -> 管理 -> 全局设定 -> 语言 -> …

Redis数据类型(下篇)

5.Redis有序集合zset(sorted set) 本质就是在set的基础上&#xff0c;每个val值前面加了一个score分数值。 &#xff08;1&#xff09;向有序集合中添加多个&#xff08;或者一个&#xff09;元素和其对应的分数 127.0.0.1:6379> zadd zset1 100 a 90 b 80 c 70 d 60 e (…

C++入门3——类与对象2(类的6个默认成员函数)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 构造函数的概念 2.2 构造函数的特性 3. 析构函数 3.1 析构函数的概念 3.2 析构函数的特性 4.拷贝构造函数 4.1 拷贝构造函数的概念 4.2 拷贝构造函数的特性 5.赋值运算符重载函数 5.1运算符重载函数 5.2 赋值运算符重…

如何跨渠道分析销售数据 - 7年制造业销售经验小结

如何跨渠道分析销售数据 - 7年制造业销售经验小结&#xff08;1&#xff09; 【前言】 在我过去7年销售工作生涯中&#xff0c;从第一年成为公司销冠后&#xff0c;我当时的确自满的一段时间&#xff0c;认为自己很了不起。但是第一年的销售业绩并没有拿到提成&#xff0c;最…

【开源】课程管理平台 JAVA+Vue.js+SpringBoot+MySQL

目录 一、项目介绍 课程管理模块 作业题目模块 考试阅卷模块 教师评价模块 部门角色菜单模块 二、项目截图 三、核心代码 一、项目介绍 Vue.jsSpringBoot前后端分离新手入门项目《课程管理平台》&#xff0c;包括课程管理模块、作业题目模块、考试阅卷模块、教师评价模…

二叉树的右视图,二叉搜索树中第k小的元素

第一题&#xff1a; 代码如下&#xff1a; vector<int> rightSideView(TreeNode* root) {vector<int> res;if (root nullptr)return res;queue<TreeNode*> q;q.push(root);int k 1;while (!q.empty()){while (k--){TreeNode* front q.front();q.pop();i…

乡镇围栏数据存储ES实践

空白的是ES渲染的问题&#xff0c;放大后看到几乎没有丢失数据 1. ES索引建立 PUT /town_fence {"settings": {"number_of_shards": 1},"mappings": {"properties": {"province": {"type": "keyword"…

ClickHouse数据管理与同步的关键技术

2024年 5 月 18 日&#xff0c;ClickHouse官方首届杭州 Meetup 活动成功举行。本次活动由 ClickHouse 和阿里云主办&#xff0c;NineData 和云数据库技术社区协办。围绕ClickHouse的核心技术、应用案例、最佳实践、数据管理、以及迁移同步等方面&#xff0c;和行业专家展开交流…

基于语音信号MFCC特征提取和GRNN神经网络的人员身份检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 MFCC特征提取 4.2 GRNN神经网络概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ...............................................…

Jupyter Notebook快速搭建

Jupyter Notebook why Jupyter Notebook Jupyter Notebook 是一个开源的 Web 应用程序&#xff0c;允许你创建和分享包含实时代码、方程、可视化和解释性文本的文档。其应用包括&#xff1a;数据清洗和转换、数值模拟、统计建模、数据可视化、机器学习等等。 Jupyter Notebo…

Vue3实战笔记(58)—从零开始掌握Vue3插槽机制,基础入门

文章目录 前言插槽基础入门总结 前言 不论是组件封装还是分析源码&#xff0c;实际开发中经常接触插槽&#xff0c;插槽是干什么用的呢&#xff1f;组件之间能够接收任意类型的 JavaScript 值作为 props&#xff0c;但组件要如何接收模板内容呢&#xff1f;在某些场景中&#…

c++(七)

c&#xff08;七&#xff09; 内联函数内联函数的特点为什么要有内联函数内联函数是如何工作的呢 类型转换异常处理智能指针单例模式懒汉模式饿汉模式 VS中数据库的相关配置 内联函数 修饰类的成员函数&#xff0c;关键字&#xff1a;inline inline 返回值类型 函数名(参数列…

vue中使用svg图像

一 、svg图像是什么 SVG&#xff08;可缩放矢量图形&#xff09;是一种图像格式&#xff0c;它以XML文档的形式存在&#xff0c;用以描述图像中的形状、线条、文本和颜色等元素。由于其基于矢量的特性&#xff0c;SVG图像在放大或改变尺寸时能够保持图形质量不受影响。这种格式…

服务器的远程桌面无法连接,服务器远程桌面无法连接问题处理教程

服务器的远程桌面无法连接&#xff0c;服务器远程桌面无法连接问题处理教程。 一、问题概述 服务器远程桌面无法连接是日常运维中常见的问题之一。它可能由多种原因造成&#xff0c;如网络问题、服务器配置错误、远程桌面服务未启动等。本教程将指导您逐步排查并解决这些问题。…

电源滤波器怎么选用

电源滤波器怎么选用 滤波器应用场景及作用第一步&#xff1a;第二步&#xff1a;第三步&#xff1a;第四步&#xff1a; 滤波器应用场景及作用 可以有效解决EMC测试无法通过、端口防护、滤除干扰、设备保护等问题 主要功能有: 1、降低主电源谐波; 2、保护驱动装置电力电子元件…

python找出100~999之间的水仙花数字

水仙花数字&#xff1a;个位&#xff0c;十位&#xff0c;百位的立方之和等于这个数本身 例如&#xff1a;153 1^35^33^3 for i in range(100, 1000):bw i // 100sw i % 100 // 10gw i % 10if bw ** 3 sw ** 3 gw ** 3 i:print(i)

Kubernetes——Pod控制器

目录 一、Pod控制器 1.定义 2.Pod与控制器的关系 3.作用 4.Pod控制器的类型组成及特点 4.1Pod控制器的类型 4.1.1ReplicaSet 4.1.2Deployment 4.1.3DaemonSet 4.1.4StateSet 4.1.5Job 4.1.6CronJob 4.2Pod与控制器的关系 二、Kubernetes中的服务发现 1.服务发现的…

内网渗透-在HTTP协议层面绕过WAF

进入正题&#xff0c;随着安全意思增强&#xff0c;各企业对自己的网站也更加注重安全性。但很多web应用因为老旧&#xff0c;或贪图方便想以最小代价保证应用安全&#xff0c;就只仅仅给服务器安装waf。 本次从协议层面绕过waf实验用sql注入演示&#xff0c;但不限于实际应用…

Ps:调整画笔工具

调整画笔工具 Adjustment Brush Tool可以将选区、创建蒙版和应用调整的传统工作流程合并为一个步骤&#xff0c;简化了对图像进行非破坏性局部调整的操作。 快捷键&#xff1a;B 调整画笔工具是 Photoshop 2024 年 5 月版&#xff08;25.9 版&#xff09;新增的工具。 ◆ ◆ …