1. 项目概述
在C++开发领域,日志记录是每个项目都绕不开的基础功能。spdlog作为当前最受欢迎的C++日志库之一,以其高性能、易用性和丰富的功能特性赢得了广大开发者的青睐。根据2023年Stack Overflow开发者调查,spdlog在C++日志库中的使用率已经达到42%,远超其他同类产品。
这个教程将带你从零开始,在Windows平台上完整构建spdlog日志库。不同于简单的使用示例,我们会深入编译过程的每个细节,包括:
- 如何选择最适合的编译工具链
- 如何处理Windows平台特有的依赖关系
- 如何优化编译参数以获得最佳性能
- 如何集成到实际项目中
提示:虽然spdlog官方推荐使用vcpkg进行安装,但在生产环境中,掌握从源码编译的能力能让你更好地应对各种定制化需求。
2. 环境准备
2.1 工具链选择与安装
在Windows平台编译C++项目,我们有以下几种主流选择:
Visual Studio 2022(推荐):
- 社区版免费使用
- 集成MSVC编译器和完善的调试工具
- 最新版本对C++20/23标准支持更好
MinGW-w64:
- 提供GCC的Windows移植版本
- 适合需要跨平台一致性的项目
- 最新版本已支持C++20大部分特性
Clang for Windows:
- 结合了LLVM的前沿优化技术
- 错误提示更友好
- 需要额外配置标准库
对于本教程,我们选择Visual Studio 2022作为主要开发环境。安装时务必勾选:
- "使用C++的桌面开发"工作负载
- Windows 10/11 SDK(最新版本)
- C++ CMake工具
2.2 源码获取
spdlog的最新稳定版本可以通过以下方式获取:
git clone --recursive https://github.com/gabime/spdlog.git cd spdlog git checkout v1.x # 使用稳定分支注意:一定要使用
--recursive参数,因为spdlog依赖fmt库作为子模块。如果忘记添加,需要手动执行:git submodule update --init
3. 编译配置详解
3.1 CMake配置选项
spdlog提供了丰富的CMake配置选项,以下是最关键的几个:
| 选项名称 | 默认值 | 推荐值 | 说明 |
|---|---|---|---|
| SPDLOG_BUILD_EXAMPLE | OFF | OFF | 是否编译示例程序 |
| SPDLOG_BUILD_TESTS | OFF | OFF | 是否编译测试用例 |
| SPDLOG_BUILD_SHARED | OFF | ON | 编译为动态库 |
| SPDLOG_WCHAR_SUPPORT | OFF | 按需 | 是否支持宽字符 |
| SPDLOG_FMT_EXTERNAL | OFF | ON | 使用外部fmt库 |
建议的配置命令:
cmake -B build -DSPDLOG_BUILD_SHARED=ON -DSPDLOG_FMT_EXTERNAL=ON -DCMAKE_BUILD_TYPE=Release3.2 Windows平台特有配置
在Windows上编译时,有几个需要特别注意的地方:
字符集问题:
- 如果项目使用Unicode字符集,需要设置
SPDLOG_WCHAR_SUPPORT=ON - 否则可能遇到中文日志乱码问题
- 如果项目使用Unicode字符集,需要设置
运行时库兼容性:
- 确保spdlog和主项目使用相同的运行时库(MT/MD)
- 可通过CMAKE_MSVC_RUNTIME_LIBRARY指定
导出符号:
- 动态库需要正确定义导出宏
- spdlog已经处理好,但自定义logger时需要注意
4. 完整编译流程
4.1 使用Visual Studio编译
生成解决方案文件:
cmake -B build -G "Visual Studio 17 2022" -A x64打开生成的spdlog.sln,选择Release配置
编译ALL_BUILD目标
可选编译INSTALL目标,将库安装到系统目录
4.2 使用命令行编译
对于自动化构建场景,可以使用以下命令:
cmake -B build -G "Visual Studio 17 2022" -A x64 cmake --build build --config Release --target ALL_BUILD编译完成后,关键文件位于:
- 静态库:build/Release/spdlog.lib
- 动态库:build/Release/spdlog.dll
- 头文件:build/include/
5. 项目集成实战
5.1 CMake项目集成
推荐使用find_package方式集成:
find_package(spdlog REQUIRED CONFIG) target_link_libraries(your_target PRIVATE spdlog::spdlog)如果使用自定义编译的spdlog,可以指定路径:
set(spdlog_DIR "/path/to/spdlog/install/lib/cmake/spdlog") find_package(spdlog REQUIRED)5.2 直接集成
对于简单的项目,可以直接包含头文件:
#include "spdlog/spdlog.h" #include "spdlog/sinks/basic_file_sink.h" int main() { auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt"); logger->info("Hello, {}!", "spdlog"); }需要确保:
- 添加头文件搜索路径
- 链接spdlog.lib
- 运行时能找到spdlog.dll(如果是动态库)
6. 性能优化技巧
6.1 编译期优化
预编译头文件: 创建stdafx.h包含常用spdlog头文件,可显著减少编译时间
链接时优化: 在CMake中设置:
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)SIMD指令优化: 启用AVX2指令集:
target_compile_options(spdlog PRIVATE /arch:AVX2)
6.2 运行时优化
异步日志:
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>( "async_file_logger", "logs/async.txt");批量刷新:
logger->flush_on(spdlog::level::warn); // 只在警告及以上级别刷新 spdlog::flush_every(std::chrono::seconds(5)); // 定时刷新格式化优化: 使用spdlog::fmt_lib而非标准库格式化,性能提升可达3-5倍
7. 常见问题排查
7.1 编译问题
问题1:找不到fmt库
- 解决方案:
git submodule update --init 或 set(SPDLOG_FMT_EXTERNAL ON) # 使用系统安装的fmt
问题2:LNK2005符号重复
- 原因:多个模块静态链接spdlog
- 解决:
target_link_libraries(your_target PRIVATE spdlog::spdlog_header_only)
7.2 运行时问题
问题1:日志文件无写入权限
- 解决方案:
try { auto logger = spdlog::basic_logger_mt("logger", "path/to/log"); } catch (const spdlog::spdlog_ex& ex) { // 处理异常 }
问题2:性能瓶颈
- 检查点:
- 是否使用了异步日志
- 是否过于频繁刷新
- 是否在热路径中进行了不必要的格式化
8. 高级应用场景
8.1 自定义格式器
auto custom_logger = std::make_shared<spdlog::logger>("custom", sink); custom_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v");支持的特殊标记:
- %Y:4位年份
- %m:月份
- %e:毫秒
- %^%l%$:带颜色的日志级别
- %t:线程ID
8.2 多接收器组合
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("log.txt"); spdlog::logger logger("multi_sink", {console_sink, file_sink}); logger.set_level(spdlog::level::debug);8.3 日志文件轮转
#include "spdlog/sinks/rotating_file_sink.h" // 每个文件最大5MB,保留3个副本 auto rotating_logger = spdlog::rotating_logger_mt("rotating", "logs/rotating.txt", 1048576 * 5, 3);9. 实际项目中的最佳实践
日志级别管理:
- Debug:开发调试用
- Info:关键业务流程节点
- Warn:可恢复的异常情况
- Error:需要干预的错误
- Critical:系统级严重错误
上下文信息:
logger->info("User {} logged in from {}", user_id, ip_address);结构化日志:
logger->info(R"({"event": "login", "user": "{}", "ip": "{}"})", user, ip);性能敏感场景:
SPDLOG_LOGGER_TRACE(logger, "High freq message: {}", data); // 编译时过滤
10. 编译后的验证测试
10.1 基础功能测试
创建测试程序:
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #include "spdlog/spdlog.h" int main() { spdlog::set_level(spdlog::level::debug); SPDLOG_INFO("Welcome to spdlog!"); SPDLOG_DEBUG("This is debug message"); spdlog::drop_all(); // 确保所有日志刷新 }检查:
- 控制台输出是否正常
- 日志文件是否生成
- 日志级别过滤是否生效
10.2 性能基准测试
使用spdlog自带的benchmark:
cmake -B build -DSPDLOG_BUILD_BENCH=ON cmake --build build --config Release ./build/bench/spdlog_bench典型结果参考:
- 同步模式:约2-3百万条/秒
- 异步模式:约6-8百万条/秒
11. 跨平台兼容性处理
虽然本教程聚焦Windows平台,但spdlog本身是跨平台的。如果需要考虑其他平台:
Linux/macOS差异:
- 文件路径使用正斜杠
- 不需要处理DLL导出
- 可以使用syslog接收器
编译器兼容性:
- GCC/Clang需要-std=c++11或更高
- 注意不同平台下的字节对齐
统一构建脚本:
if(WIN32) target_compile_definitions(spdlog PRIVATE SPDLOG_WCHAR_FILENAMES) endif()
12. 版本升级与维护
12.1 版本升级步骤
- 检查变更日志:https://github.com/gabime/spdlog/releases
- 备份现有配置
- 更新源码:
git fetch git checkout v1.12.0 # 示例版本 git submodule update - 重新编译测试
12.2 长期维护建议
版本锁定: 在CMake中固定版本:
include(FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.12.0 )补丁管理: 对于关键安全更新,建议创建分支应用补丁
性能监控: 定期运行benchmark比较性能变化
13. 替代方案比较
虽然spdlog是推荐选择,但了解替代方案也很重要:
| 特性 | spdlog | glog | Boost.Log | easylogging++ |
|---|---|---|---|---|
| 头文件库 | 是 | 否 | 否 | 是 |
| 异步支持 | 优秀 | 一般 | 需要配置 | 有限 |
| 性能 | 极高 | 高 | 中等 | 高 |
| 依赖 | fmt | 无 | Boost | 无 |
| C++20支持 | 完全 | 部分 | 部分 | 有限 |
选择建议:
- 需要极致性能:spdlog
- 已有Boost生态:Boost.Log
- Google技术栈:glog
- 最小依赖:easylogging++
14. 生产环境部署建议
日志策略:
- 开发环境:Debug级别,控制台输出
- 测试环境:Info级别,文件输出
- 生产环境:Warn级别,异步文件+监控系统
日志清理:
- 设置合理的文件大小和数量限制
- 考虑使用日志压缩归档
- 实现自动化清理脚本
监控集成:
auto stats_sink = std::make_shared<PrometheusSink>(); logger->sinks().push_back(stats_sink);异常处理:
spdlog::set_error_handler([](const std::string& msg) { // 发送警报 });
15. 调试技巧与工具
15.1 日志调试
即时级别调整:
// 运行时动态调整级别 spdlog::set_level(spdlog::level::debug);跟踪特定logger:
auto logger = spdlog::get("important"); if(logger) logger->set_level(spdlog::level::trace);
15.2 性能分析
使用ETW跟踪(Windows):
#include "spdlog/sinks/etw_sink.h" auto etw_logger = std::make_shared<spdlog::logger>("etw", std::make_shared<spdlog::sinks::etw_sink_mt>());使用WPR/WPA分析:
- 记录日志事件
- 在Windows Performance Analyzer中查看
15.3 内存检查
重载内存分配:
spdlog::set_memory_allocator(my_allocator);检测内存泄漏:
- 在调试模式下使用Visual Studio诊断工具
- 确保所有logger正确释放
16. 扩展开发指南
16.1 自定义接收器
实现一个简单的UDP接收器示例:
class udp_sink : public spdlog::sinks::base_sink<std::mutex> { protected: void sink_it_(const spdlog::details::log_msg& msg) override { // 格式化消息 spdlog::memory_buf_t formatted; formatter_->format(msg, formatted); // 通过UDP发送 udp_send(formatted.data(), formatted.size()); } void flush_() override {} };16.2 自定义格式化
实现JSON格式输出:
class json_formatter : public spdlog::formatter { public: void format(const spdlog::details::log_msg& msg, spdlog::memory_buf_t& dest) override { fmt::format_to(dest, R"({{"time":"{}","level":"{}","message":"{}"}})", msg.time, msg.level, msg.payload); } }; logger->set_formatter(std::make_unique<json_formatter>());16.3 插件系统集成
将spdlog集成到插件系统:
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> logger) { // 重写默认logger设置 } // 插件中调用 extern "C" void init_plugin() { auto plugin_logger = spdlog::basic_logger_mt("plugin", "plugin.log"); set_default_logger(plugin_logger); }17. 现代C++特性应用
17.1 C++20协程支持
#include <coroutine> spdlog::info("Starting coroutine"); auto result = co_await async_operation(); spdlog::info("Coroutine completed with {}", result);17.2 概念约束
自定义格式化类型检查:
template<typename T> concept Loggable = requires(T t, spdlog::memory_buf_t& buf) { { spdlog::detail::format_value(t, buf) } -> std::same_as<void>; }; void log_value(const Loggable auto& value) { spdlog::info("Value: {}", value); }17.3 模块化支持
创建spdlog模块:
// spdlog.ixx export module spdlog; export { #include "spdlog/spdlog.h" #include "spdlog/sinks/basic_file_sink.h" }18. 安全注意事项
敏感信息过滤:
logger->set_formatter([](const spdlog::details::log_msg& msg) { // 过滤密码等敏感信息 auto payload = filter_sensitive_data(msg.payload); return fmt::format("[{}] {}", msg.level, payload); });文件权限控制:
- 在Windows上设置合适的ACL
- 避免使用全局可写目录
日志注入防护:
logger->info("User input: {}", sanitize(user_input));内存安全:
- 避免在日志回调中分配大量内存
- 设置合理的队列大小限制
19. 性能调优实战
19.1 基准测试对比
测试场景:单线程写入1百万条日志
| 配置 | 耗时(ms) | 内存占用(MB) |
|---|---|---|
| 同步控制台 | 2450 | 15 |
| 异步控制台 | 580 | 32 |
| 同步文件 | 1850 | 12 |
| 异步文件 | 420 | 28 |
| 异步+批量 | 380 | 26 |
19.2 关键优化参数
异步队列大小:
spdlog::init_thread_pool(8192, 1); // 队列大小,线程数批量写入:
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("log.txt"); sink->set_batch_size(50); // 每50条刷新一次内存池:
spdlog::set_memory_allocator(my_pool_allocator);
20. 持续集成集成
20.1 GitHub Actions配置
name: CI on: [push, pull_request] jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v3 - run: git submodule update --init - name: Configure run: cmake -B build -DSPDLOG_BUILD_TESTS=ON - name: Build run: cmake --build build --config Release - name: Test working-directory: build run: ctest -C Release20.2 静态分析集成
在CMake中启用:
# clang-tidy set(CMAKE_CXX_CLANG_TIDY clang-tidy;-checks=*) # 或MSVC分析 if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /analyze") endif()21. 多线程最佳实践
21.1 线程安全配置
logger实例管理:
// 正确做法 auto logger = spdlog::get("global"); if (!logger) { std::lock_guard<std::mutex> lock(logger_mutex); logger = spdlog::basic_logger_mt("global", "global.log"); }避免静态初始化顺序问题:
spdlog::logger& get_logger() { static auto logger = []() { auto l = spdlog::basic_logger_mt("static", "static.log"); spdlog::set_default_logger(l); return l; }(); return *logger; }
21.2 高性能多线程日志
线程专用logger:
thread_local auto t_logger = []() { return spdlog::basic_logger_mt(fmt::format("thread-{}", std::this_thread::get_id()), "thread.log"); }();无锁队列优化:
spdlog::init_thread_pool(1024, 1, std::make_shared<spdlog::details::mpmc_blocking_queue<spdlog::details::async_msg>>(1024));
22. 资源管理与清理
22.1 正确释放logger
显式释放:
spdlog::drop("logger_name"); // 释放特定logger spdlog::shutdown(); // 释放所有资源RAII包装:
class ScopedLogger { public: ScopedLogger(std::string name) { logger_ = spdlog::basic_logger_mt(name, "logs/" + name + ".log"); } ~ScopedLogger() { spdlog::drop(logger_->name()); } private: std::shared_ptr<spdlog::logger> logger_; };
22.2 文件句柄管理
定期检查:
auto file_sink = dynamic_cast<spdlog::sinks::basic_file_sink_mt*>(logger->sinks()[0].get()); if (file_sink && !file_sink->is_file_open()) { logger->warn("Log file was closed, reopening..."); file_sink->reopen(); }异常处理:
try { logger->info("Writing to log"); } catch (const spdlog::spdlog_ex& ex) { // 处理写入失败 }
23. 实际案例分析
23.1 高并发服务日志方案
需求特点:
- 每秒上万条日志
- 不能影响主业务性能
- 需要按服务实例分离日志
解决方案:
struct ServiceLogger { static std::shared_ptr<spdlog::logger> get(std::string service_id) { auto logger = spdlog::get(service_id); if (!logger) { std::lock_guard<std::mutex> lock(creation_mutex); logger = spdlog::basic_logger_mt<spdlog::async_factory>( service_id, fmt::format("logs/{}.log", service_id)); logger->set_level(spdlog::level::info); } return logger; } };23.2 嵌入式环境适配
限制条件:
- 有限的内存(<1MB RAM)
- 无文件系统
- 实时性要求高
优化措施:
- 禁用所有格式化:
#define SPDLOG_NO_FMT - 使用简单接收器:
class SerialSink : public spdlog::sinks::base_sink<std::mutex> { void sink_it_(const spdlog::details::log_msg& msg) override { serial_write(msg.payload.data(), msg.payload.size()); } void flush_() override {} }; - 静态分配内存:
spdlog::set_memory_allocator(static_allocator);
24. 未来兼容性设计
24.1 ABI稳定性
接口设计原则:
- 使用PIMPL模式隐藏实现细节
- 避免直接暴露内部数据结构
- 提供稳定的C接口
版本控制:
#define SPDLOG_ABI_VERSION 2 static_assert(SPDLOG_ABI_VERSION == SPDLOG_HEADER_ONLY_ABI_VERSION, "ABI version mismatch");
24.2 向后兼容策略
弃用警告:
[[deprecated("Use spdlog::info() instead")]] void legacy_log_function();适配层:
namespace compat { template<typename... Args> void log(Args&&... args) { if constexpr (SPDLOG_VERSION > 10900) { spdlog::info(std::forward<Args>(args)...); } else { legacy_log(std::forward<Args>(args)...); } } }
25. 社区资源与支持
25.1 官方资源
GitHub仓库:
- 主仓库:https://github.com/gabime/spdlog
- 文档:https://spdlog.docsforge.com/
问题追踪:
- 使用GitHub Issues报告问题
- 提问前先搜索已知问题
25.2 第三方扩展
常用插件:
- spdlog_setup:基于配置文件的初始化
- spdlog-sinks-elasticsearch:Elasticsearch接收器
- spdlog-sinks-mongodb:MongoDB接收器
可视化工具:
- LogViewer:https://github.com/zarunbal/LogViewer
- LogFusion:专业的日志分析工具
25.3 获取帮助
Stack Overflow:
- 使用[spdlog]标签提问
- 提供最小可复现示例
社区论坛:
- C++论坛的spdlog专题
- Reddit的r/cpp社区
商业支持:
- 部分公司提供专业支持服务
- 考虑赞助开源维护者
26. 编译优化进阶
26.1 基于PGO的优化
收集性能数据:
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSPDLOG_BUILD_BENCH=ON cmake --build build --config Release ./build/bench/spdlog_bench --benchmark_samples=1000使用数据重新编译:
cmake -B build_pgo -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="/GL /LTCG /USEPROFILE" cmake --build build_pgo --config Release
26.2 模板实例化控制
减少编译膨胀:
// 显式实例化常用类型 template class spdlog::logger<spdlog::async_factory>; template void spdlog::logger<spdlog::async_factory>::info<int>(const char*, int);26.3 编译期过滤
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO // 以下日志在Release中不会生成代码 SPDLOG_DEBUG("Debug message: {}", expensive_call());27. 工具链集成
27.1 与Visual Studio深度集成
输出窗口接收器:
#include "spdlog/sinks/msvc_sink.h" auto msvc_logger = spdlog::create<spdlog::sinks::msvc_sink_mt>("msvc");调试可视化:
// 在natvis文件中添加spdlog类型可视化性能分析标记:
#include <windows.h> void log_with_marker(std::string_view message) { OutputDebugStringA(message.data()); SPDLOG_INFO(message); }
27.2 与CMake深度集成
导出目标:
install(TARGETS spdlog EXPORT spdlogTargets ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)配置文件生成:
include(CMakePackageConfigHelpers) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/spdlogConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake" INSTALL_DESTINATION lib/cmake/spdlog)
28. 质量保证体系
28.1 单元测试策略
核心功能测试:
- 格式化测试
- 级别过滤测试
- 多线程安全测试
性能回归测试:
BENCHMARK("async_log", [](benchmark::State& state) { auto logger = setup_async_logger(); for (auto _ : state) { logger->info("Benchmark message"); } });
28.2 静态分析
clang-tidy检查:
run-clang-tidy -checks='modernize-*,performance-*' -fixCoverity扫描:
- 配置coverity工具链
- 定期运行静态分析
28.3 动态分析
ASAN检测:
cmake -B build_asan -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fsanitize=address"UBSAN检测:
-fsanitize=undefined
29. 架构设计思想
29.1 核心设计原则
单一职责:
- 格式化与输出分离
- 同步与异步解耦
开闭原则:
- 通过继承扩展接收器
- 不修改核心代码添加功能
零成本抽象:
- 头文件only无运行时开销
- 编译期优化
29.2 关键数据结构
日志消息结构:
struct log_msg { log_clock::time_point time; level::level_enum level; source_loc source; string_view_t logger_name; string_view_t payload; };异步队列:
using async_msg_queue = moodycamel::ConcurrentQueue<async_msg>;线程池:
class thread_pool { std::vector<std::thread> threads_; std::function<void()> worker_func_; };
30. 从源码学习
30.1 值得研究的核心实现
格式化引擎:
- fmt库的深度集成
- 编译期格式字符串检查
异步机制:
- 无锁队列的应用
- 批量写入优化
注册表管理:
- logger的全局管理
- 线程安全实现
30.2 代码阅读建议
阅读顺序:
- logger.hpp - 核心接口
- async.h - 异步实现
- sinks/ - 各种接收器实现
调试技巧:
#define SPDLOG_DEBUG_ON // 可以输出内部调试信息定制开发:
- 从简单的接收器开始
- 逐步深入核心机制
31. 性能关键路径
31.1 热点分析
格式化阶段:
- 避免复杂格式字符串
- 使用编译期格式化
队列竞争:
- 调整队列大小
- 考虑分区
IO瓶颈:
- 使用更快的存储
- 增加缓冲区
31.2 优化实例
原始代码:
logger->info("Value: {}, Time: {}", compute_value(), get_time());优化后:
if (logger->should_log(spdlog::level::info)) { logger->info("Value: {}, Time: {}", compute_value(), get_time()); }进一步优化:
SPDLOG_LOGGER_INFO(logger, "Value: {}, Time: {}", compute_value(), get_time());32. 内存管理策略
32.1 高效内存使用
内存池:
class pool_allocator { static constexpr size_t chunk_size = 4096; std::vector<std::unique_ptr<char[]>> chunks_; };预分配:
spdlog::details::log_msg_buffer::log_msg_buffer(size_t prealloc) : buffer_(prealloc) {}SSO优化:
- 小字符串直接存储在对象中
- 避免频繁堆分配
32.2 内存分析工具
Visual Studio诊断工具:
- 内存使用率
- 分配热点
Valgrind(memcheck):
- Linux平台内存分析
- 检测泄漏和越界
自定义追踪:
void* operator new(size_t size) { log_allocation(size); return malloc(size); }
33. 异常处理体系
33.1 错误分类
初始化错误:
- 文件创建失败
- 权限不足
运行时错误:
- 磁盘满
- 网络中断
逻辑错误:
- 无效格式字符串
- 级别配置错误
33.2 恢复策略
- 文件日志恢复:
try { logger->info("message"); } catch (const spdlog::spdlog