当前位置: 首页 > news >正文

一个简洁的 C++ 日志模块实现

一个简洁的 C++ 日志模块实现

1. 引言

日志功能在软件开发中扮演着至关重要的角色,它帮助开发者追踪程序执行过程、诊断问题以及监控系统运行状态。本文介绍一个使用 C++ 实现的轻量级日志模块,该模块支持多日志级别、线程安全,并提供了简洁易用的接口。

2. 代码实现

2.1 主程序 (main.cpp)

#include "log.h"int main(int argc, char *argv[])
{Log::GetInstance().Init("./log");Log::GetInstance().WriteLog(ERROR, "error");Log::GetInstance().WriteLog(WARNING, "warning");Log::GetInstance().WriteLog(DEBUG, "debug");Log::GetInstance().WriteLog(INFO, "info");return 0;
}

2.2 头文件 (log.h)

#ifndef LOG_H
#define LOG_H#include <iostream>
#include <pthread.h>
#include <cstdio>using namespace std;// 日志级别枚举
typedef enum {ERROR = 0,WARNING,DEBUG,INFO,LEVEL_MAX
} LogLevel_en;// 日志状态枚举
typedef enum {LOG_INIT_STATE,LOG_READY_STATE,LOG_INVALID_STATE
} LogState_en;#define LOG_NUM 255Uclass Log {
private:char m_log_buf[LOG_NUM];        // 日志缓冲区pthread_mutex_t m_mutex;        // 互斥锁,保证线程安全FILE *m_log_fd;                 // 日志文件指针LogState_en m_log_state;        // 日志模块状态public:static Log& GetInstance(void);  // 获取单例实例void Init(const char *dir_file_name);  // 初始化日志系统void WriteLog(LogLevel_en log_level, const char* format, ...);  // 写日志接口private:Log(void) {m_log_state = LOG_INVALID_STATE;};~Log(void) = default;
};#endif

2.3 实现文件 (log.cpp)

#include "log.h"
#include <ctime>
#include <cstring>
#include <cstdarg>// 获取日志单例实例
Log& Log::GetInstance(void)
{static Log instance;return instance;
}// 初始化日志系统
void Log::Init(const char *dir_file_name)
{if(nullptr != dir_file_name){m_log_state = LOG_INIT_STATE;cout << "Log file: " << dir_file_name << endl;m_log_fd = fopen(dir_file_name, "a");if (m_log_fd != nullptr) {m_log_state = LOG_READY_STATE;}}
}// 写入日志信息
void Log::WriteLog(LogLevel_en log_level, const char* format, ...)
{// 参数有效性检查if((LEVEL_MAX <= log_level) || (nullptr == format)) {return;}// 日志状态检查if (LOG_READY_STATE != m_log_state) {return;}// 设置日志级别字符串char buffer_log_level[16] = {0};switch (log_level) {case ERROR:strcpy(buffer_log_level, "[error]:");break;case WARNING:strcpy(buffer_log_level, "[warning]:");break;case DEBUG:strcpy(buffer_log_level, "[debug]:");break;case INFO:strcpy(buffer_log_level, "[info]:");break;default:cout << "Invalid log level" << endl;return;}// 设置时间戳字符串time_t time_val = time(nullptr);struct tm *p_tm_val = localtime(&time_val);char buffer_time[48] = {0};snprintf(buffer_time, 48, "%.4d-%.2d-%.2d-%.2d-%.2d-%.2d:", p_tm_val->tm_year + 1900,p_tm_val->tm_mon + 1,p_tm_val->tm_mday,p_tm_val->tm_hour,p_tm_val->tm_min,p_tm_val->tm_sec);// 加锁保证线程安全pthread_mutex_lock(&m_mutex);// 组装日志前缀memset(m_log_buf, 0, sizeof(m_log_buf));u_int8_t log_len = strlen(buffer_log_level) + strlen(buffer_time) + 1;int n = snprintf(m_log_buf, log_len, "%s%s", buffer_log_level, buffer_time);if(n > 0) {// 处理可变参数va_list variable_list;va_start(variable_list, format);int m = vsnprintf(m_log_buf + n, LOG_NUM - n - 1, format, variable_list);va_end(variable_list);if(m > 0) {// 输出到控制台和文件cout << m_log_buf << endl;m_log_buf[n + m] = '\n';m_log_buf[n + m + 1] = '\0';fputs(m_log_buf, m_log_fd);fflush(m_log_fd);  // 确保数据写入磁盘} else {cout << "vsnprintf error!" << endl;}} else {cout << "snprintf error!" << endl;}// 释放锁pthread_mutex_unlock(&m_mutex);
}

2.4 Makefile 构建配置

# 目标程序名称
TGT := appCUR_DIR := $(shell pwd)# 源文件设置
SRC := $(wildcard *.cpp)
OBJ := $(patsubst %.cpp,%.o,$(SRC))# 编译选项设置
CPPFLAGS := -I.
CPPFLAGS += -pthread
CPPFLAGS += -I${CUR_DIR}/include# 编译器标志
CXXFLAGS := -Wall -O2# 默认构建目标
all: $(TGT)@echo "Build successful"$(TGT): $(OBJ)$(CXX) -std=c++11 $(CPPFLAGS) $(CXXFLAGS) $^ -o $@%.o: %.cpp$(CXX) -std=c++11 $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@# 清理构建文件
clean:
ifneq ($(wildcard $(OBJ)),)@rm $(OBJ)
else@echo "No object files to remove"
endif
ifneq ($(wildcard $(TGT)),)@rm $(TGT) 
else@echo "No target executable to remove"
endif# 仅清理对象文件
obj_clean:
ifneq ($(wildcard $(OBJ)),)@rm $(OBJ)
else@echo "No object files to remove"
endif.PHONY: obj_clean clean all

3. 编译与运行结果

执行构建命令:

$ make
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 -c log.cpp -o log.o
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 -c main.cpp -o main.o
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 log.o main.o -o app
Build successful

运行程序:

$ ./app 
Log file: ./log
[error]:2024-06-29-11-33-03:error
[warning]:2024-06-29-11-33-03:warning
[debug]:2024-06-29-11-33-03:debug
[info]:2024-06-29-11-33-03:info

查看生成的日志文件:

$ cat log
[error]:2024-06-29-11-23-43:error
[warning]:2024-06-29-11-23-43:warning
[debug]:2024-06-29-11-23-43:debug
[info]:2024-06-29-11-23-43:info

4. 设计特点

  1. 单例模式:确保整个应用程序中只有一个日志实例
  2. 线程安全:使用互斥锁保护共享资源,支持多线程环境
  3. 多日志级别:支持 ERROR、WARNING、DEBUG、INFO 四种级别
  4. 时间戳:每条日志都包含精确到秒的时间信息
  5. 双重输出:日志同时输出到控制台和文件
  6. 格式化支持:支持 printf 风格的格式化输出

这个日志模块虽然简洁,但提供了基本日志功能所需的核心特性,适合在中小型项目中使用。

http://www.xdnf.cn/news/1355905.html

相关文章:

  • 【数位DP】D. From 1 to Infinity
  • 金山办公的服务端开发工程师-25届春招笔试编程题
  • Python训练营打卡 DAY 45 Tensorboard使用介绍
  • 基于电磁频谱地图的辐射源定位算法复现
  • 基于TimeMixer现有脚本扩展的思路分析
  • 基础IO
  • CryptSIPVerifyIndirectData函数分析
  • 刷题日记0823
  • 环境 (shell) 变量
  • Nacos-12--扩展:@RefreshScope和@ConfigurationProperties实现热更新的原理
  • Kubernetes笔记整合-1
  • 一种通过模板输出Docx的方法
  • LeakyReLU和ReLU的区别
  • 探索 JUC:Java 并发编程的神奇世界
  • KVM虚拟化:提升企业效率的利器
  • 【嵌入式】【搜集】RTOS相关技术信息整理
  • 微信小程序界面常用操作
  • SpringBoot自动装配原理深度解析
  • 电蚊拍的原理及电压电容参数深度解析:从高频振荡到倍压整流的完整技术剖析
  • Trae Solo模式生成一个旅行足迹App
  • 最新短网址源码,防封。支持直连、跳转。 会员无广
  • Azure Kubernetes Service (AKS)
  • 视觉革命:云渲染如何让创意不再受限于硬件
  • qt ElaWidgetTools第一个实例
  • leetcode刷题记录03——top100题里的6道简单+1道中等题
  • H264编解码过程简述
  • 算法 ---哈希表
  • C 语言标准输入输出头文件stdio.h及其常见用法
  • 【KO】前端面试六
  • 【40页PPT】企业如何做好大数据项目的选型(附下载方式)