一个轻量高效的C++日志库需支持多级别、线程安全及多输出目标。1. 定义DEBUG/INFO/WARN/ERROR/FATAL五种级别;2. 设计单例Logger类,封装格式化输出与文件/控制台双写入;3. 使用__VA_ARGS__宏自动传入文件名行号,简化调用;4. log函数中通过mutex加锁,按级别过滤并格式化消息写入多目标,确保线程安全。

在C++项目中,一个轻量且高效的日志库能极大提升开发效率和调试能力。实现一个简单的日志系统并不复杂,关键在于设计清晰的接口、支持多级别输出、灵活的输出目标(控制台、文件等),以及线程安全的基础保障。
定义日志级别
日志级别用于区分消息的重要程度,常见的有:
DEBUG:调试信息,开发阶段使用INFO:普通运行信息WARN:警告,可能存在问题ERROR:错误,程序无法正常执行某功能FATAL:严重错误,可能导致程序终止
可以用枚举来表示:
enum class LogLevel { DEBUG, INFO, WARN, ERROR, FATAL};
设计日志记录器类
核心是封装一个单例的Logger类,提供简洁的调用接口。它应支持格式化输出,并允许同时输出到控制台和文件。
立即学习“C++免费学习笔记(深入)”;
基本结构如下:
class Logger {public: static Logger& instance() { static Logger logger; return logger; }void set_level(LogLevel level) { log_level_ = level; }void set_file_output(const std::string& filename);void log(LogLevel level, const char* file, int line, const char* format, ...);
private:LogLevel loglevel = LogLevel::DEBUG;std::FILE* filehandle = nullptr;std::mutex mutex_; // 保证线程安全};
通过静态instance方法获取唯一实例,避免全局变量污染。log函数使用可变参数处理格式化字符串,类似printf。
封装宏简化调用
直接调用log函数需要手动传入文件名和行号,使用宏可以自动完成:
#define LOG_DEBUG(fmt, ...) Logger::instance().log(LogLevel::DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__)#define LOG_INFO(fmt, ...) Logger::instance().log(LogLevel::INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__)#define LOG_WARN(fmt, ...) Logger::instance().log(LogLevel::WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__)#define LOG_ERROR(fmt, ...) Logger::instance().log(LogLevel::ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__)#define LOG_FATAL(fmt, ...) Logger::instance().log(LogLevel::FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
这样在代码中就可以像这样使用:
LOG_INFO("User %s logged in.", username);LOG_ERROR("Failed to open file: %s", filename);
实现日志输出逻辑
在log函数中,先判断当前级别是否满足输出条件,再格式化消息并加锁写入:
void Logger::log(LogLevel level, const char* file, int line, const char* format, ...) { if (level < log_level_) return;char time_buf[64];auto now = std::time(nullptr);std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));char msg_buf[1024];va_list args;va_start(args, format);vsnprintf(msg_buf, sizeof(msg_buf), format, args);va_end(args);std::lock_guard lock(mutex_);// 输出到控制台const char* level_str;switch (level) { case LogLevel::DEBUG: level_str = "DEBUG"; break; case LogLevel::INFO: level_str = "INFO"; break; case LogLevel::WARN: level_str = "WARN"; break; case LogLevel::ERROR: level_str = "ERROR"; break; case LogLevel::FATAL: level_str = "FATAL"; break;}printf("[%s] %s:%d %sn", time_buf, file, line, msg_buf);// 同时输出到文件(如果开启)if (file_handle_) { fprintf(file_handle_, "[%s] %s %s:%d %sn", time_buf, level_str, file, line, msg_buf); fflush(file_handle_);}
}
set_file_output函数用于打开日志文件:
void Logger::set_file_output(const std::string& filename) { if (file_handle_) { std::fclose(file_handle_); } file_handle_ = std::fopen(filename.c_str(), "a");}
基本上就这些。这个简易日志库已具备实用功能:分级输出、文件位置追踪、时间戳、线程安全、文件持久化。后续可扩展异步写入、滚动日志、颜色输出等功能,但对大多数小项目来说,这个版本已经够用。
以上就是c++++中如何实现一个简单的日志库_C++日志系统设计与实现的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1476169.html
微信扫一扫
支付宝扫一扫