简介:glog for Windows 是一份面向 Windows 开发者的 Google glog 日志库预编译集成包,适用于在 Visual Studio 2017 等环境下快速接入日志功能。资源内置完整头文件、glog.dll 与 glog.lib 库文件,搭配 5 个 CMake 配置文件和 pkg-config 文件,开发者无需手动编译依赖即可直接引用,并支持 INFO、WARNING、ERROR、FATAL 等多级日志输出及堆栈回溯,适合中大型应用的调试与运行监控。整个压缩包仅 80KB,共 13 个文件,结构清晰:include/glog 头文件、bin/glog.dll 动态库、lib/glog.lib 导入库、cmake 与 pkgconfig 目录分别提供构建集成方式。已有 297 人学习下载,对于希望避开源码编译繁琐步骤、在 Windows 上无缝使用 glog 的 C++ 开发者来说,这是一份实用且轻量的入门资源,能够显著提升日志管理效率。 做 Windows 桌面端开发这几年,日志库一直是个绕不开的话题。今天我想认真聊聊 glog for windows 这套方案——把 Google 的 glog 日志库在 Windows 下完整编译、集成、调优的全过程。如果你在写 C++ 程序,尤其是需要长时间运行、出 Bug 之后又特别难复现的客户端程序,那 glog 绝对值得你花一个下午折腾明白。
glog 全称 Google Logging Library,是 Google 内部大量使用的 C++ 日志库。它上手极快,核心就几个宏,却能覆盖日志等级、条件日志、断言检查、信号处理这些常见需求。很多人一听到“Google 出品”就先入为主觉得只在 Linux 上能跑,实际上 glog 官方对 Windows 的支持已经相当完善,只是网上资料零零散散,编译时也容易踩坑。这篇文章就是来帮你把这些坑填平的。
这篇内容适合三类人:刚接触 glog 的 C++ 新手,想在 Windows 下把日志系统替换掉的客户端开发,以及被 glog 编译问题折磨过、想找个完整参考的老手。我会从为什么选它,到怎么编译,再到代码里怎么用,最后把常见问题一次性讲透。
1. 为什么要在 Windows 上折腾 glog
1.1 glog 到底是个什么东西
在你决定引入任何第三方库之前,一定要先搞清楚它解决什么问题。glog 不是一个“日志框架全家桶”,它走的是轻量、直接、可靠的路子。你只需要 include 一个头文件,链接上库,就能通过LOG(INFO)这样的宏输出日志。它会自动帮你打上时间戳、线程 ID、源文件行号,写完日志还可以自动按大小切割文件、按等级分别输出,这些功能如果自己造轮子,至少要写几百行代码。
glog 的核心优势是稳定性。它从 2005 年前后就在 Google 内部大规模使用,属于经历了超大规模生产环境验证的基础库。对于 Windows 客户端来说,日志系统最怕的就是日志线程崩溃或者锁竞争导致性能下降,glog 在这方面的处理比大部分开源日志库都成熟。
1.2 为什么单独把 Windows 拎出来说
道理很简单:Windows 和 Linux 在动态库、文件系统、编码、线程模型上差异太大,直接照搬 Linux 那套编译参数和调用方式,很容易翻车。比如__declspec(dllexport)的符号导出问题、Windows 下多字节字符集与 UTF-8 的兼容问题、运行时库 MT/MD 的匹配问题,这些都只在 Windows 上才会遇到。
另外,很多开源库在 Linux 下装起来一条命令搞定,但 Windows 下要么用 vcpkg,要么手动编译,步骤一多就容易出错。glog 的例子很典型:源码下载下来直接 cmake 也能编,但如果不注意 CMake 版本和生成器配置,可能卡在第三方依赖上。我见过不少人最后干脆放弃 glog,换回自己写的简陋日志模块,其实是太可惜了。
2. 动手前先想清楚的三件事
2.1 依赖关系:gflags 还需要吗
这个问题是新手的第一个坎。早期版本的 glog 依赖 gflags 和 libunwind,在 Linux 上装起来还算顺利,Windows 下则要额外编译两个库,非常劝退。但是从 glog 0.4.0 开始,官方已经默认去除对 gflags 的硬依赖,CMake 里GFLAGS选项默认关闭,绝大多数用法根本用不到 gflags。这意味着现在编译 glog 其实只需要一个纯粹的源码包,不需要再折腾依赖链。
如果你看到某篇旧教程还在让你先编译 gflags,那基本可以关掉了。除非你确实要用 glog 的命令行参数解析功能,否则不要开启 gflags,少一个依赖就少一层风险。
2.2 工具链:选 MSVC、MinGW 还是 Clang
Windows 下编译 C++ 库,首选肯定是 MSVC,也就是 Visual Studio 自带的编译器。原因很简单:你的项目大概率也是用 MSVC 编译的,保持工具链一致能避免大量 ABI 兼容问题。glog 官方 CMake 对 MSVC 支持得很好,从 VS2015 到 VS2022 都能顺利编过。
MinGW 也能编 glog,但我不推荐在 Windows 上用它做生产构建。MinGW 版本的 glog 对 Windows API 的封装调用偶尔会有小瑕疵,而且后续如果你想接入 Visual Studio 的调试器,符号文件兼容性也不如 MSVC 来得顺畅。
Clang 在 Windows 上的情况这两年好了很多,但如果你的主工程是 MSVC,没必要为了 glog 单独引入 Clang 工具链。
2.3 动态库还是静态库:不要拍脑袋决定
Windows 下用 glog,首先就要确定链接方式是动态库(DLL)还是静态库(LIB)。这直接关系到你的程序发布体积和部署复杂度,也关系到调试体验。
如果你写的是内部工具、插件、测试程序,我建议直接用静态库。静态链接 glog 后,程序只需要拷贝一个 exe 就能跑,不会出现“缺 glog.dll”这种低级问题,部署省心。如果你写的是大型插件系统,或者多个模块都要用 glog,那动态库更合适,可以避免每个模块都备份一份日志实现,内存占用也更小。
但有一个坑必须提醒:静态库在 Debug 和 Release 下必须严格区分,glogd.lib对应 Debug,glog.lib对应 Release,两者绝不能混用。而且链接 glog 时,整个项目的运行时库选项要保持一致,在 Visual Studio 里就是/MD、/MT、/MDd、/MTd这几项,工程属性里的“代码生成 -> 运行库”要选对,否则链接阶段你会看到一堆莫名其妙的 LNK2038 冲突。
3. Windows 下编译 glog 的完整路线
3.1 从源码编译:CMake 加 Visual Studio 的经典组合
最简单的路线是直接拿源码编译。先去 GitHub 仓库把源码拉下来,或者下载 release 包解压到本地。在开始之前,请确认你已经装了 Visual Studio,并且安装了“使用 C++ 的桌面开发”工作负载,里面包含了 CMake 工具链。
打开“x64 Native Tools Command Prompt for VS”,进入源码目录,依次执行:
mkdir build cd build cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_CONFIGURATION_TYPES="Debug;Release" .. cmake --build . --config Release cmake --build . --config Debug-A x64参数是必须的,因为默认生成器可能是 Win32(x86),对不上 64 位程序会出问题。CMAKE_CONFIGURATION_TYPES指定你需要生成的版本类型,如果嫌麻烦,只写 Release 也行,但我强烈建议 Debug 和 Release 都编出来,因为调试的时候要用 Debug 版库,发布的时候用 Release 版库。
编译完成后,在 build 目录下会生成Release和Debug文件夹,里面包含glog.lib、glog.dll和glog.dll.a等文件。接下来需要把头文件和库文件复制到工程目录里:src/windows下的头文件复制到 include 目录,编译出来的库文件复制到 lib 目录,DLL 放到最终程序目录。
配置 Visual Studio 工程时,需要在“项目属性 -> VC++ 目录 -> 包含目录”中加入 include 路径,在“库目录”中加入 lib 路径,然后在“链接器 -> 输入 -> 附加依赖项”中填入glog.lib或glogd.lib。这一步不要漏了DbgHelp.lib,glog 在 Windows 上抓取堆栈信息时会用到它。
3.2 用 vcpkg 一键集成:推荐给不想折腾的人
如果不想手工编译,vcpkg 是最省事的方案。vcpkg 是微软维护的 C++ 包管理器,一条命令就能装好 glog:
vcpkg install glog:x64-windows这个命令会帮你编译 glog 以及它所需的依赖,并把库安装到 vcpkg 的安装目录下。使用方式很傻瓜:在 Visual Studio 工程里,通过“项目属性 -> vcpkg -> 使用 vcpkg 清单文件”开启 vcpkg 集成,然后在 CMakeLists.txt 或工程设置里直接引用 glog 即可。
我用 vcpkg 编译过多次 glog,体验比手动 CMake 更顺畅,尤其适合项目里已经用 vcpkg 管理依赖的团队。不过它也有个弊端:如果你需要定制 glog 的某些编译选项,比如关闭符号前缀或者修改日志端口,vcpkg 默认编译参数不一定能满足,这时还是要走源码编译路线。
3.3 链接时常见的坑:宏定义与符号冲突
很多人在 Windows 下链接 glog 时发现编译没问题,链接却报一堆 unresolved external symbol。这通常是因为没有正确添加GLOG_NO_ABBREVIATED_SEVERITIES宏或者没有把using namespace google;引入。实际上,glog 在 Windows 下的头文件默认就定义了ERROR等宏,这会导致LOG(ERROR)在 Windows 头文件里和windows.h产生冲突。
解决办法有两种:第一,在 include 相关文件之前先定义GLOG_NO_ABBREVIATED_SEVERITIES,这样 glog 会使用GLOG_ERROR这样完整的名字,不会和windows.h的ERROR宏撞车;第二,如果你确实想在代码里用LOG(ERROR)这种简洁写法,那就在 includewindows.h之前先 include glog 的头文件,并确保编译选项里没有强制先展开 windows 头文件的情况。
另外,链接glog.lib时,如果程序同时也用了其他日志库或者 signals 库,容易出现符号冲突。glog 在 Windows 下会导出google::LogMessage等相关符号,如果另一个库也定义了同名符号,链接器不会报错,但运行时会调错实现,这类问题排查起来非常头疼。预防办法是尽量让 glog 成为项目里唯一的日志基础设施,不要多个日志库并存。
4. glog 在 Windows 上的核心用法
4.1 初始化:google::InitGoogleLogging
几乎所有 glog 程序都必须先调用初始化函数,否则某些功能不会正常工作:
#include <glog/logging.h> int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); LOG(INFO) << "glog started!"; return 0; }InitGoogleLogging的主要作用是让 glog 记录程序名,并初始化日志输出格式。如果你的程序是带界面的 Windows GUI 程序,argv可能为空或者不符合预期,这时可以传入一个固定的字符串,比如可执行文件名。
我测试时发现一个细节:在 GUI 程序里,如果不调用InitGoogleLogging,日志默认不会输出到文件,日志数据可能直接丢弃。所以哪怕你没有命令行参数,也一定要在main函数最开始调用初始化,传入"myapp"这样的程序名即可。
4.2 分级日志与条件日志
glog 的日志等级有四种:INFO、WARNING、ERROR、FATAL。FATAL 级别比较特殊,它输出后会直接终止程序,所以适合用来断言“这个错误如果发生,程序就不能继续跑”。
教你一个很实用的写法,用LOG_IF做条件日志:
int failedCount = 3; LOG_IF(ERROR, failedCount > 2) << "失败次数过多,需排查网络或服务器端";这种方式最大的好处是:只有当指定条件满足时才会计算日志参数,不会对性能产生任何影响。同理还有LOG_EVERY_N,它表示每隔 N 次才会打印一次日志,适合避免高频循环里刷屏,又不想完全关掉的场景:
for (int i = 0; i < 1000; i++) { LOG_EVERY_N(INFO, 100) << "当前循环次数: " << i; }4.3 日志文件输出规则与轮转
glog 默认会把日志写到当前工作目录下,文件命名格式是程序名.hostname.用户名.log.等级.日期时间.进程ID。Windows 上可能出现主机名、用户名带中文或特殊字符的情况,日志文件名会有乱码风险,这时可以用google::SetLogFilenameExtension来固定扩展名,或者手动设置日志目录。
日志文件默认会定期切割,单个文件超过一定大小后会自动生成新文件。切割大小的默认值是 1800 MB,如果你不需要这么大的文件,可以通过FLAGS_logbufsecs、FLAGS_max_log_size来调。
我个人的习惯是设置一个独立日志目录,别让日志散落在 exe 旁边,否则客户那边目录一乱,定位问题反而困难:
google::SetLogDestination(google::GLOG_INFO, "C:/logs/myapp_info_"); google::SetLogDestination(google::GLOG_ERROR, "C:/logs/myapp_error_");这样 ERROR 及其以上级别的日志会单独写到错误文件里,排查时直接看错误文件,不用在 INFO 大文件里翻来翻去。
5. 常见问题与排查技巧实录
5.1 编译时报错:找不到glog/logging.h
这种问题 90% 是因为包含目录没有配置正确,或者是用了相对路径导致 Visual Studio 找不到。排查思路很直接:先确认 glog 源码是否编译过、头文件是否真的存在;再检查工程属性里的“包含目录”是否指向了正确的 include 路径。
还有一个容易忽略的点:如果你是通过 vcpkg 集成,但 CMake 配置里没有写find_package(glog REQUIRED),那么头文件路径也不会被自动添加。正确做法是写:
find_package(glog REQUIRED) target_link_libraries(myapp PRIVATE glog::glog)5.2 运行时提示找不到glog.dll
这是 Windows 新手最容易碰到的坑。程序编译链接都通过,运行的时候却报“找不到 glog.dll”。原因很简单:运行目录下没有这个 DLL,或者 DLL 依赖的其他系统库缺失。最简单的临时解决办法是把编译生成的glog.dll复制到 exe 同目录;长期建议是用静态库或者用安装包工具把 DLL 一并打包。
另外,如果用的是 MSVC 动态库,还要注意glog.dll是否依赖了特定版本的 VC++ 运行库,比如vcruntime140.dll。目标机器没装对应运行库,也会报类似错误。发布前最好用dumpbin /dependents glog.dll查看一下依赖关系。
5.3 日志中文乱码问题
glog 在 Windows 上输出的日志,默认编码取决于控制台代码页和源文件的保存编码。Visual Studio 默认源码是 UTF-8 的话,花括号、中文字符可能在旧版 MSVC 下显示乱码。
我的建议是:源文件统一保存为 UTF-8 with BOM,日志内容尽量用英文或 ASCII 字符,尤其是程序对外发布时,中英文混合日志在其他日志分析工具里很容易错位。如果一定要输出中文,可以在日志输出前做一次编码转换,把 UTF-8 转成当前系统代码页再输出。但要注意,Windows 10 以上默认代码页可能是 UTF-8 或者 GBK,这个转换逻辑必须和客户端环境一致,否则没用。
5.4 符号冲突与应用崩溃
如果你在 Windows 下同时用了 glog 和windows.h,最常见的就是ERROR宏冲突。这个前面已经说了,最稳妥的办法就是定义GLOG_NO_ABBREVIATED_SEVERITIES,然后代码里用GLOG_ERROR代替ERROR。如果项目里代码很多,不想全局改,那就在所有#include <windows.h>之前先 include glog 头文件,避免 головний Windows 宏优先定义。
还有一个崩溃场景我印象很深:程序退出时,某些全局对象的析构顺序不确定,如果 glog 还在被某个后台线程使用,而全局对象已经销毁,就会触发访问冲突。这种情况没有万能解法,只能确保退出路径上先google::ShutdownGoogleLogging(),再停止所有可能打日志的线程,最后退出主流程。
5.5 性能问题:日志写得太慢
有人抱怨 glog 写入文件太慢。其实 glog 本身做了异步缓冲,瓶颈往往不在 glog,而在输出路径。如果你把日志写到网络共享目录或者杀毒软件扫描的路径,性能会大幅下降。建议日志目录添加杀毒软件白名单,同时把日志目录放在本地 SSD 上。
另外,不要在循环里高频调用LOG(INFO),即使条件不满足也会造成函数调用开销。需要时用VLOG或日志等级的预编译宏来做编译期过滤,比如:
#define GLOG_THRESHOLD 0然后把LOG(INFO)替换成LOG_IF(INFO, GLOG_THRESHOLD <= 0),这样生产环境可以直接将日志级别调高,减少大量无意义输出。
根据我的实际操作经验,Windows 下集成 glog 最合理的路径还是:用 vcpkg 快速引入,编译成动态库,项目和 glog 保持同一套 MSVC 工具链,代码里定义GLOG_NO_ABBREVIATED_SEVERITIES,日志目录单独配置并添加白名单。这样一套下来,日志系统基本不用再操心了。
最后再分享一个小技巧:如果你不想让最终用户看到完整的日志堆栈,可以用google::SetStderrLogging(google::GLOG_INFO)把日志输出到标准错误输出,然后通过 Windows 的事件查看器或者DebugView这类工具远程抓取。很多运维问题在客户机器上没法直接开调试器,但日志文件往往已经记录了关键线索,配合好 glog 的切割和分级,你就能在问题发生后的第一时间拿到完整脉络。
本文还有配套的精品资源,点击获取