ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

glog for Windows:C++日志库编译配置与避坑指南

glog for Windows:C++日志库编译配置与避坑指南 简介这份资源是面向 Windows 平台 C 开发者的 glog 日志库集成包适合需要在 VS2017 项目中引入高效日志系统的中高级开发者。glog 是 Google 开源的跨平台日志工具支持 INFO、WARNING、ERROR、FATAL 多级别输出、堆栈回溯、VLOG 调试日志以及日志速率限制与异步处理等特性能帮助排查运行时错误并做性能分析。压缩包共 13 个文件约 80KB包含 5 个 cmake 构建脚本、5 个头文件、1 个 lib 静态库、1 个 dll 动态库和 1 个 pc 配置文件覆盖编译、链接与 pkgconfig 集成所需的核心内容。已有 298 人学习下载。拿到后可直接在 Windows 工程中完成头文件包含、库链接与初始化调用省去自行编译依赖的繁琐过程快速搭建可用的日志模块。1. glog for windows把 C 日志从 printf 泥潭里拽出来如果你在 Windows 上写 C大概率经历过这种场面服务跑了一晚上早上回来发现崩了翻遍代码只有一堆printf和std::cout日志文件里连个时间戳都没有更别提线程 ID 和堆栈。glog for windows 要解决的就是这件事——把 Google 那套经过大规模线上验证的日志库在 Windows 上跑通、跑稳、跑出可检索的日志。glog 本身是 Google 开源的 C 日志库提供分级日志、条件日志、崩溃时自动输出堆栈、日志文件按大小和天数滚动、CHECK宏失败即终止等能力。它在 Linux 上是很多基础库的默认依赖但到了 Windows因为构建系统、字符编码、堆栈符号解析这几道坎不少人第一次编译就翻车。这篇面向的是需要在 Windows 上做 C 服务端、客户端或工具链的工程师尤其是用 Visual Studio 或 MSVC 工具链、又不想自己造日志轮子的那批人。读完你能拿到一套可复现的编译流程、关键参数配置以及几个我踩过的坑。2. 在 Windows 上把 glog 编出来从源码到静态库2.1 为什么 Windows 上编译 glog 容易翻车glog 的构建系统经历过从 autotools 到 CMake 的迁移早期版本在 Windows 上依赖 MinGW 或 Cygwin用 MSVC 直接编会碰到unistd.h找不到、ssize_t未定义这类问题。现在主流做法是用 CMake 配合 MSVC但版本选择很关键太老的 glog比如 0.3.x对 MSVC 支持不完整太新的又可能要求 C17 以上。我一般选 0.6.0 之后的版本CMake 最低 3.16MSVC 2019 或 2022。另一个翻车点是字符编码。glog 内部用char处理文件名和日志内容Windows 默认是 UTF-16 宽字符如果你在代码里传了中文路径日志文件可能创建失败或者文件名乱码。常见做法是统一用 UTF-8 窄字符串或者在调用google::InitGoogleLogging之前把路径转成 ANSI。还有一个隐藏坑glog 的崩溃堆栈依赖google::InstallFailureSignalHandler这个在 Windows 上需要额外链接dbghelp.lib否则信号处理注册不上崩溃时只能看到一句干巴巴的退出码。2.2 用 CMake 生成 VS 工程并编译先准备环境Visual Studio 2022 社区版安装时勾选「使用 C 的桌面开发」CMake 3.20 以上加到 PATHgit 用于拉源码。然后按下面步骤走。# 拉取 glog 源码选一个稳定 tag git clone https://github.com/google/glog.git cd glog git checkout v0.6.0 # 创建构建目录用 CMake 生成 VS 2022 工程 mkdir build cd build cmake .. -G Visual Studio 17 2022 -A x64 ^ -DCMAKE_INSTALL_PREFIXD:/libs/glog ^ -DBUILD_SHARED_LIBSOFF ^ -DWITH_GFLAGSOFF ^ -DWITH_UNWINDOFF这段命令的逻辑-G指定生成器为 VS 2022-A x64指定 64 位CMAKE_INSTALL_PREFIX是安装路径我习惯放到非系统盘避免权限问题BUILD_SHARED_LIBSOFF生成静态库省得运行时还要拷 DLLWITH_GFLAGSOFF关掉对 gflags 的依赖除非你确实要用命令行参数解析WITH_UNWINDOFF在 Windows 上本来也用不到 libunwind。生成之后编译安装cmake --build . --config Release --target INSTALL这一步会把头文件、glog.lib、glog.dll如果开了动态库放到D:/libs/glog下。检查一下include/glog/logging.h和lib/glog.lib是否都在。2.3 在 VS 工程里链接 glog 并写第一行日志新建一个空 C 控制台项目配置三处C/C → 常规 → 附加包含目录填D:/libs/glog/include链接器 → 常规 → 附加库目录填D:/libs/glog/lib链接器 → 输入 → 附加依赖项填glog.lib和dbghelp.lib。然后写最小可运行代码#include glog/logging.h int main(int argc, char** argv) { // 初始化 glogargv[0] 作为日志文件前缀 google::InitGoogleLogging(argv[0]); // 设置日志目录必须提前创建好 FLAGS_log_dir D:/logs; // 设置最小日志级别INFO 及以上都写文件 FLAGS_minloglevel google::GLOG_INFO; // 同时输出到 stderr方便调试 FLAGS_alsologtostderr true; LOG(INFO) glog for windows 启动成功; LOG(WARNING) 这是一条警告线程 ID 会自动带上; LOG(ERROR) 这是一条错误; // 条件日志只在表达式为假时输出 int fd -1; CHECK_NE(fd, -1) 文件描述符无效程序终止; google::ShutdownGoogleLogging(); return 0; }逻辑说明InitGoogleLogging必须在任何LOG之前调用否则日志会写到默认位置或者直接丢弃。FLAGS_log_dir指定的目录必须存在glog 不会自动创建。FLAGS_minloglevel控制写入文件的最低级别FLAGS_alsologtostderr让日志同时打到控制台调试时很有用。CHECK_NE在条件不满足时会输出日志并调用abort()适合做断言。参数方面FLAGS_logbufsecs默认 30 秒意思是日志先缓存在内存30 秒才刷盘。如果你在调试崩溃问题建议改成 0实时刷盘代价是性能下降。FLAGS_max_log_size默认 1800MB超过就滚动新文件一般改成 100 或 200 更合理。3. 日志滚动、崩溃堆栈与多线程下的参数调优3.1 日志文件滚动策略怎么配才不丢日志glog 的滚动由两个参数控制FLAGS_max_log_size和FLAGS_logbufsecs。前者按大小切分单位 MB后者控制刷盘间隔。默认 1800MB 一个文件在 Windows 上如果服务跑得久单个文件可能几 GB用记事本打开直接卡死。我一般设成 100MB配合FLAGS_logbufsecs1兼顾性能和可读性。还有一个参数FLAGS_stop_logging_if_full_disk默认 false意思是磁盘满了也不停止写日志。这在生产环境很危险可能把磁盘写爆。建议设成 true磁盘满时 glog 会丢弃后续日志而不是拖垮系统。日志文件命名格式是程序名.主机名.用户名.日志级别.时间戳.进程ID在 Windows 上主机名和用户名可能包含特殊字符如果发现文件名异常检查一下FLAGS_log_link或者直接用FLAGS_log_dir指定纯英文路径。3.2 崩溃时自动输出堆栈InstallFailureSignalHandler 的正确用法Windows 上让 glog 在崩溃时输出堆栈需要调用google::InstallFailureSignalHandler()并且链接dbghelp.lib。但有个细节这个函数默认只处理 SIGSEGV、SIGABRT 等信号Windows 的 SEH 异常比如空指针访问需要额外配置。#include glog/logging.h int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); FLAGS_log_dir D:/logs; FLAGS_minloglevel google::GLOG_INFO; // 安装崩溃信号处理器 google::InstallFailureSignalHandler(); // 设置堆栈深度默认 32 层够用 google::InstallFailureWriter([](const char* data, int size) { LOG(ERROR) std::string(data, size); }); int* p nullptr; *p 42; // 这里会崩溃glog 会输出堆栈 google::ShutdownGoogleLogging(); return 0; }逻辑说明InstallFailureSignalHandler注册信号处理函数崩溃时把堆栈写到 stderr 和日志文件。InstallFailureWriter可以自定义输出方式比如写到自己的日志系统。注意这个功能在 Release 模式下需要保留调试符号PDB 文件否则堆栈只有地址没有函数名。参数上FLAGS_symbolize_stacktrace默认 true但在 Windows 上符号解析依赖dbghelp.dll如果系统缺少或者版本不对堆栈会显示成0x00007FF...这种地址。解决办法是把 VS 安装目录下的dbghelp.dll拷到程序目录或者确保系统 PATH 里有正确的版本。3.3 多线程写日志线程安全与性能取舍glog 本身是线程安全的多个线程同时LOG(INFO)不会交错或崩溃。但线程安全是有代价的每次写日志都会加锁。在高并发场景下如果日志量很大锁竞争会成为瓶颈。我一般做两件事一是把FLAGS_logbufsecs设成 0 或 1减少缓冲区带来的内存占用和丢失风险二是对高频日志用LOG_IF或者VLOG控制输出量。VLOG是 verbose 日志默认不输出需要--v1才打开适合调试信息。// 高频循环里只记录关键信息 for (int i 0; i 1000000; i) { VLOG(1) 处理第 i 条数据; // 默认不输出 if (i % 10000 0) { LOG(INFO) 已处理 i 条; } }如果确实需要极致性能可以考虑异步日志方案比如自己起一个线程消费队列glog 只负责格式化。但大多数 Windows 服务场景glog 默认性能已经够用没必要过度设计。4. glog for windows 避坑记录五个让我加班到凌晨的问题4.1 日志文件创建失败目录明明存在现象FLAGS_log_dir指向D:/logs目录确实存在但日志文件就是不出来程序也不报错。原因glog 在 Windows 上对路径分隔符敏感D:/logs在某些版本里会被解析成D:\logs然后拼接文件名时出错。另外如果路径末尾带了反斜杠也可能导致文件名拼接异常。解决统一用正斜杠并且末尾不加斜杠。如果还不行在InitGoogleLogging之前用_mkdir确保目录存在然后传绝对路径。4.2 崩溃堆栈只有地址没有函数名现象程序崩溃后日志里输出了一堆0x00007FF6...完全看不懂。原因Release 模式下编译器优化掉了符号信息或者 PDB 文件没有和 exe 放在一起。解决在 VS 里把「调试信息格式」设为/Zi「生成调试信息」设为「是」确保 PDB 和 exe 同目录。如果用了InstallFailureSignalHandler还要确保dbghelp.dll版本正确。4.3 CHECK 宏导致程序直接退出没有日志现象CHECK失败时程序直接闪退日志文件里什么都没有。原因CHECK失败会调用abort()如果此时日志缓冲区还没刷盘内容就丢了。解决把FLAGS_logbufsecs设成 0或者在CHECK之前手动google::FlushLogFiles(google::GLOG_INFO)。更稳妥的做法是用LOG(FATAL)替代CHECKLOG(FATAL)会先刷盘再终止。4.4 中文日志乱码现象LOG(INFO) 中文测试日志文件里显示乱码。原因Windows 控制台默认 GBK 编码glog 输出 UTF-8 字节流两者不匹配。解决如果日志文件用 UTF-8 打开正常只是控制台乱码可以忽略。如果文件本身乱码检查源文件编码是否为 UTF-8 with BOMVS 里把「高级保存选项」改成 UTF-8 无 BOM。4.5 多个模块同时初始化 glog 导致崩溃现象主程序和 DLL 都调用了InitGoogleLogging程序启动时崩溃。原因glog 的全局状态只能初始化一次重复调用会触发未定义行为。解决只在主程序入口初始化一次DLL 里直接用LOG宏不要重复初始化。如果 DLL 需要独立日志用google::InitGoogleLogging的另一个重载传不同的程序名但这种情况很少见。5. 用 glog 的 VLOG 和自定义日志级别做精细化排查VLOG 是 glog 里被低估的功能。它允许你按模块和级别输出调试日志默认关闭需要时通过--v和--vmodule打开。比如你怀疑某个模块有性能问题可以这样写VLOG(1) 进入数据处理函数; VLOG(2) 当前队列长度: queue.size();启动时加--v2就能看到所有 VLOG(1) 和 VLOG(2) 的输出。更精细的做法是用--vmoduleprocessor2,network1只打开 processor 模块的 2 级日志和 network 模块的 1 级日志。这个在排查线上问题时特别有用不用重新编译就能调整日志粒度。自定义日志级别方面glog 默认只有 INFO、WARNING、ERROR、FATAL 四级。如果你需要更细的级别比如 TRACE 或 DEBUG可以用VLOG模拟或者用LOG_IF配合条件判断。我一般用 VLOG(3) 当 TRACEVLOG(2) 当 DEBUGVLOG(1) 当 INFO 的补充。验证日志是否按预期输出可以写一个简单的测试#include glog/logging.h int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); FLAGS_log_dir D:/logs; FLAGS_v 2; // 相当于 --v2 VLOG(1) 一级调试日志; VLOG(2) 二级调试日志; VLOG(3) 三级调试日志默认不输出; google::ShutdownGoogleLogging(); return 0; }运行后检查日志文件应该只看到前两行。如果三级也输出了检查FLAGS_v是否被其他代码覆盖。最后说一个我自己的习惯每次新建 Windows C 项目先把 glog 编好、链接好、写一行LOG(INFO)跑通再开始写业务代码。这个前置动作花不了十分钟但能省掉后面无数个「日志去哪了」的深夜排查。希望帮到你。本文还有配套的精品资源点击获取
返回列表