C++还在用printf/cout进行Debug?学习一下如何自己写日志库吧(上篇) 您所在的位置:网站首页 怎么能写一篇好的日志 C++还在用printf/cout进行Debug?学习一下如何自己写日志库吧(上篇)

C++还在用printf/cout进行Debug?学习一下如何自己写日志库吧(上篇)

2024-06-26 22:28| 来源: 网络整理| 查看: 265

文章目录 一. 前言二. 基本功能三. 代码实现1. fdoglogger.h2. fdoglogger.cpp 四. 测试用例1. fdoglogger_test.cpp

一. 前言

哈喽,自从实习以来很久没有更文了,一是没有时间,二是实习了之后突然发现自己能写的东西也没有多少了。赶上1024有征文活动,就写一篇吧,在实习的这段时间,我更加认识到日志的重要性,客户端值没传过来?看日志,服务崩溃了?看日志,没错,日志是出现异常第一个想到的东西,它记录了程序运行过程中所调用的函数,所接受到的值,所执行的行为等等。大家也都看到这篇的标题了,我这个人有一个缺点,就是不太喜欢用别人的东西,如果有能力,我希望自己造,所以今天我们自己来动手撸一个日志库,文章重点讲实现过程,如果需要源码,可以前往github获取FdogLog,一个轻量级C++日志库,用于日志服务。

跪求三连!

二. 基本功能

我们先来捋一捋这个日志库应该实现那些功能。

日志最最最基本的功能是什么,当然是打印或记录日志。信息应该包括哪些信息,时间?运行用户?所在文件?想要显示的信息?(自定义显示信息下篇实现)信息虽然显示丰富,但是要尽可能让代码自己获取其他信息,调用者只需要设置最主要的信息。信息有重要等级之分,所以我们需要对信息做必要分类,提高效率。如何实现全局尽可能简洁的调用。如果日志库是运行在多线程环境,如何保证线程安全。(下篇实现)

这些就是一个日志库所具备的最基本的功能,接下来继续思考,还需要什么。

怎么控制日志的行为。如果保存在文件,如何定义文件名。随着日志增加,文件会越来越大,如何解决。(下篇实现)

简单规划完一个不那么完美的日志库所具备的能力,现在我们来对这几条做更详细的规划。

日志最最最基本的功能是什么,当然是打印或记录日志。信息应该包括哪些信息,时间?运行用户?所在文件?想要显示的信息?

当我在调用一个名为function的函数时。

function();

你希望它输出怎么样的信息。

我被调用 [2021-10-20 23:27:23] 我被调用 [2021-10-20 23:27:23] INFO 我被调用 [2021-10-20 23:27:23] INFO root 我被调用 [2021-10-20 23:27:23] INFO root 17938 我被调用 [2021-10-20 23:27:23] INFO root 17938 [/media/rcl/FdogIM/service.h function:8] 我被调用

我想大部分人都会选择最后一种输出信息吧(虽然在这之前,我们都大量使用cout输出第一种),所以我们的日志应该包括时间,日志等级,运行用户,进程ID,调用函数所在文件,以及调用时所在行数。当然总会有人不想全部输出,这将在后面给出方案。

信息虽然显示丰富,但是要尽可能让代码自己获取其他信息,调用者只需要设置最主要的信息。

信息有重要等级之分,所以我们需要对信息做必要分类,提高效率。

如何实现全局尽可能简洁的调用.

信息有重要等级之分,要可以对信息做区分,按照常见的等级之分,有:

ERROR: 此信息输出后,主体系统核心模块不能正常工作,需要修复才能正常工作。 WARN:   此信息输出后,系统一般模块存在问题,不影响系统运行。 INFO:     此信息输出后,主要是记录系统运行状态等关联信息。 DEBUG: 最细粒度的输出,除去上面各种情况后,你希望输出的相关信息,都可以在这里输出。 TRACE:  最细粒度的输出,除去上面各种情况后,你希望输出的相关信息,都可以在这里输出。

有了等级之分,如何实现全局尽可能简洁的调用,通俗的说就是去掉一切不必要的调用,只留下最主要的调用。

例如:

#include #include"fdoglogger.h" //添加日志库头文件 using namespace fdog; //日志库的命名空间 int main(){ FdogError("错误"); FdogWarn("警告"); FdogInfo("信息"); FdogDebug("调试"); FdogTrace("追踪"); return 0; }

你不必初始化什么信息,调用什么多余的初始化函数,只需要用这五个类似函数的东西来输出即可,同样,如果是另一个源文件,依旧是这样的调用方式(这里可以使用单一模式来实现,其意图是保证一个类仅有一个实列,并提供一个访问它的全局访问点,该实例被所有程序模块共享。就比如日志的输出。)。

如果日志库是运行在多线程环境,如何保证线程安全。

到目前,一个基本的日志库的调用基本成形,如果在单线程,它可以很好的工作,但是到了多线程环境下,就不能保证了,第一点就是单例模式的创建,当两个线程同时去初始化时,无法保证单一实例被成功创建,第二,日志既然是输出到文件,不同线程写入文件时,如何保证写入数据不会错乱。既然写的是C++的日志输出,必然用到了cout ,cout 不是原子性的操作,所以在多线程下是不安全的,这些都是我们需要考虑到的。

怎么控制日志的行为。

这里使用配置文件进行日志的行为规定,包括打印什么日志,输入到文件,还是终端,输出的等级,以及日志开关,等等,配置文件将在程序启动时被读取。(提醒各位千万不要写死代码,后患无穷!!!)

如果保存在文件,如何定义文件名。

随着日志增加,文件会越来越大,如何解决。

日志的文件名由配置文件指定,但是创建时会在后面加上创建日期后缀,并且可以在配置文件中配置每隔多少天创建一个新的日志文件,如果配置中心有设置日志文件大小,则会优先大小判断,超过便创建一个新文件。

三. 代码实现 1. fdoglogger.h #ifndef FDOGLOGGER_H #define FDOGLOGGER_H #include #include #include #include #ifndef linux #include #include #include #include #include #endif #ifndef WIN32 //TODO #endif using namespace std; namespace fdog { #define RED "\e[1;31m" #define BLUE "\e[1;34m" #define GREEN "\e[1;32m" #define WHITE "\e[1;37m" #define DEFA "\e[0m" enum class coutType: int {Error, Warn, Info, Debug, Trace}; enum class fileType: int {Error, Warn, Info, Debug, Trace}; enum class terminalType: int {Error, Warn, Info, Debug, Trace}; struct Logger { string logSwitch; //日志开关 string logFileSwitch; //是否写入文件 string logTerminalSwitch; //是否打印到终端 string logName; //日志文件名字 string logFilePath; //日志文件保存路径 string logMixSize; //日志文件最大大小 string logBehavior; //日志文件达到最大大小行为 string logOverlay; //日志文件覆盖时间 string logOutputLevelFile; //日志输出等级(file) string logOutputLevelTerminal;//日志输出等级 }; class FdogLogger { public: void initLogConfig(); void releaseConfig(); static FdogLogger* getInstance(); string getCoutType(coutType coutType); bool getFileType(fileType fileCoutBool); bool getTerminalType(terminalType terminalCoutTyle); string getLogCoutTime(); string getLogNameTime(); string getFilePash(); string getLogCoutProcessId(); string getLogCoutThreadId(); string getLogCoutUserName(); bool createFile(string filePash); bool logFileWrite(string messages); bool bindFileCoutMap(string value1, fileType value2); bool bindTerminalCoutMap(string value1, terminalType value2); private: char szbuf[128]; Logger logger; static FdogLogger * singleObject; static mutex * mutex_new; map coutTypeMap; map fileCoutMap; map terminalCoutMap; private: FdogLogger(); ~FdogLogger(); }; #define Error1 __FDOGNAME__(Error) #define Warn1 __FDOGNAME__(Warn) #define Info1 __FDOGNAME__(Info) #define Debug1 __FDOGNAME__(Debug) #define Trace1 __FDOGNAME__(Trace) #define SQUARE_BRACKETS_LEFT " [" #define SQUARE_BRACKETS_RIGHT "] " #define SPACE " " #define LINE_FEED "\n" #define COLON ":" #define SLASH "/" #define __FDOGTIME__ FdogLogger::getInstance()->getLogCoutTime() //时间宏 #define __FDOGPID__ FdogLogger::getInstance()->getLogCoutProcessId() //进程宏 #define __FDOGTID__ FdogLogger::getInstance()->getLogCoutThreadId() //线程宏 #define __FDOGFILE__ __FILE__ //文件名宏 #define __FDOGPASH__ FdogLogger::getInstance()->getFilePash() + __FDOGFILE__ //文件路径 #define __FDOGFUNC__ __func__ //函数名宏 #define __FDOGLINE__ __LINE__ //行数宏 #define __USERNAME__ FdogLogger::getInstance()->getLogCoutUserName() //获取调用用户名字 #define __FDOGNAME__(name) #name //名字宏 #define COMBINATION_INFO_FILE(coutTypeInfo, message) \ do{\ string messagesAll = __FDOGTIME__ + coutTypeInfo + __USERNAME__ + __FDOGTID__ + SQUARE_BRACKETS_LEFT + \ __FDOGPASH__ + SPACE +__FDOGFUNC__ + COLON + to_string(__FDOGLINE__) + SQUARE_BRACKETS_RIGHT + message + LINE_FEED;\ FdogLogger::getInstance()->logFileWrite(messagesAll); \ }while(0); #define COMBINATION_INFO_TERMINAL(coutTypeInfo, message) \ do{\ string messagesAll = __FDOGTIME__ + WHITE + coutTypeInfo + DEFA + __USERNAME__ + __FDOGTID__ + SQUARE_BRACKETS_LEFT + \ __FDOGPASH__ + SPACE +__FDOGFUNC__ + COLON + to_string(__FDOGLINE__) + SQUARE_BRACKETS_RIGHT + message + LINE_FEED;\ cout \ COMBINATION_INFO_FILE(coutTypeInfo, message)\ }\ if (FdogLogger::getInstance()->getTerminalType(terminalCoutBool)) {\ COMBINATION_INFO_TERMINAL(coutTypeInfo, message)\ }\ }while(0); #define FdogError(message) \ do{\ LoggerCout(fdog::coutType::Error, Error1, fdog::fileType::Error, fdog::terminalType::Error, message)\ }while(0); #define FdogWarn(message) \ do{\ LoggerCout(fdog::coutType::Warn, Warn1, fdog::fileType::Warn, fdog::terminalType::Warn, message)\ }while(0); #define FdogInfo(message) \ do{\ LoggerCout(fdog::coutType::Info, Info1, fdog::fileType::Info, fdog::terminalType::Info, message)\ }while(0); #define FdogDebug(message) \ do{\ LoggerCout(fdog::coutType::Debug, Debug1, fdog::fileType::Debug, fdog::terminalType::Debug, message)\ }while(0); #define FdogTrace(message) \ do{\ LoggerCout(fdog::coutType::Trace, Trace1, fdog::fileType::Trace, fdog::terminalType::Trace, message)\ }while(0); } #endif 2. fdoglogger.cpp #include"fdoglogger.h" using namespace fdog; FdogLogger * FdogLogger::singleObject = nullptr; mutex * FdogLogger::mutex_new = new(mutex); FdogLogger::FdogLogger(){ initLogConfig(); } FdogLogger::~FdogLogger(){ } FdogLogger* FdogLogger::getInstance(){ mutex_new->lock(); if (singleObject == nullptr) { singleObject = new FdogLogger(); } mutex_new->unlock(); return singleObject; } void FdogLogger::initLogConfig(){ map flogConfInfo; flogConfInfo["logSwitch"] = &this->logger.logSwitch; flogConfInfo["logFileSwitch"] = &this->logger.logFileSwitch; flogConfInfo["logTerminalSwitch"] = &this->logger.logTerminalSwitch; flogConfInfo["logName"] = &this->logger.logName; flogConfInfo["logFilePath"] = &this->logger.logFilePath; flogConfInfo["logMixSize"] = &this->logger.logMixSize; flogConfInfo["logBehavior"] = &this->logger.logBehavior; flogConfInfo["logOverlay"] = &this->logger.logOverlay; flogConfInfo["logOutputLevelFile"] = &this->logger.logOutputLevelFile; flogConfInfo["logOutputLevelTerminal"] = &this->logger.logOutputLevelTerminal; string str; ifstream file; char str_c[100]={0}; file.open("fdoglogconf.conf"); if(!file.is_open()){ cout continue; } string str_copy = str; //cout sscanf(str_copy.data(),"%[^=]",str_c); auto iter = flogConfInfo.find(str_c); if(iter!=flogConfInfo.end()){ sscanf(str_copy.data(),"%*[^=]=%s",str_c); *iter->second = str_c; } else { } } } logger.logName = logger.logName + getLogNameTime() + ".log"; bindFileCoutMap("5", fileType::Error); bindFileCoutMap("4", fileType::Warn); bindFileCoutMap("3", fileType::Info); bindFileCoutMap("2", fileType::Debug); bindFileCoutMap("1", fileType::Trace); bindTerminalCoutMap("5", terminalType::Error); bindTerminalCoutMap("4", terminalType::Warn); bindTerminalCoutMap("3", terminalType::Info); bindTerminalCoutMap("2", terminalType::Debug); bindTerminalCoutMap("1", terminalType::Trace); if(logger.logFileSwitch == "on"){ if(!createFile(logger.logFilePath)){ std::cout time_t timep; time (&timep); char tmp[64]; strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S",localtime(&timep)); string tmp_str = tmp; return SQUARE_BRACKETS_LEFT + tmp_str + SQUARE_BRACKETS_RIGHT; } string FdogLogger::getLogNameTime(){ time_t timep; time (&timep); char tmp[64]; strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H:%M:%S",localtime(&timep)); return tmp; } string FdogLogger::getFilePash(){ getcwd(szbuf, sizeof(szbuf)-1); string szbuf_str = szbuf; return szbuf_str + SLASH; } string FdogLogger::getLogCoutProcessId(){ #ifndef linux return to_string(getpid()); #endif #ifndef WIN32 // unsigned long GetPid(){ // return GetCurrentProcessId(); // } #endif } string FdogLogger::getLogCoutThreadId(){ #ifndef linux return to_string(syscall(__NR_gettid)); #endif #ifndef WIN32 // unsigned long GetTid(){ // return GetCurrentThreadId(); // } #endif } string FdogLogger::getLogCoutUserName(){ struct passwd *my_info; my_info = getpwuid(getuid()); string name = my_info->pw_name; return SPACE + name + SPACE; } bool FdogLogger::createFile(string filePash){ int len = filePash.length(); if(!len){ filePash = "log"; if (0 != access(filePash.c_str(), 0)){ if(-1 == mkdir(filePash.c_str(),0)){ std::cout if (-1 == access(filePash_cy.c_str(), 0)){ if(0!=mkdir(filePash_cy.c_str(),0)){ std::cout cout fileCoutMap[value2] = true; } else { fileCoutMap[value2] = false; } } bool FdogLogger::bindTerminalCoutMap(string value1, terminalType value2){ if(logger.logOutputLevelTerminal.find(value1)!=std::string::npos) { terminalCoutMap[value2] = true; } else { terminalCoutMap[value2] = false; } } 四. 测试用例

在这里插入图片描述

1. fdoglogger_test.cpp #include #include"fdoglogger.h" //添加日志库头文件 using namespace fdog; //日志库的命名空间 int main(){ FdogError("错误"); FdogWarn("警告"); FdogInfo("信息"); FdogDebug("调试"); FdogTrace("追踪"); return 0; }

在这里插入图片描述

在这里插入图片描述

暂时考虑到的就是这些,如有缺陷,欢迎评论区补充。(比如文件写入打开就关闭,很浪费资源,如何优化,下篇见)。

源码已上传github,还原star! FdogLog,一个轻量级C++日志库,用于日志服务。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有