在C++开发中,日志系统是软件开发中不可或缺的组成部分,良好的日志系统可以帮助开发者快速定位问题、监控程序运行状态。选择合适的日志库可以帮助你更高效地记录信息、警告、错误等日志信息。
常用的日志库
- spdlog:一个非常快速的C++日志库,支持多线程,具有良好的性能,并且易于使用。
- glog:Google提供的开源日志库,功能强大,适合大型项目。
- log4cpp:灵感来源于Java的Log4j,为C++应用程序提供灵活的日志记录框架。
- Boost.Log:Boost库的一部分,提供了丰富的功能来处理日志记录的需求。
1. 常用 C++ 日志库
1.1 spdlog (推荐)
特点:
-
高性能
-
支持多线程
-
丰富的格式化选项
-
支持多种输出目标(文件、控制台、系统日志等)
-
支持异步日志
基本用法:
#include "spdlog/spdlog.h"
int main() {
// 控制台日志
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
// 创建文件日志
auto file_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
file_logger->info("File log message");
// 设置日志级别
spdlog::set_level(spdlog::level::debug);
spdlog::debug("This debug message will be displayed");
}
1.2 glog (Google Logging Library)
特点:
-
由 Google 开发
-
支持分级日志
-
支持条件日志
-
支持崩溃堆栈跟踪
基本用法:
#include <glog/logging.h>
int main(int argc, char* argv[]) {
// 初始化
google::InitGoogleLogging(argv[0]);
google::SetLogDestination(google::INFO, "/tmp/log_");
// 日志输出
LOG(INFO) << "Found " << 5 << " cookies";
LOG(WARNING) << "This is a warning";
LOG(ERROR) << "This is an error";
// 条件日志
CHECK(5 == 5) << "The world is ending!";
// 关闭日志
google::ShutdownGoogleLogging();
return 0;
}
1.3 Boost.Log
特点:
-
Boost 生态系统的一部分
-
高度可配置
-
支持多种日志格式和目标
-
支持属性系统
基本用法:
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
namespace logging = boost::log;
void init_logging() {
logging::add_file_log("sample.log");
logging::add_common_attributes();
}
int main() {
init_logging();
BOOST_LOG_TRIVIAL(trace) << "This is a trace severity message";
BOOST_LOG_TRIVIAL(debug) << "This is a debug severity message";
BOOST_LOG_TRIVIAL(info) << "This is an informational severity message";
BOOST_LOG_TRIVIAL(warning) << "This is a warning severity message";
BOOST_LOG_TRIVIAL(error) << "This is an error severity message";
BOOST_LOG_TRIVIAL(fatal) << "This is a fatal severity message";
return 0;
}
1.4 log4cpp 详细介绍
log4cpp 是一个基于 Java 的 log4j 设计的 C++ 日志库,提供了灵活的日志记录功能。下面我将详细介绍 log4cpp,并与 spdlog、glog 和 Boost.Log 进行对比分析。
1. 基本特性
-
源自 log4j 的设计理念
-
高度可配置的日志系统
-
支持多级日志(DEBUG, INFO, WARN, ERROR, FATAL)
-
支持多种输出目标(Appenders)
-
支持日志格式自定义(Layouts)
-
线程安全
2 基本用法示例
#include <log4cpp/Category.hh>
#include <log4cpp/Appender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/Layout.hh>
#include <log4cpp/BasicLayout.hh>
#include <log4cpp/Priority.hh>
int main() {
// 1. 初始化Appender和Layout
log4cpp::Appender *consoleAppender = new log4cpp::OstreamAppender("console", &std::cout);
log4cpp::Appender *fileAppender = new log4cpp::FileAppender("file", "example.log");
// 使用BasicLayout(也可以使用PatternLayout自定义格式)
consoleAppender->setLayout(new log4cpp::BasicLayout());
fileAppender->setLayout(new log4cpp::BasicLayout());
// 2. 获取Category并添加Appender
log4cpp::Category& root = log4cpp::Category::getRoot();
root.addAppender(consoleAppender);
root.addAppender(fileAppender);
// 3. 设置优先级
root.setPriority(log4cpp::Priority::DEBUG);
// 4. 记录日志
root.debug("This is a debug message");
root.info("This is an info message");
root.warn("This is a warning message");
root.error("This is an error message");
root.fatal("This is a fatal message");
// 5. 关闭日志系统
log4cpp::Category::shutdown();
return 0;
}
3 高级配置(使用配置文件)
log4cpp 支持通过配置文件进行配置:
# log4cpp.properties 示例
log4cpp.rootCategory=DEBUG, rootAppender
log4cpp.appender.rootAppender=FileAppender
log4cpp.appender.rootAppender.fileName=application.log
log4cpp.appender.rootAppender.layout=PatternLayout
log4cpp.appender.rootAppender.layout.ConversionPattern=%d [%p] %m%n
然后在代码中加载配置:
#include <log4cpp/PropertyConfigurator.hh>
// 加载配置文件
log4cpp::PropertyConfigurator::configure("log4cpp.properties");
2. 自定义简单日志系统
如果需要轻量级的解决方案,可以自己实现一个简单的日志系统:
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <mutex>
enum LogLevel { DEBUG, INFO, WARNING, ERROR };
class Logger {
private:
std::ofstream log_file;
LogLevel min_level;
std::mutex log_mutex;
const char* levelToString(LogLevel level) {
switch(level) {
case DEBUG: return "DEBUG";
case INFO: return "INFO";
case WARNING: return "WARNING";
case ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
std::string getCurrentTime() {
time_t now = time(0);
char buf[80];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
return buf;
}
public:
Logger(const std::string& filename, LogLevel level = INFO)
: min_level(level) {
log_file.open(filename, std::ios::app);
}
~Logger() {
if (log_file.is_open()) {
log_file.close();
}
}
void log(LogLevel level, const std::string& message) {
if (level < min_level) return;
std::lock_guard<std::mutex> lock(log_mutex);
std::string log_entry = "[" + getCurrentTime() + "] [" +
levelToString(level) + "] " + message + "\n";
// 输出到控制台
std::cout << log_entry;
// 写入文件
if (log_file.is_open()) {
log_file << log_entry;
log_file.flush();
}
}
void debug(const std::string& message) { log(DEBUG, message); }
void info(const std::string& message) { log(INFO, message); }
void warning(const std::string& message) { log(WARNING, message); }
void error(const std::string& message) { log(ERROR, message); }
};
// 使用示例
int main() {
Logger logger("app.log", DEBUG);
logger.debug("This is a debug message");
logger.info("Application started");
logger.warning("Low memory condition detected");
logger.error("Failed to open file");
return 0;
}
3. 日志库的详细对比
1 功能对比表
特性 | log4cpp | spdlog | glog | Boost.Log |
---|---|---|---|---|
开发活跃度 | 维护较少 | 非常活跃 | 活跃 | 活跃 |
性能 | 中等 | 非常高 | 高 | 中等 |
线程安全 | 是 | 是 | 是 | 是 |
异步日志 | 需手动实现 | 内置支持 | 不支持 | 内置支持 |
配置文件支持 | 是 | 否 | 否 | 是 |
日志级别 | 多级 | 多级 | 多级 | 多级 |
输出目标多样性 | 丰富 | 丰富 | 较少 | 丰富 |
格式自定义 | 强大 | 强大 | 有限 | 强大 |
学习曲线 | 中等 | 低 | 低 | 高 |
依赖项 | 无 | 无 | glog | Boost |
崩溃报告 | 无 | 无 | 有 | 无 |
结构化日志 | 有限 | 支持 | 有限 | 支持 |
2 性能对比
-
spdlog:性能最优,特别是在异步模式下
-
glog:性能优秀,专注于日志的核心功能
-
log4cpp:性能中等,配置灵活性带来一定开销
-
Boost.Log:功能最全但性能相对较低
3 使用场景对比
-
log4cpp:
-
需要从 log4j/log4net 迁移的项目
-
需要复杂配置和多种输出目标
-
已有使用 log4cpp 的遗留系统
-
-
spdlog:
-
新项目首选
-
需要高性能日志
-
需要简洁API和现代化特性
-
-
glog:
-
Google技术栈项目
-
需要崩溃报告和检查宏
-
命令行工具开发
-
-
Boost.Log:
-
已使用Boost的项目
-
需要与Boost生态系统集成
-
需要高度可定制的日志解决方案
-
4. 日志系统设计要点
-
日志级别:通常包括 TRACE, DEBUG, INFO, WARNING, ERROR, FATAL
-
输出目标:控制台、文件、网络、系统日志等
-
格式化:时间戳、日志级别、线程ID、文件名和行号等
-
性能考虑:
-
异步日志(避免阻塞主线程)
-
日志文件轮转(按大小或时间分割)
-
缓冲机制
-
-
线程安全:多线程环境下的安全写入
-
易用性:简洁的API,支持流式输出
5. 高级日志技巧
-
结构化日志:
// 使用spdlog的结构化日志
spdlog::info("User {0} logged in from {1}", username, ip_address);
-
条件日志:
// 使用glog的条件日志
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-
性能关键区域的日志控制:
// 使用宏定义在发布版本中移除调试日志
#ifdef NDEBUG
#define LOG_DEBUG(message)
#else
#define LOG_DEBUG(message) logger.debug(message)
#endif
-
日志文件轮转:
// spdlog创建每日日志
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 0, 0);
6. 日志库选择建议
-
需要高性能:选择 spdlog
-
需要崩溃分析:选择 glog
-
已在用 Boost:选择 Boost.Log
-
嵌入式/资源受限环境:考虑轻量级自定义实现
-
需要丰富特性:spdlog 或 Boost.Log
选择适合项目需求的日志库可以大大提高开发效率和系统可维护性。对于新项目,推荐从 spdlog 开始,它提供了良好的平衡点。