实现简单日志管理类 + 单例模式分析优化
#pragma once #include <stdio.h> #include <iostream> #include <string> #include <vector> #include <list> #include <stdarg.h> #include <pthread.h> #include <unistd.h> #include <mutex> #include <thread> #include <functional> #include <condition_variable> using namespace std; // 打印宏 #define _DEBUG(...) LoggerMng::ptrInstance().get()->writeLog(LoggerMng::LEVEL_DEBUG, __VA_ARGS__) #define _INFO(...) LoggerMng::ptrInstance().get()->writeLog(LoggerMng::LEVEL_INFO, __VA_ARGS__) #define _WARN(...) LoggerMng::ptrInstance().get()->writeLog(LoggerMng::LEVEL_WARN, __VA_ARGS__) #define _ERROR(...) LoggerMng::ptrInstance().get()->writeLog(LoggerMng::LEVEL_ERROR, __VA_ARGS__) #define LOG_FILE_PATH "./log" #define INIT_GARBAGE 1 /** * @brief The LoggerMng class 日志管理类 */ class LoggerMng { public: ~LoggerMng(); // 日志等级 enum LOG_LEVEL { LEVEL_DEBUG = 0, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR }; // 改进1: 利用其他类的析构来回收单例对象 static LoggerMng *getInstance(); class GarbageCollector { public: ~GarbageCollector() { std::cout << __PRETTY_FUNCTION__ << std::endl; if ( m_pInstance ) { delete m_pInstance; m_pInstance = nullptr; } } }; static GarbageCollector gc; /** * @brief Instance * @return */ //改进2: 使用静态局部变量(注意:要使用引用,不然外面调用会引起新的一次构建) static LoggerMng &Instance(); // 改进3: 使用智能指针自动释放 static unique_ptr<LoggerMng> &ptrInstance(); /** * @brief init 初始化日志类 * @return */ bool init(const string &filePath); /** * @brief writeLog 写文件 */ void writeLog(const LOG_LEVEL &level, const char* format, ...); private: LoggerMng(); LoggerMng(LoggerMng &l) = delete; LoggerMng &operator=(LoggerMng &l) = delete; inline string level2Str(const LOG_LEVEL &level); static LoggerMng *m_pInstance; static unique_ptr<LoggerMng> m_ptrInstance; /** * @brief threadWriteLog 写日志线程 */ void threadWriteLog(); bool m_isExit = false; // 线程是否结束 int m_today = 1993; // 日期比较 FILE *m_pFile = nullptr; // 文件句柄 std::mutex m_mutex; // 互斥锁 unique_ptr<std::thread> m_threadPtr; // 线程指针 std::condition_variable m_conVar; // 条件变量 std::list<std::string> m_strList; // 字符队列, 存放打印字符 };
#include "LoggerMng.h" LoggerMng *LoggerMng::m_pInstance = nullptr; #if (INIT_GARBAGE == 1) LoggerMng::GarbageCollector gc; #endif unique_ptr<LoggerMng> LoggerMng::m_ptrInstance(nullptr); LoggerMng &LoggerMng::Instance() { static LoggerMng instance; // 静态局部变量, 要使用引用返回值, 否则调用一次就构建一次,浪费资源 return instance; } LoggerMng *LoggerMng::getInstance() { if ( m_pInstance == nullptr ) // 静态成员使用指针的话,程序退出时无法指针类的析构函数 { m_pInstance = new LoggerMng(); } return m_pInstance; } unique_ptr<LoggerMng> &LoggerMng::ptrInstance() { if ( m_ptrInstance == nullptr ) { m_ptrInstance.reset(new LoggerMng()); } return m_ptrInstance; } bool LoggerMng::init(const string &filePath) { if ( filePath.empty() ) { return false; } // 开启写日志线程 m_threadPtr.reset(new std::thread(std::bind(&LoggerMng::threadWriteLog, this))); // 创建日志文件 time_t t = time(NULL); struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; char buf[256] = {0}; snprintf(buf, 255, "%d_%02d_%02d.log",my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday); string fileName = buf; m_today = my_tm.tm_mday; m_pFile = fopen(fileName.c_str(), "a"); return (m_pFile != nullptr); } void LoggerMng::writeLog(const LoggerMng::LOG_LEVEL &level, const char *format, ...) { string levelStr = level2Str(level); struct timeval now = {0,0}; gettimeofday(&now, NULL); time_t t = now.tv_sec; struct tm* sys_tm = localtime(&t); struct tm my_tm = *sys_tm; // 时间不是同一天时,先关闭之前的文件,重新打开新的文件 if ( m_today != my_tm.tm_mday ) { m_today = my_tm.tm_mday; // 关闭之前的文件 fflush(m_pFile); fclose(m_pFile); char newFileName[256] = {0}; snprintf(newFileName, 256, "%d_%02d_%02d.log",my_tm.tm_year+1900, my_tm.tm_mon+1, my_tm.tm_mday); m_pFile = fopen(newFileName, "a"); if ( m_pFile == nullptr ){ return ; } } char msg[256] = {0}; va_list valist; va_start(valist, format); vsnprintf(msg, 256, format, valist); // 取可变参数保存到缓冲区 va_end(valist); char content[512] = { 0 }; snprintf(content, 512, "[%04d-%02d-%02d %02d:%02d:%02d.%ld][%s:%d]%s\n", sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, sys_tm->tm_hour, sys_tm->tm_min, sys_tm->tm_sec, now.tv_usec/1000, __FILE__, __LINE__, msg); { // {}作用域结束后释放锁 std::unique_lock<std::mutex> uniqueLock(m_mutex); m_strList.emplace_back(string(content)); // 从末尾添加数据 - emplace_back效率比push_back高 } m_conVar.notify_one(); } void LoggerMng::threadWriteLog() { while( !m_isExit ) { std::unique_lock<std::mutex> uniqueLck(m_mutex); // 避免虚假唤醒 while (m_strList.empty()) { if (m_isExit){ return; } m_conVar.wait(uniqueLck); } string str = m_strList.front(); std::cout << "write str: " << str << endl; fputs(str.c_str(), m_pFile); fflush(m_pFile); m_strList.pop_front(); } } LoggerMng::LoggerMng() { } string LoggerMng::level2Str(const LoggerMng::LOG_LEVEL &level) { if ( level == LEVEL_DEBUG ) return string("Debug"); if ( level == LEVEL_INFO ) return string("Info"); if ( level == LEVEL_WARN ) return string("Warn"); if ( level == LEVEL_ERROR ) return string("Error"); return string("Debug"); } LoggerMng::~LoggerMng() { std::cout << __PRETTY_FUNCTION__ << std::endl; m_isExit = true; m_conVar.notify_one(); m_threadPtr->join(); if ( m_pFile ) { fclose(m_pFile); m_pFile = nullptr; } }
#include "LoggerMng.h" int main() {
// LoggerMng::Instance().init(LOG_FILE_PATH);
// LoggerMng::getInstance()->init(LOG_FILE_PATH);
// LoggerMng::getInstance()->init(LOG_FILE_PATH); //堆指针方式实现单例
// LoggerMng::Instance().init(LOG_FILE_PATH); //静态局部变量方式实现单例
LoggerMng::ptrInstance().get()->init(LOG_FILE_PATH); // 指针指针方式实现单例 for ( int i=0; i< 10; i++ ) { string str = string("AD") + std::to_string(i) ; _DEBUG("你好, 教练%s, 我是成员: %d", str.c_str(), i); } string str = "AD"; _ERROR("ERROR"); _INFO("str: %s", str.c_str()); _WARN("num: %d - str: %s", 8, str.c_str()); }
运行结果:
越努力,越幸运
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端