news 2026/6/25 5:21:00

Linux 之 【日志】(实现一个打印日志的类)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 之 【日志】(实现一个打印日志的类)

目录

1.日志的简介

1.1日志的概念

1.2日志的常见格式

2.实现日志类

包含所需头文件,定义所需宏

类成员

levelToString

operator()

printLog

printOneFile&printClassFile

完整呈现


1.日志的简介

1.1日志的概念

日志是软件运行过程中产生的带时间戳的、结构化的诊断记录,是调试的重要助手

1.2日志的常见格式

一条日志信息常包括默认部分与自定义部分

默认部分常包括:日志等级与日志时间

自定义部分则包括用户自定义的日志消息

常见的日志等级为:

(1)Info:常规消息(2)Warning:报警信息(3)Error:比较严重了,可能需要立即出来(4)Fatal:致命的(5)Debug:调试

2.实现日志类

使用下述类时,注意确保./log/目录存在

  • 包含所需头文件,定义所需宏

#pragma once #include <fcntl.h> #include <iostream> #include <stdarg.h> #include <string> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.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) { } //运算符重载以记录日志消息 void operator()(int level, const char *format, ...) { } //打印函数 void printLog(int level, const std::string &logtxt) { } //向一个文件打印日志消息 void printOneFile(const std::string &logname, const std::string &logtxt) { } //分类打印日志消息 void printClassFile(int level, const std::string &logtxt) { } //无实际意义,让类看着更完整 ~Log() { } private: int printMethod;//打印方法:显示器;一个文件;多个文件 std::string path;//日志文件存放的路径 };
  • levelToString

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"; } }

根据宏定义直接转化

  • operator()

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 + 1]; snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer); // 存储或者打印 printLog(level, logtxt); }

(1)重载 operator(),使得写日志像函数调用一样自然

Log log; log(Info, "Server started on port %d", 8080); // 输出:[Info][2024-...] Server started on port 8080

(2)默认部分包含日志等级和日志时间:将时间戳改造为年月日时分秒的格式。指的注意的是年是从1900开始的,月的范围是0-11,打印时需注意

(3)对于自定义部分日志信息,需要处理可变参数:

void Log(int level, const char *format, ...) // 1. 函数声明接受可变参数 { // 2. 定义遍历指针 va_list args_ptr; // char* 指针,用于逐个“指向”内存中的可变参数 // 3. 初始化指针,定位到第一个可变参数 va_start(args_ptr, format); // 关键:编译器知道‘format’是最后一个固定参数, // 通过它的地址计算出第一个可变参数在内存中的位置, // 让 args_ptr 指向那里。 // 4. 使用指针进行格式化 char buffer[256]; vsnprintf(buffer, sizeof(buffer), format, args_ptr); // vsnprintf 内部会: // a) 解析 format 字符串中的占位符(如 %d, %s) // b) 根据占位符类型,从 args_ptr 指向的内存位置“取出”相应大小的数据 // c) 每取一个参数,自动将 args_ptr 向内存高位移动相应距离,指向下一个参数 // d) 循环直到所有占位符处理完毕 // 5. 清理指针(防止野指针) va_end(args_ptr); // 通常将 args_ptr 置为 NULL }

(4)将默认部分与自定义部分整合后调用日志打印函数

  • printLog

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; } }

日志打印函数通过打印方法,调用对应打印方法的函数

  • printOneFile&printClassFile

void printOneFile(const std::string &logname, const std::string &logtxt) { std::string filename = path + logname; int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666); // log.txt 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); // log.txt./Info/Debug....... printOneFile(filename, logtxt); }

(1)向一个文件打印所有日志信息时,首先为默认文件名添加路径(让日志信息处于新建目录中),然后调用系统调用open打开文件并写入

(2)分类打印日志消息,只需要为每种日志定义好文件名,然后复用向一个文件打印日志信息的代码就可

  • 完整呈现

使用下述类时,注意确保./log/目录存在

#pragma once #include <fcntl.h> #include <iostream> #include <stdarg.h> #include <string> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.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 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 + 1]; snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer); // 存储或者打印 printLog(level, logtxt); } 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 filename = path + logname; int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666); // log.txt 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); // log.txt./Info/Debug....... printOneFile(filename, logtxt); } ~Log() { } private: int printMethod; std::string path; };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 10:53:38

Python教程

查看Python版本我们可以在命令窗口(Windows 使用 winR 调出 cmd 运行框)使用以下命令查看我们使用的 Python版本:python -V 或 python --version以下命令执行结果如下&#xff1a;你也可以进入Python的交互式编程模式&#xff0c;查看版本&#xff1a;第一个Python程序对于大多…

作者头像 李华
网站建设 2026/6/20 6:27:13

【课程设计/毕业设计】基于springboot的实验幼儿园信息管理系统基于springboot的幼儿园管理系统【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/21 22:18:59

【UVM】UVM寄存器测试标准工具详解及一个完整例子示意

文章目录 UVM寄存器测试标准工具详解 一、UVM寄存器测试标准工具列表 二、详细使用流程及代码示例 1. uvm_reg_hw_reset_seq - 硬件复位测试序列 2. uvm_reg_bit_bash_seq - 位位测试序列 3. uvm_reg_access_seq - 寄存器访问测试序列 三、针对特定寄存器的测试控制 四、寄存器…

作者头像 李华
网站建设 2026/6/20 4:34:48

普通本科慎入网安!转行 5 年月薪 2 万 +,掏心窝的大实话

别盲目冲网安&#xff01;普通本科转行5年&#xff0c;月薪2万的真心话 网安行业确实火&#xff0c;但真话难听&#xff1a;这行超卷&#xff0c;缺的是能干活的实战派&#xff0c;不是凑数的小白。 我普通本科出身&#xff0c;转行网安5年&#xff0c;如今月薪2万&#xff0…

作者头像 李华
网站建设 2026/6/21 11:39:56

警惕“链经济”背后的风险陷阱

在线上线下融合、乡村振兴成为国家战略的背景下&#xff0c;一种名为“链经济”的新型商业模式正在悄然兴起。以“某了宝”为代表的平台&#xff0c;宣称以农特产品为纽带&#xff0c;打造“消费分享创业”的生态闭环。 这种模式听起来既符合政策导向&#xff0c;又具有商业创…

作者头像 李华
网站建设 2026/6/20 19:40:56

【开题答辩全过程】以 基于协同过滤算法的旅游推荐系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华