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

C++通用日志模块

概述

在 C++ 项目中开发时经常需要日志模块,为了不引入其它第三方日志模块包的基础上,基于标准的 C++17 的基础自己封装了一个日志模块

功能总结

  • 日志分等级(DEBUG / INFO / WARN / ERROR)
  • 支持日志文件轮转,自动备份旧日志
  • 彩色控制台输出
  • 线程安全(加锁)
  • 单例模式
  • 支持日志文件名带日期
  • 配置日志目录、文件名模板、等级、最大大小、保留个数

代码

#ifndef LOGMANAGER_H
#define LOGMANAGER_H#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <filesystem>
#include <chrono>
#include <ctime>
#include <mutex>
#include <iomanip>namespace fs = std::filesystem;// 定义日志等级
enum class LogLevel {DEBUG,  // 调试信息INFO,   // 普通信息WARN,   // 警告信息ERROR   // 错误信息
};// 日志管理器单例类
class LogManager {
public:// 获取单例实例static LogManager& getInstance() {static LogManager instance;return instance;}// 禁止拷贝构造和赋值LogManager(const LogManager&) = delete;LogManager& operator=(const LogManager&) = delete;// 配置日志参数void configure(const std::string& logDir = "logs",                     // 日志目录const std::string& filenamePattern = "logs_{time}.log", // 日志文件名格式,{time}会替换成日期LogLevel level = LogLevel::INFO,                        // 日志等级std::size_t maxSize = 10 * 1024 * 1024,                 // 最大文件大小,默认10MBint backupCount = 6) {                                  // 最大保留备份数std::lock_guard<std::mutex> lock(m_mutex);if (m_configured) return;  // 防止重复配置m_configured = true;m_logDir = logDir;m_filenamePattern = filenamePattern;m_logLevel = level;m_maxSize = maxSize;m_backupCount = backupCount;// 如果日志目录不存在则创建if (!fs::exists(m_logDir)) {fs::create_directories(m_logDir);}openLogFile(); // 打开日志文件}// 写入日志void log(LogLevel level, const std::string& message, const char* file, int line) {if (level < m_logLevel) return; // 如果日志等级不满足,直接返回std::lock_guard<std::mutex> lock(m_mutex);rotateIfNeeded(); // 检查是否需要日志轮转// 获取日志等级字符串std::string levelStr = getLevelString(level);// 获取当前时间字符串std::string timeStr = getCurrentTimeString();// 写入日志文件m_logFile << timeStr << " [" << levelStr << "] "<< fs::path(file).filename() << ":" << line<< " - " << message << std::endl;// 控制台彩色输出std::string colorCode = getColorCode(level);std::string resetCode = "\033[0m";std::cout << colorCode<< timeStr << " [" << levelStr << "] "<< fs::path(file).filename() << ":" << line<< " - " << message<< resetCode << std::endl;}private:// 私有构造函数,保证单例模式LogManager() : m_configured(false) {}// 获取日志等级对应的终端颜色代码std::string getColorCode(LogLevel level) {switch (level) {case LogLevel::DEBUG: return "\033[36m"; // 青色case LogLevel::INFO:  return "\033[32m"; // 绿色case LogLevel::WARN:  return "\033[33m"; // 黄色case LogLevel::ERROR: return "\033[31m"; // 红色}return "\033[0m"; // 默认颜色}// 打开日志文件void openLogFile() {std::string timeStr = getCurrentTimeStringForFilename(); // 获取当前日期字符串std::string filename = m_filenamePattern;size_t pos = filename.find("{time}");if (pos != std::string::npos) {filename.replace(pos, 6, timeStr); // 替换文件名中的{time}}m_logFilePath = fs::path(m_logDir) / filename;m_logFile.open(m_logFilePath, std::ios::app); // 追加方式打开}// 判断文件大小,必要时执行日志轮转void rotateIfNeeded() {if (fs::exists(m_logFilePath) &&fs::file_size(m_logFilePath) >= m_maxSize) {m_logFile.close();// 依次重命名旧文件,从最大备份编号往回推for (int i = m_backupCount - 1; i >= 0; --i) {std::string oldName = m_logFilePath.string() + "." + std::to_string(i);std::string newName = m_logFilePath.string() + "." + std::to_string(i + 1);if (fs::exists(oldName)) {fs::rename(oldName, newName);}}// 当前日志文件改名为 .0fs::rename(m_logFilePath, m_logFilePath.string() + ".0");openLogFile(); // 新建日志文件}}// 获取日志等级对应的字符串std::string getLevelString(LogLevel level) {switch (level) {case LogLevel::DEBUG: return "DEBUG";case LogLevel::INFO:  return "INFO";case LogLevel::WARN:  return "WARN";case LogLevel::ERROR: return "ERROR";}return "UNKNOWN";}// 获取当前时间字符串,格式:2025-05-29 20:15:03.123std::string getCurrentTimeString() {auto now = std::chrono::system_clock::now();auto time = std::chrono::system_clock::to_time_t(now);auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;std::ostringstream oss;oss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S")<< "." << std::setfill('0') << std::setw(3) << ms.count();return oss.str();}// 获取当前日期字符串,供文件名使用,格式:2025-05-29std::string getCurrentTimeStringForFilename() {auto now = std::chrono::system_clock::now();auto time = std::chrono::system_clock::to_time_t(now);std::ostringstream oss;oss << std::put_time(std::localtime(&time), "%Y-%m-%d");return oss.str();}// 成员变量定义bool m_configured;                  // 是否已配置std::string m_logDir;               // 日志目录std::string m_filenamePattern;      // 日志文件名格式LogLevel m_logLevel;                // 日志等级std::size_t m_maxSize;              // 单个日志最大大小int m_backupCount;                  // 备份日志个数std::ofstream m_logFile;            // 日志文件输出流fs::path m_logFilePath;             // 当前日志文件路径std::mutex m_mutex;                 // 互斥锁,线程安全
};// 定义日志宏,自动记录当前文件名和行号
#define LOG_DEBUG(msg) LogManager::getInstance().log(LogLevel::DEBUG, msg, __FILE__, __LINE__)
#define LOG_INFO(msg)  LogManager::getInstance().log(LogLevel::INFO,  msg, __FILE__, __LINE__)
#define LOG_WARN(msg)  LogManager::getInstance().log(LogLevel::WARN,  msg, __FILE__, __LINE__)
#define LOG_ERROR(msg) LogManager::getInstance().log(LogLevel::ERROR, msg, __FILE__, __LINE__)#endif // LOGMANAGER_H

运行示例


#include "LogManager.h"int main() {// 配置日志系统,只需要调用一次LogManager::getInstance().configure("logs",                     // 日志目录"app_log_{time}.log",       // 日志文件名模板,{time} 会被替换成日期LogLevel::DEBUG,            // 最低日志等级(DEBUG/INFO/WARN/ERROR)1024 * 1024,                // 日志文件最大 1MB(测试方便改小一点)3                           // 最多保留 3 个备份日志);// Log output example
LOG_DEBUG("This is a debug message");
LOG_INFO("This is an INFO level message");
LOG_WARN("This is a WARN level message");
LOG_ERROR("This is an ERROR level message");return 0;
}

在这里插入图片描述

相关文章:

  • 【Linux篇】叩响新世界的大门:线程
  • 在VirtualBox中打造高效开发环境:CentOS虚拟机安装与优化指南
  • vue3 导出excel
  • 第一章 Linux的例行性工作(计划任务)
  • vite配置一个css插件
  • 【NATURE氮化镓】GaN超晶格多沟道场效应晶体管的“闩锁效应”
  • R3GAN训练自己的数据集
  • 【深度剖析】义齿定制行业数字化转型模式创新研究(上篇2:痛点和难点分析)
  • 架构设计之慢SQL监控
  • 【Redis】string 类型
  • 第5讲、Odoo 18 CLI 模块源码全解读
  • 大数据学习(124)-spark数据倾斜
  • Java中的设计模式实战:单例、工厂、策略模式的最佳实践
  • 自动化测试实例:Web登录功能性测试(无验证码)
  • 算法日记32:埃式筛、gcd和lcm、快速幂、乘法逆元
  • HTML5 Canvas 星空战机游戏开发全解析
  • DNS缓存
  • Java面试实战:从Spring Boot到微服务与AI的全栈挑战
  • java直接获取MyBatis将要执行的动态sql命令(不是拦截器方式)
  • 【海康USB相机被HALCON助手连接过后,MVS显示无法连接故障。】
  • 沈阳营销型网站设计教程/百度搜索推广操作简要流程
  • 汽车网站大全/抖音营销推广方案
  • 快站建站/石家庄百度推广优化排名
  • 整合营销传播之父/排名sem优化软件
  • 乌鲁木齐网站建设电话/今天nba新闻最新消息
  • 环艺做网站/微信公众号软文怎么写