尧图精选

VS中C++项目集成glog日志库:从编译到配置全解析

🕒 发布时间:2026/9/7 20:29:03 📁 来源:尧图网络
1. 为什么要在VS里引入glog日志库选型的底层逻辑1.1 你真正需要的日志能力如果你在C项目里干过几个月大概率会走到这一步printf打天下打到想吐调试信息散落各处线上崩溃日志全靠猜多线程环境下日志顺序乱成一锅粥。这时候就需要把日志这件事正经地做成“基础设施”而不是随手写几行输出。glog就是Google内部那套日志系统开源出来的版本C项目里用得非常多尤其在Windows Visual Studio以下简称VS这套环境下配置得当之后它的稳定性和实用性非常能打。先说清楚glog到底帮你解决了什么。它不是你随口调用的std::cout而是一套完整的日志框架核心能力包括分级日志输出INFO / WARNING / ERROR / FATAL、按大小自动切割日志文件、每条日志自动带时间戳和源码位置、支持条件日志和每N条日志打一条这种细粒度控制、FATAL级别自动触发程序终止并留下栈信息。这些能力覆盖了绝大多数C项目对日志的真实需求从日常调试到线上问题追溯都用得上。我在真实项目里最看重的是三点。一是日志级别体系能让INFO和ERROR各归其位代码评审时一眼就能看出哪里是正常流程、哪里是异常分支二是条件日志LOG_IF、LOG_EVERY_N这类宏能极大减少“日志刷屏”和“无效日志”的问题三是FATAL级别的崩溃钩子配合调试器可以精确还原崩溃现场。所以这篇文章不打算只给一份“抄作业”式的配置步骤而是把“为什么这么配”“去哪编译库”“踩过哪些坑”一起讲透你配置完心里有底后续扩展也顺手。1.2 主流日志库横评glog、spdlog、log4cplus怎么选选glog之前建议你先花两分钟想清楚自己的场景因为C日志库不止glog一个选错方向后面会很痛苦。我日常用过的有spdlog、log4cplus、glog三套简单对比一下对比维度glogspdloglog4cplus日志级别四级INFO/WARNING/ERROR/FATAL六级trace到critical多级可自定义性能侧重中高重稳定性极高异步模式强中功能全面配置方式命令行参数 环境变量代码API配置文件上手难度低低中典型场景Google系项目、需要源码级调试高频日志、游戏服务器企业级老项目如果你的项目是高并发、日志量极大、对性能敏感到每秒几万条以上spdlog的异步模式会更强如果你是从Java log4j转过来的老开发log4cplus的配置风格你会更熟悉。但如果你是在VS里做Windows桌面程序、工具链软件、客户端组件或者团队本身是Google C风格glog是最稳的选择。为什么这么说glog对源码位置的记录、对FATAL语义的处理、对崩溃信号的捕获在Windows上配合VS的调试体验非常顺。它不会给你整一堆用不上的配置项也没有XML/JSON配置文件的额外学习成本编译好之后把库一链头文件一引LOG(INFO)走天下。这篇博文全部以glog为主线展开。2. 获取glog库的两种主流方式2.1 方式一vcpkg一键集成省心但要知道原理在VS里引入任何第三方C库最大的痛点不是写代码而是“库从哪来”。最省心的方案就是用vcpkg。vcpkg是微软官方维护的C包管理器装好之后一条命令就能把glog拉下来编译好并且自动与VS工程集成。先看安装vcpkg的标准流程。打开PowerShell或CMD找个干净目录执行git clone https://github.com/microsoft/vcpkg.git cd vcpkg bootstrap-vcpkg.batbootstrap-vcpkg.bat会生成vcpkg.exe然后设置环境变量或者直接调用。接着安装glogvcpkg install glog:x64-windows这里x64-windows指定的是64位动态库版本。如果你用的是32位工程改成x86-windows如果你想用静态库用x64-windows-static。这一步会拉取glog依赖的gflags等库一起编译耗时取决于网络和机器性能。安装完最关键的一步是集成到VS。继续执行vcpkg integrate install这个命令会在VS里注册一个全局配置让你新建工程后不需要手动配置头文件路径和库路径就能直接#include glog/logging.h。听着很美好不过我建议你仍然花两分钟看一下它到底做了什么它本质上是往VS的Microsoft.Cpp.x64.user属性表里写入了包含目录和库目录。这意味着你打开任何一个VS工程的属性页都能在“VC目录”里看到vcpkg的路径。vcpkg方案适合快速搭环境、或者在多个工程之间共享同一套库。但它有一个隐藏问题库版本升级由vcpkg控制你没法轻易“锁定”某个commit如果团队里有人更新了vcpkg到新版本glog的行为可能发生变化导致你的代码突然编译不过或者链接报错。所以如果是正式项目建议在说明文档里固定vcpkg的git版本或者直接走源码编译方案。2.2 方式二源码编译glog可控性最强的路线如果你不想依赖包管理器或者需要定制glog的编译选项或者公司内网环境没法随意拉取vcpkg依赖那就老老实实从源码编译。这条路线其实不复杂我来拆解一遍。第一步获取源码git clone https://github.com/google/glog.git cd glog第二步用CMake生成VS工程。在glog目录下新建一个build文件夹然后执行cd build cmake .. -G Visual Studio 17 2022 -A x64 -DCMAKE_INSTALL_PREFIX./install这里的-G要跟你的VS版本对应VS2019就是Visual Studio 16 2019VS2022就是Visual Studio 17 2022。-A x64指定64位架构。CMAKE_INSTALL_PREFIX是安装路径我习惯放在build/install里方便后面引用。第三步编译并安装。直接打开生成的glog.sln用VS编译也行我更推荐命令行方式干净利落cmake --build . --config Release --target install注意--config ReleaseDebug版本可以单独编一次但默认先出Release。编译完成后你会发现build/install目录下有include和lib两个子目录这就是后面配置VS工程所需要的全部东西。源码编译方案最大的优势是可控。你在CMake配置阶段可以加一堆开关比如-DBUILD_SHARED_LIBSOFF编静态库、-DWITH_GFLAGSOFF去掉gflags依赖等。对于大多数不需要gflags的工程我建议直接关掉gflags减少依赖链。命令如下cmake .. -G Visual Studio 17 2022 -A x64 -DCMAKE_INSTALL_PREFIX./install -DBUILD_SHARED_LIBSOFF -DWITH_GFLAGSOFF这里面BUILD_SHARED_LIBSOFF会生成静态库WITH_GFLAGSOFF则让glog不依赖gflags。关掉gflags之后你在代码里就没法用FLAGS_v这种gflags风格的命令行参数来控制日志了但对大部分场景没有影响。3. VS工程配置的核心步骤3.1 工程属性配置包含目录、库目录、附加依赖项一次配齐拿到编译好的glog无论是vcpkg还是源码编译下一步就是让VS工程认这套库。右键项目 - 属性切到“所有配置”和“所有平台”开始配置。不要只在Debug或Release里单独配否则换个配置就编译不过而且很难排查。先配“VC目录”下的“包含目录”把include路径填进去。如果你用源码编译方案路径就是build/install/include如果用vcpkg理论上不需要手动填但为了保险你还是可以检查一下是否有vcpkg的路径。然后是“库目录”把lib路径填进去比如build/install/lib。最后是“链接器 - 输入 - 附加依赖项”填库文件名。这里有一个非常关键的细节Release版本和Debug版本的库文件名不一样。通常Release版是glog.libDebug版是glogd.lib。如果你只编译了Release库却用Debug配置去链接会直接报LNK1104 cannot open file glogd.lib。解决方案有三种一是切换到“Debug”配置再填一次Debug的库名二是把Debug和Release都编译出来三是用宏区分在附加依赖项里写glog.lib然后在“链接器 - 输入 - 附加依赖项”旁边有个“继承的值”你可以在预处理宏里加一个_DEBUG分支效果一样。但实际项目里我强烈建议Debug和Release的库都装上因为你总会在Debug下调试。3.2 运行库匹配这是90%链接错误的根源配置完目录和库名你以为能编译了先别急还有一个隐藏大坑运行库不匹配。VS的C运行库有两种模式/MT静态运行时和/MD动态运行时。如果你的工程用的是/MD默认值Release最常见但编译glog时CMake设置成了静态运行时链接时就会报一团乱麻的错误最常见的是LNK2038 mismatch detected for RuntimeLibrary: value MT_StaticRelease doesnt match value MD_DynamicRelease。务必要把工程属性和glog编译选项对齐。检查方式在“C/C - 代码生成 - 运行库”里查看当前值。如果你用的是/MD那么源码编译glog时不要额外乱改CMake的运行库参数直接用默认值CMake默认跟随VS的设置如果你用静态运行时/MT则需要在CMake时指定cmake .. -DCMAKE_CXX_FLAGS_RELEASE/MT -DCMAKE_CXX_FLAGS_DEBUG/MTd其实最稳妥的办法是先看自己工程的运行库设置再据此确定glog的编译选项两边一致再继续。提示vcpkg的x64-windows默认编的是/MD动态库如果你的工程是/MT需要安装x64-windows-static版。这一点非常容易忽略很多人配完还是报LNK2038排查半天发现是运行库不一致。4. 代码接入与日志落地4.1 初始化glog与最常用的日志宏库配置好之后开始写代码。先引入头文件并初始化#include glog/logging.h int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); LOG(INFO) This is an info log; LOG(WARNING) This is a warning log; LOG(ERROR) This is an error log; google::ShutdownGoogleLogging(); return 0; }InitGoogleLogging接收程序名这个名称会出现在日志文件名的前缀里。ShutdownGoogleLogging在程序退出前调用确保日志缓冲区全部落盘。写到这里有几个细节提醒。一是LOG(FATAL)会导致程序abort如果你不想让它终止程序可以用LOG(ERROR)替代或者自定义google::InstallFailureFunction来接管FATAL行为。二是LOG_IF非常实用按条件打日志int ret DoSomething(); if (ret ! 0) { LOG(ERROR) DoSomething failed, ret ret; } // 等价写法 LOG_IF(ERROR, ret ! 0) DoSomething failed, ret ret;三是LOG_EVERY_N控制频次防止高频循环里日志刷爆磁盘for (int i 0; i 100000; i) { LOG_EVERY_N(INFO, 1000) Processing i th item; }这个宏的意思是每1000次打一条日志对线上服务或者长跑分析程序来说非常有用。4.2 日志文件输出与切割策略默认情况下glog是输出到stderr的在VS里跑的时候“输出窗口”可以看到但程序关闭后日志就没了。要落盘有两个办法。方法一设置标志FLAGS_log_dir指定日志输出目录FLAGS_log_dir D:/logs/; google::InitGoogleLogging(argv[0]);这样glog会自动在该目录下生成文件命名规则是programname.hostname.user.log.severity.date.time.pid比如mytest.local.admin.log.ERROR.20241115-103025.12345。注意一旦指定了日志目录INFO、WARNING、ERROR级别都会分别写到对应文件里。说到目录这个路径必须真实存在glog不会自动创建目录路径不存在时日志会静默丢失。我第一次用的时候就在这里栽过因为程序启动时指定了一个不存在的目录结果所有日志都不见了好不容易才排查出来。方法二使用glog的命令行参数机制。在InitGoogleLogging之后所有以FLAGS_开头的变量都可以通过命令行参数覆盖比如mytest.exe --log_dirD:/logs --minloglevel0这其实是gflags风格的继承如果编译时开了gflags生效--log_dir就能直接解析。如果关掉了gflagsFLAGS_log_dir仍然存在但命令行解析功能弱化建议直接用代码设置。日志切割由glog内部自动处理默认单文件超过1GB左右会触发切割——但说实话1GB对日常开发来说太大我一般用FLAGS_max_log_size调整单位是MBFLAGS_max_log_size 100; // 单文件100MB切割然后配合FLAGS_log_level、FLAGS_stderrthreshold来精细化控制。FLAGS_stderrthreshold表示某个级别及以上的日志同时输出到stderr比如设成google::WARNING那么WARNING和ERROR日志除了写文件还会打到控制台方便开发时实时看。5. 编译链接常见问题排查实录5.1 经典LNK错误与运行库不匹配在VS里引第三方库链接错误真是天天见我先把最常见的几个罗列出来。LNK1104 cannot open file glog.lib库目录没配对或者没找到这个库文件。先确认lib目录下确实有glog.lib如果是Debug配置下的报错多半是你没编译Debug库而附加依赖项里填了glogd.lib但文件不存在。LNK2038 RuntimeLibrary mismatch运行库不一致工程是/MD但glog是/MT编的或者反过来。解决办法要么重新编glog要么改工程设置两边对齐。注意这个错误有时候不会直接报LNK2038而是变成一堆奇奇怪怪的外部符号错误比如__imp_??...无法解析别被表象迷惑先查运行库。LNK2019 unresolved external symbol class std::basic_ostream...这个也经常是运行库不匹配导致的因为C标准库的实现方式在/MT和/MD下不同。如果错误堆里混合了大量STL相关符号优先检查运行库。LNK2001 unresolved external symbol void __cdecl google::InitGoogleLogging...这个更直白链接器找不到glog的函数实现。除了路径配错之外有一个隐蔽原因你可能没定义GOOGLE_GLOG_DLL_DECL宏。当glog编译成动态库时头文件里的导出导入声明依赖这个宏缺少它会导致找到头文件但找不到符号。解决方案是在预处理定义里加上GOOGLE_GLOG_DLL_DECL或者在源码编译glog时选择静态库。5.2 DLL找不到与运行路径问题如果你用的是动态库版glog编译链接都过了但程序一运行就报“找不到glog.dll”。这个问题的本质是动态库没有放到系统搜索路径里。三个解法按优先级排序一是把glog.dll复制到可执行文件exe同目录下最直接最稳妥。二是把库所在路径加到系统环境变量PATH里但这个要重启VS甚至重启系统才生效容易造成环境污染不推荐。三是用VS的调试工作目录设置把包含DLL的目录设为“调试 - 工作目录”。不过这个只在VS里调试时有效独立运行exe还是会报错。我建议直接用第一种构建后事件脚本自动拷贝copy /Y $(SolutionDir)..\lib\glog.dll $(TargetDir)如果用的是源码编译DLL通常都在build/install/bin下面拷到工程输出目录就行。注意如果同时用了多个第三方库且它们依赖不同版本的同一个DLL那才是真正的噩梦。我遇到过glog和另一个库都依赖不同版本的gflags导致运行时崩溃。这种问题的排查思路是用dumpbin /dependents your_exe.exe查看依赖链把所有DLL版本对齐。5.3 多线程环境与FATAL崩溃的真实体验glog本身是线程安全的多线程并发打日志不会出现数据竞争。但有一个使用习惯要注意InitGoogleLogging和ShutdownGoogleLogging必须确保线程安全建议在main函数里单线程调用不要在全局对象构造或析构时触发。FATAL日志是另一个坑。默认情况下LOG(FATAL)会打印栈信息然后调用abort()。在Windows VS的Debug配置下你会看到一个“中断”弹窗大部分时候这个是有用的因为它帮你定位到了崩溃点。但在Release发布版里弹窗会影响用户体验。我一般这么处理google::InstallFailureFunction([]() { // 自定义崩溃回调比如把崩溃信息发送到日志服务器 std::cerr Fatal error occurred, see log for details std::endl; });这样FATAL时不会abort得那么粗暴但注意你替换掉默认的abort行为后“崩溃前释放资源、保存现场”这些逻辑需要你自己保证。如果你是做服务端程序建议还是保留默认abort让守护进程来拉起来。还有一个问题在Windows上特别突出glog的FATAL信号捕获对Windows的SEH结构化异常处理支持不如Linux下的POSIX信号好。也就是说如果你的程序是因为访问空指针触发的崩溃那走的是Windows异常处理机制glog默认的InstallFailureSignalHandler不一定会捕获到反而LOG(FATAL)这种主动触发的FATAL能正常走钩子。这是个很多人不知道的差异。如果你的Windows程序要捕获完整崩溃栈建议额外用Windows自己的SetUnhandledExceptionFilter或者把glog和breakpad一起用。6. 从能用到好用glog工程的进阶配置与调试技巧6.1 多工程解决方案下如何统一配置glog很多人做项目不是一个工程而是有一个解决方案solution包含多个项目核心库工程、业务库工程、主程序工程、测试工程。如果每个工程都手动配一遍属性维护成本很高。我的做法是用VS的属性表Property Sheet统一管理。在解决方案里新建一个glog.props属性表把这些配置写进去?xml version1.0 encodingutf-8? Project ToolsVersion4.0 xmlnshttp://schemas.microsoft.com/developer/msbuild/2003 PropertyGroup IncludePath$(SolutionDir)third_party\glog\include;$(IncludePath)/IncludePath LibraryPath$(SolutionDir)third_party\glog\lib\$(Configuration)\$(Platform);$(LibraryPath)/LibraryPath /PropertyGroup ItemDefinitionGroup Link AdditionalDependenciesglog.lib;%(AdditionalDependencies)/AdditionalDependencies /Link /ItemDefinitionGroup /Project然后在每个工程里右键“添加现有属性表”这个表就会应用到所有工程。以后升级glog版本只要替换目录内容或改属性表里的路径不需要动工程代码。这个做法在只有两三个工程的时候显得多余项目一多优势特别明显。还有一点值得做在Debug|x64和Release|x64下分别设置预处理宏。由于glog的Debug库名是glogd.lib我通常会在属性表里用条件表达式区分Link AdditionalDependencies Condition$(Configuration)Debugglogd.lib;%(AdditionalDependencies)/AdditionalDependencies AdditionalDependencies Condition$(Configuration)Releaseglog.lib;%(AdditionalDependencies)/AdditionalDependencies /Link这样一劳永逸不用每个配置来回切换着填库名。6.2 用glog进行性能定位与线上问题排查日志除了记录程序轨迹其实还承担着一个重要职责性能定位。你可以在关键路径前后记录耗时用LOG(INFO)输出时间戳配合glog自带的时间信息就能大概判断瓶颈。但注意日志本身也有性能开销在for循环里每轮都打日志IO开销会反过来拖慢程序。这时候LOG_EVERY_N和LOG_IF_EVERY_N就是救星。另外我推荐一个“统计型日志”的写法在循环结束后一次性汇总打日志避免高频日志的干扰。比如int successCount 0, failCount 0; for (...) { if (DoSomething()) successCount; else failCount; } LOG(INFO) Total: successCount failCount , success: successCount , fail: failCount;这样在应用日志里看到的是干净的汇总信息排查效率远高于熬着一屏滚动的单条日志。线上排查场景里glog最有用的是ERROR和FATAL日志文件。我习惯在程序启动时把LOG(ERROR)单独输出到error文件同时通过FLAGS_stderrthreshold让控制台只显示WARNING以上日志。这样即使程序跑了一整天我只需要翻error日志就能定位问题不需要在几十MB的INFO日志里大海捞针。6.3 日志序列化与自定义输出glog的LOG(INFO) 重载支持一切可以通过ostream流输出的类型但如果你要记录一个自定义结构体就得自己重载operator。我在一个通信项目里记录数据包时这么干过struct PacketHead { uint32_t seq; uint8_t type; uint16_t len; }; std::ostream operator(std::ostream os, const PacketHead head) { os [seq head.seq , type static_castint(head.type) , len head.len ]; return os; }这样LOG(INFO) packetHead就能直接得到可读的输出不需要每次打日志都写一段代码拼字符串。这个习惯养成了日志代码的整洁度会高很多。如果你想控制日志的格式细节比如去掉文件名、行号可以用google::SetLogFilenameExtension或自定义LogMessage的回调。不过大部分场景默认格式就够了。7. 收尾我在实际项目中积累的几条经验最后分享几个我踩过坑后沉淀下来的小习惯。第一glog的头文件在Windows下偶尔会和Windows.h有符号冲突尤其是如果你还引入了windows.h建议先包含glog/logging.h再包含其他头文件或者使用WIN32_LEAN_AND_MEAN宏避免一堆无关的Windows定义。第二如果你在同一个程序中同时用了glog和gtest一定要注意它们可能都通过gflags暴露同名变量尽量减少gflags依赖或者在编译glog时关掉它。第三发布程序时需要把glog的DLL或静态库一起带上且要同时确认发布版本是Release编译的否则客户机器上会突然冒出来一堆_ITERATOR_DEBUG_LEVEL的错误。配置glog这件事本身不难但它涉及库编译、工程属性、运行库匹配、头文件引入、动态库部署等多个环节任何一个环节出错报错信息都容易让人一头雾水。我写这篇博文的价值就是把这些错误和解决方案一次性摆出来你在从零配置的时候能少走很多弯路。按上面的顺序操作一遍基本十分钟内就能让glog在VS的C工程里跑起来然后你就能享受一套踏实可靠的日志系统带来的便利了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →