日志Log程序(C++)
本篇将使用 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 2const 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。
将其放入到我们的线程池程序中进行测试,如下: