日志记录是每个C++项目都绕不开的基础设施。在Linux环境下,选择哪种方式往往取决于项目规模、性能要求和维护成本。下面整理几种主流方案,从标准库的轻量级实现到工业级第三方库,最后聊聊业界公认的最佳实践。

1. 使用标准库 和文件操作
最直接的方式是裸写——直接用和拼凑一个简易日志函数。代码虽短,五脏俱全:获取时间戳、打开文件、追加写入。比如这样:
#include #include #include #include void logMessage(const std::string& message) {
std::time_t now = std::time(nullptr);
char timestamp[20];
std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
std::ofstream logFile("app.log", std::ios::app);
if (logFile.is_open()) {
logFile << "[" << timestamp << "] " << message << std::endl;
logFile.close();
} else {
std::cerr << "无法打开日志文件。" << std::endl;
}
}
int main() {
logMessage("程序启动");
// ... 程序逻辑 ...
logMessage("程序结束");
return 0;
}
优点是很纯粹,零依赖;缺点是功能简陋,没有日志级别、异步写入这些高级特性,频繁IO下性能也堪忧。
2. 使用第三方日志库
为了获得更强大的功能和更好的性能,推荐使用成熟的第三方日志库。以下是几个流行的C++日志库:
a. spdlog
要说C++社区里最火的日志库,spdlog当仁不让。它号称极速,支持多线程和异步日志,配置灵活。安装也简单——Ubuntu上一条命令搞定。
安装:
sudo apt-get install libspdlog-dev
示例代码:
#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"
int main() {
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
logger->set_level(spdlog::level::info);
logger->info("欢迎使用spdlog!");
logger->warn("这是一个警告信息");
logger->error("这是一个错误信息");
return 0;
}
编译命令:
g++ -std=c++11 -o myapp myapp.cpp -lspdlog
b. log4cpp
受Ja va Log4j启发的log4cpp,在C++世界里有一席之地。虽然最近热度不如spdlog,但功能依然扎实。安装需要从源码编译,步骤稍多,但一次搞定。
安装:
wget https://apache.mirrors.lucidnetworks.net/log4cpp/log4cpp-0.9.9.tar.gz
tar xzf log4cpp-0.9.9.tar.gz
cd log4cpp-0.9.9
mkdir build && cd build
cmake ..
make
sudo make install
示例代码:
#include #include #include #include
int main() {
log4cpp::BasicLayout* layout = new log4cpp::BasicLayout();
log4cpp::FileAppender* fileAppender = new log4cpp::FileAppender("default", "app.log");
fileAppender->setLayout(layout);
log4cpp::Category& root = log4cpp::Category::getRoot();
root.addAppender(fileAppender);
root.setPriority(log4cpp::Priority::INFO);
root.info("欢迎使用log4cpp!");
root.warn("这是一个警告信息");
root.error("这是一个错误信息");
delete layout;
delete fileAppender;
return 0;
}
编译命令:
g++ -std=c++11 -o myapp myapp.cpp -llog4cpp
c. Boost.Log
如果你已经是Boost用户,Boost.Log是个省心的选择。它深度集成Boost库,功能完备,但编译依赖较重。
安装:
sudo apt-get install libboost-all-dev
示例代码:
#include #include #include #include #include
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
void init_logging() {
logging::add_console_log(std::cout,
logging::keywords::format = "%TimeStamp%: %Message%");
auto file_sink = boost::make_shared(
boost::asio::io_context(), "app.log");
file_sink->set_formatter(
boost::make_shared("%TimeStamp%: %Message%"));
auto file_logger = boost::make_shared>(file_sink);
logging::add_sink(file_logger);
}
int main() {
init_logging();
BOOST_LOG_TRIVIAL(info) << "欢迎使用Boost.Log!";
BOOST_LOG_TRIVIAL(warning) << "这是一个警告信息";
BOOST_LOG_TRIVIAL(error) << "这是一个错误信息";
return 0;
}
编译命令:
g++ -std=c++11 -o myapp myapp.cpp -lboost_log -lboost_system -pthread
3. 日志记录的最佳实践
无论选择哪种日志库,以下是一些最佳实践可以帮助你更好地进行日志记录:
a. 使用日志级别
日志级别是基本功。DEBUG、INFO、WARN、ERROR、FATAL必须区分清楚,否则线上排查问题时日志犹如汪&洋大海。
// 示例使用spdlog的日志级别
logger->debug("调试信息");
logger->info("普通信息");
logger->warn("警告信息");
logger->error("错误信息");
logger->critical("严重信息");
b. 异步日志
高性能应用必须关注异步写入,否则日志IO会拖垮主线程。好在spdlog、Boost.Log都原生支持,开箱即用。
c. 日志轮转
单日志文件无限增长是灾难。设置按大小或时间轮转,保留最近N个文件,是生产环境的标配。
d. 格式化日志
统一格式便于工具解析。常用的标配:时间戳、日志级别、线程ID、位置信息。spdlog的pattern语法非常强大。
e. 配置管理
把日志级别、路径、格式放在配置文件或环境变量中,实现动态调整,减少停机修改代码的尴尬。
f. 资源管理
正确关闭文件、释放内存,避免内存泄漏。RAII是C++的老本行,日志库一般已经处理好,但使用者也要注意生命周期。
4. 示例:使用spdlog进行高级日志记录
来看一个综合示例:异步+日志轮转。spdlog的异步模式需要初始化线程池,然后创建rotating_file_sink,设置pattern和日志级别。最后别忘了shutdown。
#include "spdlog/spdlog.h"#include "spdlog/sinks/rotating_file_sink.h"#include "spdlog/async.h"
int main() {
spdlog::init_thread_pool(8192, 1);
auto rotating_logger = std::make_shared("logs/rotating.log", 1024*1024, 3);
rotating_logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");
rotating_logger->set_level(spdlog::level::debug);
spdlog::register_logger(rotating_logger);
auto logger = spdlog::get("rotating_logger");
logger->info("欢迎使用spdlog的日志轮转功能!");
logger->error("这是一个错误信息,会触发日志轮转。");
spdlog::drop_all();
spdlog::shutdown();
return 0;
}
编译命令:
g++ -std=c++11 -o myapp myapp.cpp -lspdlog -pthread
总结
选择哪种方案取决于实际场景:小工具用标准库凑合,严肃项目推荐spdlog或Boost.Log。无论哪种,最佳实践都要跟上——级别、异步、轮转、格式化、配置化、资源管理,缺一不可。
