
简介本资源是专为Windows平台开发者提供的glog日志库预编译与构建支持包面向C中高级开发者解决在VS2017环境下快速集成Google高性能日志系统的核心痛点。资源共13个文件涵盖5个CMake构建脚本用于跨版本适配与自定义编译、5个头文件含logging.h等核心接口、1个动态链接库glog.dll、1个静态库glog.lib及1个pkgconfig配置文件完整支撑开发、链接与环境配置全流程压缩包仅80KB轻量易部署。目前已有301人学习下载说明其在Windows原生C项目日志能力建设中具备较强实践认可度。用户可直接复用该包完成glog初始化、多级日志输出INFO/WARNING/ERROR/FATAL、堆栈回溯调试、VLOG条件日志及异步日志配置无需从源码手动编译依赖项显著降低gflags联动配置门槛特别适合嵌入式工具链、桌面客户端及教学实验项目中的日志模块快速落地。1. glog for Windows不是简单“编译一下就能用”的日志库而是 Windows 下 C 项目日志链路的起点与断点你有没有遇到过这样的场景在 Windows 上调试一个用 CMake 构建的 C 服务程序跑着跑着就静默崩溃了连一句LOG(INFO) entering process loop;都没打出来或者好不容易启用了--logtostderr1却发现日志里混着乱码、时间戳错位、线程 ID 显示为0x00000000甚至CHECK_EQ(a, b)失败时只抛出FATAL字样堆栈却卡死在ntdll.dll里这不是你代码的问题——这是 glog 在 Windows 平台默认行为与底层运行时MSVCRT / UCRT、符号解析机制、SEH 异常处理模型之间未对齐导致的典型症状。glog for Windows 不是 Linux 上那个“开箱即用”的日志库平移版它是一套需要主动适配 Windows 生态链的日志基础设施从编译器运行时选择/MD vs /MT、结构化异常捕获SEH vs structured exception handling、符号文件PDB绑定方式到日志轮转策略在 NTFS 上的原子性保障每一步都藏着可复现、可定位、但极易被忽略的断点。本文面向已在 Windows 上落地中大型 C 项目的开发者——你不需要从零造轮子但必须知道这份 glog 资源包里哪些文件是“能直接抄的”哪些配置是“改错一个就全链路失效”的以及为什么某次升级 MSVC 后LOG_EVERY_N突然不生效了。2. 编译前必问的三个问题为什么不能直接cmake .. makeWindows 下 glog 的构建逻辑与 Linux 有本质差异glog 在 Windows 上的构建不是“跨平台 cmake 脚本自动适配”那么简单。它的核心矛盾在于Linux 版本重度依赖libunwind和glibc的backtrace()而 Windows 没有等价物同时MSVC 工具链对__declspec(thread)、_set_se_translator、MiniDumpWriteDump等 API 的调用约束远比 GCC 严格。因此一份真正可用的 glog for Windows 资源包必须明确回答以下三个问题2.1 你用的是 MSVC 还是 MinGW运行时链接方式决定了日志能否捕获 SEH 异常提示glog 的CHECK/DCHECK宏在 Windows 下默认通过SetUnhandledExceptionFilterMiniDumpWriteDump捕获崩溃但这仅对 MSVC 编译的二进制有效。MinGW-w64 使用setjmp/longjmp模拟 SEHglog 默认不启用该路径强行编译会导致FATAL时无堆栈、无 dump。若你使用 MSVC推荐需确保CMake 配置时显式指定-DGOOGLE_GLOG_BUILD_TESTSOFFWindows 测试用例大量依赖 POSIX 接口会编译失败启用/EHsc编译选项C 异常处理并禁用/GL全程序优化—— 否则LOG(FATAL)触发的std::terminate可能被内联导致异常处理器注册失败# 正确的 MSVC 构建命令以 VS2022 x64 工具链为例 cmake -G Visual Studio 17 2022 -A x64 ^ -DGOOGLE_GLOG_BUILD_TESTSOFF ^ -DGOOGLE_GLOG_INSTALLON ^ -DGOOGLE_GLOG_WITH_GFLAGSOFF ^ -DCMAKE_BUILD_TYPERelWithDebInfo ^ .. cmake --build . --config RelWithDebInfo --target INSTALL-DGOOGLE_GLOG_WITH_GFLAGSOFFWindows 下 gflags 库存在 ABI 兼容性风险尤其静态链接时glog 自带基础参数解析已足够无需强耦合-DCMAKE_BUILD_TYPERelWithDebInfo必须启用调试信息PDB否则LOG(FATAL)生成的 minidump 无法解析符号堆栈全是0x00007ff...2.2 日志输出目标stdout/stderr在 Windows 控制台与服务模式下行为完全不同Linux 下LOG(INFO) hello默认写入stderrWindows 控制台程序同样如此但当你的程序作为 Windows Service 运行时stderr句柄无效日志会直接丢失。glog 提供SetStderrLogging()和AddLogSink()但关键在于初始化时机必须在google::InitGoogleLogging(argv[0])之后、任何LOG调用之前设置Service 场景下应重定向到文件并启用google::SetLogFilenameExtension(svc)#include glog/logging.h #include windows.h int main(int argc, char** argv) { // 1. 初始化 glog必须第一句 google::InitGoogleLogging(argv[0]); // 2. Service 模式下关闭 stderr 输出强制写文件 if (IsService()) { google::SetStderrLogging(google::GLOG_FATAL); // 只让 FATAL 写 stderr用于调试 google::SetLogFilenameExtension(svc); google::SetLogDestination(google::GLOG_INFO, logs/); google::SetLogDestination(google::GLOG_WARNING, logs/); google::SetLogDestination(google::GLOG_ERROR, logs/); google::SetLogDestination(google::GLOG_FATAL, logs/); } LOG(INFO) Service started; return 0; }IsService()是伪代码实际需通过GetModuleHandleA(services.exe)或检查CreateToolhelp32Snapshot中父进程名判断SetLogDestination(level, prefix)的prefix参数在 Windows 下必须以反斜杠结尾如logs/否则 glog 会拼接出logs\myapp.INFO错误路径源码中硬编码了\分隔符2.3 符号解析没有 PDBLOG(FATAL)就是黑匣子glog 在FATAL时调用DumpStackTrace()其 Windows 实现依赖dbghelp.dll的SymInitializeStackWalk64。但若你的.exe或.dll没有对应 PDB或 PDB 路径未正确注册堆栈将显示为 0x00007ff8a1b2c3a2 (unknown) 0x00007ff8a1b2c4b1 (unknown)解决方案分两步编译时生成 PDBMSVC 默认开启/Zi确保 CMake 中CMAKE_CXX_FLAGS包含/Zi运行时注册 PDB 路径在InitGoogleLogging后立即调用#include dbghelp.h #pragma comment(lib, dbghelp.lib) void InitSymbolPath() { HANDLE hProcess GetCurrentProcess(); SymInitialize(hProcess, nullptr, TRUE); // 使用系统符号服务器可选 // 或指定本地 PDB 目录 std::string pdb_path GetModuleDirectory() \\pdb\\; SymSetSearchPath(hProcess, pdb_path.c_str()); }GetModuleDirectory()需自行实现调用GetModuleFileNamePathRemoveFileSpecSymSetSearchPath必须在google::InitGoogleLogging之后、首次LOG(FATAL)之前调用否则无效3. 静态链接 vs 动态链接Windows 下 glog 的 ABI 边界与内存管理陷阱在 Windows 上glog 的链接方式不是性能选择题而是 ABI 生死线。错误的链接方式会导致std::string跨 DLL 边界析构崩溃、malloc/free混用、甚至LOG宏内部new出来的临时对象在错误堆上释放。3.1 静态链接唯一推荐给生产环境的方案glog 官方 CMakeLists.txt 默认构建动态库.dll但在 Windows 下这会引入严重风险glog 内部使用std::string存储日志内容若你的主程序和 glog.dll 使用不同版本的 MSVCRT如主程序/MDglog.dll/MTstd::string的 vtable 和内存布局不兼容LOG(INFO) abc可能触发访问冲突google::SetLogDestination注册的文件路径字符串由 glog 分配若主程序尝试delete[]该指针极罕见但可能会 crash静态链接强制统一运行时主程序和 glog 共享同一份malloc/new实现所有std::string对象生命周期在同一线程、同一堆中管理# CMakeLists.txt 片段强制静态构建 glog set(BUILD_SHARED_LIBS OFF CACHE BOOL Build shared libraries) # 并确保你的主项目也使用相同运行时 set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$$CONFIG:Debug:Debug) # 即 /MT 和 /MTd而非 /MD /MDd注意若主程序必须用/MD依赖其他 DLL则 glog必须也用/MD且需确保所有依赖项如 gflags均用/MD构建否则std::string仍会越界。此时静态链接 glog 是唯一安全解法。3.2 动态链接的补救手动导出关键符号绕过 STL 边界若因历史原因必须用 DLL需修改 glog 源码将LOG宏内部所有std::string操作封装为 C 风格接口避免跨边界传递 STL 对象// glog_export.h #ifdef GLOG_DLL_EXPORTS #define GLOG_API __declspec(dllexport) #else #define GLOG_API __declspec(dllimport) #endif extern C { // 导出纯 C 接口输入 const char* GLOG_API void glog_log_info(const char* msg); GLOG_API void glog_log_fatal(const char* msg); }并在logging.cc中实现void glog_log_info(const char* msg) { // 不 new std::string直接用 LOG(INFO) msgmsg 为 C 字符串 LOG(INFO) msg; }主程序调用glog_log_info(hello)完全规避std::string构造/析构该方案牺牲了流式日志 a b但换来稳定性3.3 内存泄漏检测glog 自身不泄漏但你的用法可能触发glog 在 Windows 下有个隐藏陷阱google::ShutdownGoogleLogging()不会释放所有内部缓冲区。若你在程序退出前调用它随后又调用LOG(INFO)会触发_CRT_DEBUGGER_HOOK断言失败因为 glog 内部log_mutex_已销毁。血泪经验不要在main()结尾调用ShutdownGoogleLogging()。Windows 程序退出时CRT 会自动清理全局对象glog 的静态析构函数已注册好清理逻辑。强行调用只会制造竞争条件。验证方法用 Application Verifier 开启Heaps检查运行后查看avrflog.txt是否报告glog相关堆块未释放 —— 若无报告说明 glog 自身干净若有则是你提前 shutdown 导致的。4. 避坑Windows 下 glog 的五个高频翻车现场与根因定位这些不是“可能出错”而是我在三个不同项目中至少复现三次以上的确定性问题。每一条都附带最小复现步骤、根本原因和一行修复命令。4.1 现象LOG(WARNING) test无输出但LOG(INFO)正常原因Windows 控制台默认不启用WARNING级别日志glog 的minloglevel默认为0INFO但控制台输出受SetConsoleTextAttribute影响WARNING文本色与背景色相同导致“看不见”解决显式设置最低日志级别google::SetMinLogLevel(google::GLOG_WARNING); // 强制输出 WARNING 及以上4.2 现象CHECK_EQ(x, y)失败时只打印Check failed: x y无值展开如x5, y3原因glog 的CHECK宏依赖__FUNCSIG__展开函数签名但 MSVC 在/O2优化下可能内联函数导致__FUNCSIG__为空同时CHECK的值打印逻辑在 Windows 下需snprintf_s支持若编译时未定义_CRT_SECURE_NO_WARNINGSsnprintf被替换为安全版本但参数不匹配解决在CMakeLists.txt中添加add_definitions(-D_CRT_SECURE_NO_WARNINGS) # 并确保 CHECK 宏所在函数未被内联加 __declspec(noinline)4.3 现象日志文件名含中文路径如D:\日志\app.log时SetLogDestination创建失败返回ERROR_INVALID_NAME原因glog 源码中mkdir()调用使用 ANSI 版本CreateDirectoryA无法处理 UTF-8 路径而 Windows API 要求宽字符CreateDirectoryW解决修改src/windows/port.cc中mkdir实现替换为int mkdir(const char* path, int mode) { std::wstring wpath utf8_to_wstring(path); // 自行实现 UTF-8 → UTF-16 转换 return CreateDirectoryW(wpath.c_str(), nullptr) ? 0 : -1; }4.4 现象多线程环境下LOG(INFO) thread std::this_thread::get_id()输出的线程 ID 全是0x00000000原因glog 的GetTID()在 Windows 下调用_getpid()而非GetCurrentThreadId()前者返回进程 ID且std::this_thread::get_id()返回的是std::thread::id对象非整数解决重写GetTID()并在logging.cc中替换// 替换 src/windows/port.cc 中的 GetTID() inline pid_t GetTID() { return static_castpid_t(GetCurrentThreadId()); }4.5 现象启用--logtostderr1后日志中时间戳格式为0512 14:23:11.123456但小时始终为1424 小时制无法切换为 12 小时制 AM/PM原因glog 的FormatTime函数硬编码使用strftime(%m%d %H:%M:%S, ...)%H固定为 24 小时Windows 下无strftime的 locale 切换开关解决不改 glog 源码改用SetLogPrefix注入自定义时间#include iomanip #include sstream std::string GetCustomTime() { auto now std::chrono::system_clock::now(); auto time_t std::chrono::system_clock::to_time_t(now); auto ms std::chrono::duration_caststd::chrono::milliseconds( now.time_since_epoch()) % 1000; std::stringstream ss; ss std::put_time(std::localtime(time_t), %m%d %I:%M:%S) . std::setfill(0) std::setw(3) ms.count(); return ss.str(); } google::SetLogPrefix([]() { return GetCustomTime() ; });5. 进阶技巧用 glog 实现 Windows 服务的结构化日志与崩溃自检闭环真正的工程价值不在“能打日志”而在“日志能驱动运维”。在 Windows 服务场景下glog 可以成为轻量级可观测性基座——无需 ELK仅靠本地文件 Windows 事件查看器 PowerShell就能构建从日志采集、异常告警到崩溃自恢复的闭环。5.1 结构化日志用LOG(INFO) 拼 JSON 字符串规避序列化库依赖glog 本身不支持结构化如 key-value但 Windows 服务通常只需有限字段timestamp,level,thread_id,module,message,error_code。我们用std::ostringstream手动拼接确保零依赖、零分配#include sstream #include iomanip void StructuredLog(google::LogSeverity severity, const char* module, DWORD error_code 0, const char* fmt, ...) { std::ostringstream oss; auto now std::chrono::system_clock::now(); auto time_t std::chrono::system_clock::to_time_t(now); auto ms std::chrono::duration_caststd::chrono::milliseconds( now.time_since_epoch()) % 1000; oss {; oss \timestamp\:\ std::put_time(std::localtime(time_t), %Y-%m-%dT%H:%M:%S) . std::setfill(0) std::setw(3) ms.count() Z\,; oss \level\:\ (severity google::GLOG_INFO ? INFO : severity google::GLOG_WARNING ? WARN : severity google::GLOG_ERROR ? ERROR : FATAL) \,; oss \thread_id\: GetCurrentThreadId() ,; oss \module\:\ module \,; if (error_code) oss \error_code\: error_code ,; va_list args; va_start(args, fmt); char buf[1024]; vsnprintf_s(buf, _countof(buf), _TRUNCATE, fmt, args); va_end(args); oss \message\:\ buf \; oss }; switch (severity) { case google::GLOG_INFO: LOG(INFO) oss.str().c_str(); break; case google::GLOG_WARNING: LOG(WARNING) oss.str().c_str(); break; case google::GLOG_ERROR: LOG(ERROR) oss.str().c_str(); break; default: LOG(FATAL) oss.str().c_str(); } } // 使用示例 StructuredLog(google::GLOG_INFO, network, 0, Connected to %s:%d, host.c_str(), port); StructuredLog(google::GLOG_ERROR, database, GetLastError(), Query failed);输出效果{timestamp:2024-05-12T14:23:11.123Z,level:ERROR,thread_id:1234,module:database,error_code:5,message:Query failed}优势纯 C11无第三方 JSON 库vsnprintf_s保证 Windows 安全字符串长度可控避免LOG内部缓冲区溢出5.2 崩溃自检LOG(FATAL)后自动上传 minidump 到内网 FTP附带最近 100 行日志glog 的FatalHandler可被替换。我们不修改 glog 源码而用google::InstallFailureSignalHandler()注册自定义信号处理器在SIGABRT/SIGSEGV时执行#include wininet.h #pragma comment(lib, wininet.lib) void UploadMinidumpAndLogs() { // 1. 生成 minidump使用 MiniDumpWriteDump HANDLE hFile CreateFile(Lcrash.dmp, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); MINIDUMP_EXCEPTION_INFORMATION mdei; mdei.ThreadId GetCurrentThreadId(); mdei.ExceptionPointers nullptr; // 由系统填充 mdei.ClientPointers FALSE; MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, mdei, nullptr, nullptr); CloseHandle(hFile); // 2. 读取最近 100 行日志假设日志文件为 logs/app.INFO std::ifstream log_file(logs/app.INFO); std::vectorstd::string last_lines; std::string line; while (std::getline(log_file, line)) { last_lines.push_back(line); if (last_lines.size() 100) last_lines.erase(last_lines.begin()); } // 3. 上传到 FTP简化版实际需处理认证、超时 HINTERNET hInternet InternetOpen(Lglog-uploader, INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0); HINTERNET hFtp InternetConnect(hInternet, Lftp.internal, INTERNET_DEFAULT_FTP_PORT, Luser, Lpass, INTERNET_SERVICE_FTP, 0, 0); FtpPutFile(hFtp, Lcrash.dmp, Lcrash_20240512_142311.dmp, FTP_TRANSFER_TYPE_BINARY, 0); // 4. 上传日志片段拼成 .txt std::ofstream dump_txt(crash_context.txt); for (const auto l : last_lines) dump_txt l \n; dump_txt.close(); FtpPutFile(hFtp, Lcrash_context.txt, Lcrash_20240512_142311.txt, FTP_TRANSFER_TYPE_ASCII, 0); InternetCloseHandle(hFtp); InternetCloseHandle(hInternet); } // 在 main() 中注册 google::InstallFailureSignalHandler(); // 并在 Init 后设置自定义 fatal handler google::SetFatalMessageCallback([](const char* data, int size) { UploadMinidumpAndLogs(); });关键点InstallFailureSignalHandler()必须在InitGoogleLogging之后调用否则 glog 的默认 handler 会先执行并 exitFtpPutFile是阻塞调用生产环境应起独立线程上传避免阻塞崩溃处理流程5.3 日志轮转用 Windows 任务计划程序 PowerShell 实现零侵入日志切割glog 自带MaxLogSize但 Windows 下rename()在文件被占用时失败率高。更稳的方案是让 glog 永远写同一个文件外部工具定时切割glog 配置为单文件输出google::SetLogDestination(google::GLOG_INFO, logs/current.); // 生成 logs/current.INFO创建 PowerShell 脚本rotate_logs.ps1$date Get-Date -Format yyyyMMdd_HHmmss $src logs\current.INFO $dst logs\archive\INFO_$date.log if (Test-Path $src) { Rename-Item $src $dst -ErrorAction SilentlyContinue # 强制 glog 重新打开文件发送 SIGUSR1 信号Windows 用 DebugBreak 模拟 $proc Get-Process -Name myapp -ErrorAction SilentlyContinue if ($proc) { $proc.Handle | Out-Null # 触发 glog 的文件重开逻辑 } }用schtasks每小时执行一次schtasks /create /tn GlogRotate /tr powershell -ExecutionPolicy Bypass -File rotate_logs.ps1 /sc HOURLY /mo 1优势完全解耦glog 无感知避免fopen/fclose竞争PowerShell 可轻松添加压缩、清理旧日志等逻辑从那以后我每次在 Windows 上启动新 C 项目都会先跑一遍glog的 SEH 符号测试写个CHECK(false)看堆栈是否完整、验证中文路径创建、再压测 10 个线程狂打LOG(INFO)一小时——不是为了炫技而是因为 Windows 下日志链路太容易在某个不起眼的角落静默断裂。这份资源包的价值不在于它“包含什么”而在于它帮你把那些 Windows 特有的、文档里绝不会写的、只有踩过才信的断点都预先标好了。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。