简单的日志系统,包含了日志级别定义、日志宏,以及一个单例的 Logger 类,方便程序中统一、灵活地打印日志
1日志级别(LogLevel 枚举)
enum LogLevel { INFO, // 普通重要流程信息 ERROR, // 错误信息,不影响程序运行 FATAL, // 毁灭性错误,程序一般需要退出 DEBUG, // 调试信息,便于开发时排查问题 };
2. 日志打印宏(LOG_INFO、LOG_ERROR、LOG_FATAL、LOG_DEBUG)
//宏参数是一个格式字符串 LogmsgFormat 和可变参数 ... //这是一个宏定义,名字是 LOG_INFO //使用 do { ... } while(0) 包裹宏体,是为了让宏在任何位置(如 if 语句中)都能安全使用,避免意外逻辑错误。 {\ Logger &logger=Logger::instance();\ //获取日志系统的单例对象 Logger。用引用 logger 引用这个对象,避免复制 logger.setLogLevel(INFO);\ //设置当前这条日志的级别为 INFO(普通流程日志)。 char buf[1024]={0};\ //定义一个字符串缓冲区,用于保存格式化后的日志内容。 snprintf(buf,1024,LogmsgFormat,##__VA_ARGS__);\ //使用 snprintf 把用户传入的格式字符串和参数格式化为日志文本,保存在 buf 中。 logger.log(buf);\ //将格式化后的日志字符串传入 Logger::log() 函数打印输出 } while (0) //代表出现致命错误,记录完日志后直接退出程序。 } while (0) //如果没有定义 MUDEBUG,LOG_DEBUG 会被编译为空,避免发布版本输出调试信息。
3. Logger 类设计 //输出一个日志类 class Logger:noncopyable { public: //获取日志唯一的实例对象 static Logger& instance(); //获取 唯一实例,这是 Singleton 设计模式的核心接口。返回一个引用,保证全局使用的是同一个 Logger。 //设置日志级别 void setLogLevel(int Level); //设置当前日志输出的级别: //写日志 void log(std::string msg); //打印日志消息(写入日志系统、终端、文件等)。 private: int logLevel_; //当前 Logger 实例保存的日志级别,在调用 log() 时使用 Logger() //私有构造函数,防止外部直接创建对象,保证 只能通过 instance() 创建一次 { } };
函数方法实现
//获取日志唯一的实例对象 Logger& Logger::instance()//第一次调用 instance() 时,创建唯一一个 Logger 实例,线程安全 { static Logger logger; return logger; } //设置日志级别 void Logger::setLogLevel(int Level)// 设置日志级别 { logLevel_=Level; } //写日志[级别信息] time msg void Logger::log(std::string msg) { switch (logLevel_)//logLevel_ 是当前日志的级别,它决定输出的前缀 { case INFO: std::cout<<"[INFO]"; break; case ERROR: std::cout<<"[ERROR]"; break; case FATAL: std::cout<<"[FATAL]"; break; case DEBUG: std::cout<<"[DEBUG]"; break; default: break; } //打印时间和msg,Timestamp::now().toString() 获取当前时间字符串; std::cout<< Timestamp::now().toString()<<" :"<<msg<<std::endl; } //体输出格式类似: [INFO]2025-07-11 20:11:35.123456 : 服务启动成功