news 2026/5/1 9:09:34

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/4/18 3:06:53

Python简介

Python 是一个高层次的结合了解性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&#xff0c;相比其他语言经常使用英文关键字&#xff0c;其他语言的一些标点符号&#xff0c;它具有比其他语言更有特色语法结构。 Python 是一种解释型语言&#xff…

作者头像 李华
网站建设 2026/5/1 8:29:54

0 基础小白如何快速入门网络安全?这份指南帮你少走弯路

0 基础小白如何快速入门网络安全&#xff1f;这份指南帮你少走弯路 一、为什么要学网络安全&#xff1f; 在互联网时代&#xff0c;网络安全早已不是 “黑客” 的专属领域。从大学生的个人信息保护&#xff0c;到企业的数据安全&#xff0c;甚至国家的网络主权&#xff0c;都离…

作者头像 李华
网站建设 2026/4/27 19:32:53

焊接机器人设计

第2章 焊接机器人的总体方案 该设计的目的是为了设计一台焊接机器人&#xff0c;本章主要对焊接机器人的机械结构部分进行设计和分析。 2.1 总体设计的思路 设计机器人大体上可分为两个阶段&#xff1a; (1) 系统分析阶段 1&#xff09;根据系统的目标&#xff0c;明确所采用机…

作者头像 李华