本篇将使用 C/C++ 代码实现一个日志程序,便于我们在调试程序的时候,打印出对应的日志信息,同时也可以重定向然后记录我们的日志信息。
日志:软件运行的记录信息,可以向显示器打印,也可以向文件中打印。
本篇中设置出的日志特定格式:
[日志等级][pid][filename][filenumber][time] 日志内容(支持可变参数)
其中日志等级分为:DEBUG(调试信息)、INFO(正常信息)、WARNING(存在问题,但不影响正常运行)、ERROR(存在错误,但还是希望尽量不要退出程序)、FATAL(致命错误,直接退出)。
实现的日志程序如下:
Log.hpp:
#pragma once #include <iostream> #include <string> #include <cstdarg> #include <cstring> #include <fstream> #include <sys/types.h> #include <pthread.h> #include <unistd.h> namespace log_ns { enum { DEBUG = 1, INFO, WARNING, ERROR, FATAL }; // 定义日子真正需要记录的信息 struct LogMessage { std::string _level; int _id; std::string _filename; int _filenumber; std::string _curtime; std::string _log_message; }; #define SCREEN_TYPE 1 #define FILE_TYPE 2 const std::string defaultlogfile = "./log.txt"; pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER; class Log { private: std::string LevelToString(int level) { switch(level) { case DEBUG: return "DEBUG"; case INFO: return "INFO"; case WARNING: return "WARNING"; case ERROR: return "ERROR"; case FATAL: return "FATAL"; default: return "UNKNOWN"; } } std::string CurTime() { // 获取当前的时间戳 time_t curtime = time(nullptr); // 将当前时间戳转换成结构体 struct tm* now = localtime(&curtime); char buff[128]; snprintf(buff, sizeof(buff), "%d-%02d-%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec ); return buff; } void Flush(const LogMessage& lg) { // 打印日志的时候可能存在线程安全,使用锁lock住 pthread_mutex_lock(&log_lock); switch(_type) { case SCREEN_TYPE: FlushToScreen(lg); break; case FILE_TYPE: FlushToFile(lg); break; } pthread_mutex_unlock(&log_lock); } void FlushToFile(const LogMessage& lg) { std::ofstream out; out.open(_logfile, std::ios::app); // 文件的操作使用追加 if (!out.is_open()) return; char buff[2024]; snprintf(buff ,sizeof(buff), "[%s][%d][%s][%d][%s] %s", lg._level.c_str(), lg._id, lg._filename.c_str(), lg._filenumber, lg._curtime.c_str(), lg._log_message.c_str() ); out.write(buff, strlen(buff)); out.close(); } void FlushToScreen(const LogMessage& lg) { printf("[%s][%d][%s][%d][%s] %s", lg._level.c_str(), lg._id, lg._filename.c_str(), lg._filenumber, lg._curtime.c_str(), lg._log_message.c_str() ); } public: Log(std::string logfile = defaultlogfile) : _type(SCREEN_TYPE), _logfile(logfile) {} void Enable(int type) { _type = type; } void LoadMessage(std::string filename, int filenumber, int level, const char* format, ...) { LogMessage lg; lg._level = LevelToString(level); lg._filename = filename; lg._filenumber = filenumber; // 获取当前时间 lg._curtime = CurTime(); // std::cout << lg._curtime << std::endl; lg._id = getpid(); // 获取可变参数 va_list ap; va_start(ap, format); char buff[2048]; vsnprintf(buff, sizeof(buff), format, ap); va_end(ap); lg._log_message = buff; // std::cout << lg._log_message; Flush(lg); } void ClearOurFile() { std::ofstream out; out.open(_logfile); out.close(); } ~Log() {} private: int _type; std::string _logfile; }; Log lg; // LOG 宏 #define LOG(level, format, ...) \ do \ { \ lg.LoadMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \ } while (0) #define EnableToScreen() \ do \ { \ lg.Enable(SCREEN_TYPE); \ } while (0) #define EnableToFile() \ do \ { \ lg.Enable(FILE_TYPE); \ } while (0) // 清理文件 #define ClearFile() \ do \ { \ lg.ClearOurFile(); \ } while (0) }
实现如上代码之后,我们只需要将在需要进行日志打印出直接使用宏 LOG 就可以进行日志打印了(同时该 LOG 支持类似与 printf 函数的可变参数打印),同时宏 EnableToFile 和 EnableToScreen 可以选择是在屏幕上打印还是在文件中打印(文件打印可以指定文件位置,不指定则是默认的当前路径下的文件)。
以后想要打印 debug 程序完全就可以使用 LOG 宏代替我们的打印语句,测试如下:
可以自动的记录当前打印的 LOG 宏在哪个文件,哪一行,以及当前进程的 pid。
将其放入到我们的线程池程序中进行测试,如下:
08-07
492
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)