1. 项目概述
如果你在C++项目里用过Google Logging(glog),大概率对它又爱又恨。爱的是它那套基于C++流式风格的日志API,用起来确实顺手,LOG(INFO) << “Something happened: “ << value;这种写法,既直观又符合C++程序员的直觉,比printf或者sprintf安全,也比一些复杂的日志库要轻量。恨的是,当你兴致勃勃地把它集成到项目里,编译、链接、运行时,各种稀奇古怪的问题就冒出来了:链接时找不到符号、运行时日志文件不生成、多线程下日志错乱、或者想自定义个输出格式却无从下手。更让人头大的是,这个项目在2025年6月30日已经被官方归档(Archived),不再维护了。这意味着,你遇到的新问题可能再也等不到官方的修复,社区支持的力度也会逐渐减弱。
但这并不意味着glog就完全不能用了。恰恰相反,作为一个曾经被广泛使用、代码质量极高的库,它在很多存量项目和特定场景下依然有很强的生命力。很多问题并非无解,只是需要一些“踩坑”后总结出来的经验。这篇文章,就是基于我过去几年在多个C++服务端项目中深度使用glog的经验,为你梳理出一份“避坑指南”和“解决方案手册”。我会覆盖从编译安装、基础配置、高级用法到疑难杂症排查的全过程,所有方案都是亲测有效,并且完全基于免费、开源的生态。无论你是刚刚接触glog的新手,还是在维护一个使用了glog的老项目,这篇文章都能帮你节省大量折腾的时间。
2. 核心问题全景与解决思路拆解
在深入具体问题之前,我们有必要先对glog可能遇到的问题做一个全景式的扫描,并理解其背后的根源。这能帮助你在遇到问题时快速定位方向,而不是盲目搜索。
2.1 问题分类与根源分析
glog的问题大致可以归结为以下几类,每一类都有其特定的发生场景和解决思路:
构建与集成问题:这是新手遇到最多的第一道坎。问题通常表现为编译错误、链接错误(如
undefined reference togoogle::InitGoogleLogging`)。根源在于:- 依赖缺失:glog本身依赖不多,但在某些系统(如较老的Linux发行版)上,可能需要
libunwind或gflags(Google的命令行标志库)。虽然glog可以不依赖gflags,但很多示例和旧文档默认你装了。 - 构建系统不匹配:你的项目可能用CMake、Bazel、Makefile或Autotools,而glog的源码构建方式需要与之适配。直接
#include源码或使用预编译的不匹配的库文件,很容易出问题。 - C++标准版本冲突:glog要求C++14,如果你的项目用的是C++11或更老的标准,或者用的是C++17/20的某些新特性,可能在编译时遇到语法或标准库兼容性问题。
- 依赖缺失:glog本身依赖不多,但在某些系统(如较老的Linux发行版)上,可能需要
运行时配置与行为问题:库能编译链接,但一运行就发现和预期不符。比如:
- 日志文件不输出:程序运行了,但当前目录下没有生成
<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>这样的文件。 - 日志级别不生效:设置了
FLAGS_minloglevel,但DEBUG或INFO日志依然满天飞。 - 日志格式不符合预期:想添加线程ID、自定义时间格式,却发现无从下手。
- 性能问题:在高并发场景下,日志输出成为性能瓶颈。 根源在于对glog的运行时初始化流程、标志位(Flags)系统以及内部缓冲机制不了解。
- 日志文件不输出:程序运行了,但当前目录下没有生成
高级特性与兼容性问题:当你想用一些更强大的功能时,会遇到障碍。
- 自定义日志接收器(Sink):想把日志同时输出到文件、网络和标准错误,需要深入glog的内部接口。
- 信号处理与崩溃日志:glog内置了信号处理,能在程序崩溃时生成堆栈跟踪,但这需要正确的编译选项和系统支持(如
libunwind)。 - 与其它日志库共存:项目历史遗留,部分模块用了log4cxx、spdlog等,如何让glog与之和平共处?
- 官方归档后的替代方案:既然官方不维护了,是硬着头皮继续用,还是迁移?有哪些平滑迁移的选项?
2.2 总体解决思路
面对这些问题,我的解决思路遵循一个从基础到高级、从通用到特定的路径:
- 确保环境纯净与依赖正确:这是所有工作的基础。使用包管理器(如apt, yum, vcpkg, conan)安装通常是避免编译问题的最快方法。
- 理解初始化与标志位:花半小时读懂
InitGoogleLogging和几个核心标志位(FLAGS_log_dir,FLAGS_minloglebug,FLAGS_stderrthreshold),能解决80%的运行时问题。 - 掌握日志流与宏的语义:
LOG(LEVEL),VLOG(n),DLOG,PLOG这些宏有什么区别?什么时候用CHECK而不是assert?理解这些能写出更健壮、信息量更足的日志。 - 深入内部机制以定制化:当标准功能不满足时,需要研究
LogMessage、LogSink等内部类,这是解决高级需求的钥匙。 - 建立监控与排查习惯:将日志问题本身也纳入监控,比如定期检查日志目录磁盘空间、设置日志文件滚动(Rotate)策略,防患于未然。
接下来,我们就按照这个思路,进入具体的实操环节。
3. 构建、集成与环境配置的坑与解法
让我们从第一步开始,把glog稳稳地“请”进你的项目。
3.1 选择正确的安装方式
首选:系统包管理器对于快速验证和大多数开发环境,这是最推荐的方式。它能自动处理依赖和库路径。
# Ubuntu/Debian sudo apt-get install libgoogle-glog-dev # CentOS/RHEL (需要EPEL仓库) sudo yum install epel-release sudo yum install google-glog-devel # macOS (使用Homebrew) brew install glog注意:包管理器提供的版本可能不是最新的。如果遇到需要特定版本或补丁的情况,才需要考虑从源码编译。
次选:从源码编译当包管理器版本不合适,或者你需要开启某些特定编译选项(如调试符号、定制化的安装路径)时,需要源码编译。
# 1. 克隆代码 (注意:主仓库已归档,可考虑fork或使用社区维护的镜像) git clone https://github.com/google/glog.git cd glog # 2. 创建构建目录并配置 mkdir build && cd build # 关键配置项: # -DWITH_GFLAGS=OFF # 如果不需gflags支持,建议关闭,减少依赖 # -DWITH_UNWIND=ON # 如果需要更好的崩溃堆栈信息,开启(需系统有libunwind) # -DBUILD_SHARED_LIBS=ON # 默认静态库,如需动态库则开启 cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=OFF .. # 3. 编译并安装 make -j$(nproc) sudo make install # 默认安装到 /usr/local避坑点1:安装路径冲突如果你系统里已经通过包管理器安装了glog,再从源码make install到/usr/local,可能会造成头文件和库文件版本混乱。一个干净的做法是使用CMAKE_INSTALL_PREFIX指定一个独立路径,并在你的项目中显式引用这个路径。
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/myglog .. make -j$(nproc) sudo make install之后在你的项目CMakeLists.txt中,可以通过find_package(glog REQUIRED PATHS /opt/myglog)来定位。
避坑点2:Windows下的编译Windows下编译glog相对麻烦,缺少一些Unix特有的头文件和功能。最省事的方法是使用vcpkg。
# 安装vcpkg后 vcpkg install glog:x64-windows然后在你的CMake项目中配置-DCMAKE_TOOLCHAIN_FILE=[path/to/vcpkg]/scripts/buildsystems/vcpkg.cmake即可。
3.2 项目集成:CMake最佳实践
假设你的项目使用CMake,集成glog的最佳实践如下:
cmake_minimum_required(VERSION 3.10) project(MyGlogProject) # 方式1:使用find_package (推荐,清晰) find_package(glog REQUIRED CONFIG) # 如果是从源码安装到标准路径或vcpkg # 或者指定路径 # find_package(glog REQUIRED PATHS /opt/myglog/lib/cmake/glog) add_executable(my_app main.cpp) target_link_libraries(my_app PRIVATE glog::glog) # 方式2:使用pkg-config (如果glog提供了.pc文件) find_package(PkgConfig REQUIRED) pkg_check_modules(GLOG REQUIRED libglog) target_include_directories(my_app PRIVATE ${GLOG_INCLUDE_DIRS}) target_link_libraries(my_app PRIVATE ${GLOG_LIBRARIES})关键点:使用glog::glog这个CMake导入目标(Imported Target),它能自动传递所有必要的编译定义和依赖关系,比手动指定include_directories和link_libraries更可靠。
3.3 一个可运行的“Hello Log”示例
创建一个main.cpp文件,验证集成是否成功:
#include <glog/logging.h> #include <iostream> int main(int argc, char* argv[]) { // 初始化glog。argv[0]用于生成默认日志文件名。 google::InitGoogleLogging(argv[0]); // 设置日志输出到标准错误(除了ERROR和FATAL,它们默认就输出) // 这行很重要,否则你可能在控制台看不到任何输出 google::InstallFailureSignalHandler(); // 可选:安装失败信号处理器,用于捕获崩溃 LOG(INFO) << "This is an informational message."; LOG(WARNING) << "This is a warning message."; LOG(ERROR) << "This is an error message."; // DLOG(INFO) << "This debug log will only be printed in debug builds."; // 使用VLOG进行分级详细日志记录,级别由FLAGS_v控制 VLOG(1) << "I'm printed when you run the program with --v=1 or higher"; VLOG(2) << "I'm printed when you run the program with --v=2 or higher"; // 使用CHECK宏进行断言,比assert更强大,失败会记录FATAL日志 int x = 10; CHECK_GT(x, 0) << "x must be positive"; // 检查 x > 0 std::cout << "Program finished. Check the log files in the current directory." << std::endl; // 关闭日志库(非必须,程序退出时会自动清理) google::ShutdownGoogleLogging(); return 0; }编译并运行:
g++ -std=c++14 -o my_app main.cpp -lglog -lpthread ./my_app运行后,你应该能在当前目录看到生成的日志文件(如my_app.yourhostname.yourusername.log.INFO.20250315-123456.7890),并在控制台看到ERROR和FATAL级别的日志(因为默认FLAGS_stderrthreshold=2,即ERROR及以上级别输出到stderr)。
如果这一步成功了,恭喜你,最基础的集成已经完成。如果失败了,请根据错误信息回溯到前面的安装和集成步骤。
4. 运行时配置:让日志听话的关键
glog的行为几乎完全由一系列gflags管理的命令行标志控制。即使你不链接gflags库,glog内部也有一套兼容的实现来解析这些标志。理解并正确设置它们是驯服glog的关键。
4.1 核心标志位详解与设置方法
你可以在代码中直接设置这些标志,也可以通过命令行参数传递。
在代码中设置(程序启动时):
int main(int argc, char* argv[]) { // 必须在InitGoogleLogging之前设置一些标志! FLAGS_logtostderr = false; // 默认false。为true时,所有日志输出到stderr,不生成日志文件。 FLAGS_alsologtostderr = false; // 默认false。为true时,日志除了写入文件,也同时输出到stderr。 FLAGS_stderrthreshold = google::GLOG_ERROR; // 默认2(ERROR)。设置输出到stderr的最低级别。0(INFO), 1(WARNING), 2(ERROR), 3(FATAL) FLAGS_log_dir = "/var/log/myapp"; // 默认空(当前目录)。日志文件输出目录。务必确保进程有写权限! FLAGS_minloglevel = google::GLOG_INFO; // 默认0(INFO)。全局最低日志级别,低于此级别的日志将被直接丢弃。 FLAGS_v = 0; // 默认0。VLOG的全局级别,只有VLOG(n)中 n < FLAGS_v 的日志才会被记录。 google::InitGoogleLogging(argv[0]); // ... 其余代码 }通过命令行参数设置(更灵活):
# 将日志同时输出到文件和stderr ./my_app --alsologtostderr # 只输出到stderr,不生成文件 (常用于调试) ./my_app --logtostderr # 设置stderr只输出ERROR及以上 ./my_app --stderrthreshold=2 # 设置日志目录 ./my_app --log_dir=/tmp/my_logs # 只记录WARNING及以上级别的日志 ./my_app --minloglevel=1 # 启用VLOG级别为1的详细日志 ./my_app --v=1实操心得:初始化顺序的坑FLAGS_log_dir、FLAGS_logtostderr等影响输出目标的标志,必须在google::InitGoogleLogging之前设置,否则初始化可能不会采用你的设置。而像FLAGS_v这种控制日志内容的标志,在初始化前后设置都可以。一个好习惯是,在main函数开头,紧接在gflags解析(如果有的话)之后,InitGoogleLogging之前,集中设置所有输出相关的标志。
4.2 日志文件命名与滚动策略
默认的日志文件名很长,包含了程序名、主机名、用户名、级别、时间戳和PID,这保证了唯一性,但也可能让人眼花缭乱。你可以通过设置FLAGS_log_file_name来影响基础命名(但不建议完全自定义,因为唯一性可能被破坏)。
更重要的管理在于日志文件滚动(Rotate),防止单个文件过大。
// 在代码中设置滚动策略 FLAGS_max_log_size = 100; // 单位MB,默认1800MB。单个日志文件达到此大小后,会滚动到下一个文件。 FLAGS_stop_logging_if_full_disk = true; // 默认false。当磁盘空间不足时,停止日志记录,避免写满磁盘。glog的滚动是自动的。当myapp.xxx.log.INFO.20250315-120000.12345达到max_log_size后,会被重命名为myapp.xxx.log.INFO.20250315-120000.12345.1,然后创建一个新的myapp.xxx.log.INFO.20250315-120001.12345继续写入。旧文件会以.1,.2后缀依次递增。
避坑点:日志文件清理glog不会自动删除旧的日志文件!这是一个潜在的磁盘空间杀手。你必须自己实现清理逻辑,例如通过crontab定时任务:
# 每天凌晨2点,删除超过7天的glog日志文件 0 2 * * * find /path/to/log/dir -name \"*.log.*\" -mtime +7 -delete或者在你的应用程序启动或关闭时,调用一个清理函数。千万不要指望glog帮你做这件事。
4.3 多线程与异步日志(性能考量)
glog的日志输出默认是线程安全的,但它是同步的。这意味着每次调用LOG(INFO) << ...,当前线程都会等待I/O操作(写文件或写stderr)完成。在高并发、高频率打日志的场景下,这可能会成为性能瓶颈。
glog本身不提供内置的异步日志机制。社区有一些方案,比如将日志先写入一个内存队列,再由后台线程批量写出,但这需要你自行实现或集成第三方库(如spdlog的异步模式)。
一个简单的性能优化技巧:对于极度频繁的调试日志(例如在紧凑循环中),使用VLOG并默认将FLAGS_v设为0来禁用。在需要排查问题时,再通过命令行动态开启--v=2。这比用LOG(DEBUG)配合FLAGS_minloglevel更灵活,因为VLOG的调用开销在禁用时极低。
5. 高级用法与定制化技巧
当基础功能满足不了你时,就需要深入了解glog的内部接口了。
5.1 自定义日志格式
glog默认的日志格式是固定的:[级别][月/日 时:分:秒.微秒][线程ID?] 文件名:行号] 消息体。有时你可能想添加更多上下文,比如协程ID、请求ID,或者改变时间格式。
这需要通过自定义LogMessage的生成逻辑来实现,比较底层。一个更实用的方法是继承google::LogSink。LogSink允许你拦截每一条日志消息,然后以任意格式输出到任意地方。
#include <glog/logging.h> #include <iomanip> #include <sstream> class MyCustomSink : public google::LogSink { public: void send(google::LogSeverity severity, const char* full_filename, const char* base_filename, int line, const google::LogMessageTime& logmsgtime, const char* message, size_t message_len) override { // 忽略某些级别的日志 if (severity < google::GLOG_WARNING) { return; } std::ostringstream oss; // 自定义格式: [时间][级别][文件:行] 消息 auto t = logmsgtime.time(); std::time_t tt = std::chrono::system_clock::to_time_t(t); oss << "[" << std::put_time(std::localtime(&tt), "%Y-%m-%d %H:%M:%S") << "][" << google::GetLogSeverityName(severity) << "]" << "[" << base_filename << ":" << line << "] " << std::string(message, message_len) << std::endl; // 输出到标准错误(也可以写到网络、数据库等) std::cerr << oss.str(); // 同时,你也可以继续让默认的文件日志生效(如果你还添加了默认的file sink) } }; int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); MyCustomSink sink; google::AddLogSink(&sink); // 注册自定义Sink LOG(INFO) << "This info will be filtered out by our sink."; LOG(WARNING) << "This warning will be formatted by our sink."; LOG(ERROR) << "This error will also be formatted by our sink."; // 注意:sink对象必须在程序生命周期内有效,所以通常定义为全局或静态变量。 // 程序结束前,可以调用 google::RemoveLogSink(&sink); 但非必须。 google::ShutdownGoogleLogging(); return 0; }5.2 条件日志与调试宏
glog提供了一系列强大的宏,用于有条件的日志记录和检查,这比简单的if语句加LOG更高效、更安全。
LOG_IF(LEVEL, CONDITION): 当条件满足时才记录日志。int error_count = 5; LOG_IF(ERROR, error_count > 0) << "Encountered " << error_count << " errors.";LOG_EVERY_N(LEVEL, N): 每执行到第N次时记录一次日志。非常适合记录周期性事件,避免日志刷屏。for (int i = 0; i < 10000; ++i) { LOG_EVERY_N(INFO, 1000) << "Processed " << google::COUNTER << " items."; // COUNTER是内部计数器 // ... 处理逻辑 }LOG_FIRST_N(LEVEL, N): 前N次执行时记录日志。DLOG(LEVEL),DLOG_IF,DVLOG: 这些是调试版本专用的宏,在非调试(NDEBUG定义)构建中会被完全编译掉,不产生任何运行时开销。强烈推荐在调试日志中使用。CHECK系列宏:这是glog的王牌功能之一。它不仅是断言,失败时会生成FATAL级别日志,打印堆栈跟踪(如果编译时支持),并终止程序。比标准assert更强大。CHECK_NOTNULL(pointer); // 检查指针非空 CHECK_EQ(a, b); // 检查相等 CHECK_GE(a, b); // 检查 a >= b CHECK_STREQ(str1, str2); // 检查C风格字符串相等 // 所有CHECK宏都支持 << 流操作符附加额外信息 CHECK_LT(index, vector.size()) << "Index out of bounds. index=" << index << ", size=" << vector.size();
实操心得:善用DLOG和VLOG将开发调试用的日志全部用DLOG和VLOG来写。DLOG在发布版本中自动消失,零开销。VLOG可以通过命令行动态调整级别,在线上排查问题时非常有用,无需重新编译部署。而LOG(INFO)/LOG(WARNING)等则用于记录程序正常运行时的关键状态和事件。
5.3 信号处理与崩溃堆栈
google::InstallFailureSignalHandler();这行代码非常有用。它会让glog捕获SIGSEGV(段错误)、SIGILL(非法指令)等致命信号,并在程序崩溃前,将堆栈跟踪信息输出到stderr和日志文件。这对于定位线上崩溃原因至关重要。
要使堆栈跟踪信息更可读(包含函数名和行号,而不是一堆地址),你需要:
- 编译你的程序时加上
-g或-rdynamic选项(GCC/Clang),以包含调试符号或动态符号表。 - 确保系统安装了
libunwind或glibc的backtrace相关功能,并且glog在编译时开启了WITH_UNWIND或WITH_STACKTRACE支持。
6. 疑难杂症排查实录
即使按照最佳实践操作,一些奇怪的问题还是会出现。下面是我遇到过的典型问题及其解决方法。
6.1 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
编译错误:undefined reference to ‘google::InitGoogleLogging’ | 1. 没有链接glog库 (-lglog)。2. 链接顺序不对, -lglog需要放在源文件/目标文件之后。3. 使用了C++编译器链接C库(罕见)。 | 1. 确保编译命令包含-lglog,CMake中使用target_link_libraries。2. 调整链接顺序: g++ main.cpp -o app -lglog -lpthread。3. 如果是C项目,使用 extern “C”包含头文件,并链接glog。 |
| 运行时无日志文件生成 | 1.FLAGS_log_dir路径不存在或进程无写权限。2. FLAGS_logtostderr或FLAGS_alsologtostderr被设为true。3. 当前目录不可写。 | 1. 检查目录权限,或在代码中创建目录mkdir(FLAGS_log_dir.c_str(), 0755)。2. 确认这些标志在 InitGoogleLogging前被正确设置为false。3. 使用 --log_dir指定一个绝对路径。 |
| 日志同时输出到屏幕和文件,但只想保留一种 | FLAGS_alsologtostderr被设置为true。 | 如果想只输出到文件,确保FLAGS_logtostderr=false且FLAGS_alsologtostderr=false。如果想只输出到屏幕,设置FLAGS_logtostderr=true。 |
CHECK宏失败时没有堆栈信息 | 1. 程序编译时未包含调试符号 (-g)。2. 编译时被优化(如 -O2)可能导致行号信息不准确。3. glog编译时未启用栈回溯支持。 | 1. 开发/调试版本编译时加上-g选项。2. 对于发布版本,考虑使用 -rdynamic并保留一份带调试符号的二进制用于调试。3. 从源码编译glog时,确保 -DWITH_UNWIND=ON。 |
| 多线程程序日志内容错乱或丢失 | 虽然glog内部是线程安全的,但极端高并发下,如果多个线程同时写一条很长的日志(多次<<操作),可能会交叉。 | 对于单条日志,确保在一条LOG语句内完成构建。避免先构建字符串再输出,因为中间状态可能被其他线程打断。LOG(INFO) << “Key: “ << key << “, Value: “ << value;是原子的。 |
程序退出时报告内存泄漏(google::LogMessage::~LogMessage) | 通常是因为没有调用google::ShutdownGoogleLogging(),或者日志线程还未完全结束程序就退出了。 | 1. 在main函数返回前调用google::ShutdownGoogleLogging()。2. 确保所有全局/静态对象的析构函数中不再打日志。 3. 对于某些检测工具(如Valgrind),这可能是误报,可以忽略glog内部的内存池。 |
| 日志文件体积增长过快 | 1.FLAGS_max_log_size设置过大。2. 日志级别太低(如 INFO),打印了过多无关紧要的日志。3. 没有清理旧日志文件的策略。 | 1. 根据需求调整max_log_size(如100MB)。2. 提高 FLAGS_minloglevel到WARNING或ERROR,减少日志量。3.必须实现外部日志清理(如crontab脚本)。 |
| 在动态库中使用glog,主程序也使用,冲突 | 符号冲突或重复初始化。 | 理想情况下,整个进程应该只使用一份glog。如果动态库是第三方库,你无法控制,可以考虑:1. 让动态库静态链接glog并隐藏符号。2. 与库提供者协商使用统一的glog版本。这是一个复杂问题,可能需具体分析。 |
6.2 深入排查:使用GDB调试glog问题
当遇到一些玄学问题,比如日志偶尔丢失、程序在打日志时卡住,可能需要更深入的调试。
- 查看glog内部状态:glog提供了一些未公开但有用的函数。例如,你可以通过
google::GetLoggingDirectories()来检查它当前认为的日志目录是什么。 - GDB断点:如果你怀疑问题在glog内部,可以编译一份带调试符号的glog,然后在关键函数上设断点。
这可以帮助你观察每条日志是如何被处理和写入的。gdb your_program (gdb) break google::LogMessage::SendToLog (gdb) break google::LogFileObject::Write - 跟踪系统调用:使用
strace查看写文件是否失败。
然后查看strace -e trace=file -o strace.out ./your_programstrace.out文件中对日志文件的open、write、close等调用是否返回错误(如-1 EACCES权限错误)。
6.3 官方归档后的出路与迁移考量
最后,我们无法回避glog已被归档的事实。对于新项目,我建议慎重考虑。对于老项目,你有几个选择:
- 继续使用,自行维护:glog代码很稳定,如果你的使用场景简单,且当前版本工作良好,可以继续使用。将源码作为第三方库引入你的项目,并做好不再有更新的心理准备。关注社区是否有重要的安全补丁分支。
- 迁移到
absl::Log(Abseil Logging):这是Google官方推荐的继任者,属于Abseil库的一部分。它设计更现代,性能可能更好,并且与Google内部的C++生态集成更紧密。但API与glog不完全相同,迁移需要一定工作量。 - 迁移到
ng-log:这是一个社区维护的、旨在API兼容glog的项目。目标是让现有使用glog的代码只需极少的修改就能切换过去。如果你的项目严重依赖glog的特定API,这是一个平滑迁移的选项。 - 评估其他日志库:如
spdlog(高性能,特性丰富,头文件库)、fmtlog(基于fmt库,高性能)、Boost.Log(功能强大但较重)。这需要重新评估需求和进行大量代码修改。
迁移建议:如果项目处于活跃开发期,且对日志功能有更高要求(如异步、更灵活的格式、sink),趁早规划迁移到spdlog或absl::Log。如果项目稳定,且glog完全满足需求,可以暂时不动,但要在构建系统中固定glog的版本和源码,避免未来因系统环境变化导致的不兼容。
说到底,工具是为人服务的。glog虽然“年事已高”,但其简洁的设计和稳定的表现,在无数C++项目中证明了它的价值。希望这份汇集了多年踩坑经验的指南,能让你在使用glog的路上走得更顺畅一些。