文章目录
- 一、下载 spdlog
- 方法1:使用包管理器(推荐)
- 方法2:手动下载
- 二、编译 spdlog
- 使用 CMake 编译
- 编译选项
- 三、在项目中使用
- CMake 项目集成
- 简单使用示例
- 四、编译示例程序
- 五、依赖要求
- 注意事项
- 一、环境准备
- 1. 安装编译工具
- 2. 获取 spdlog 源代码
- 二、使用 CMake 生成不同配置
- 方法1:使用批处理脚本(推荐)
- 方法2:手动命令编译
- 三、完整的编译脚本
- 四、单独编译各版本命令
- 编译 x64 Debug
- 编译 x64 Release
- 编译 Win32 Debug
- 编译 Win32 Release
- 五、打包为可分发库
- 六、使用注意事项
- 1. 编译器版本对应关系
- 2. 文件命名规则
- 3. 在项目中使用
- 使用例子
- 完整的 spdlog 使用示例
- 项目结构
- 1. CMakeLists.txt
- 2. logger_config.h - 日志配置类
- 3. logger_config.cpp
- 4. main.cpp - 主程序示例
- 5. advanced_logging.cpp - 高级功能演示
- 6. 编译和运行
- Windows (MSVC):
- Linux/Mac:
- 7. 示例输出
- 8. 主要特点
一、下载 spdlog
方法1:使用包管理器(推荐)
vcpkg:
vcpkginstallspdlogConan:
conaninstallspdlog/1.x.x@APT (Ubuntu/Debian):
sudoapt-getinstalllibspdlog-dev方法2:手动下载
Git Clone:
gitclone https://github.com/gabime/spdlog.gitcdspdlog# 切换到稳定版本gitcheckout v1.x.x直接下载 ZIP:
访问 https://github.com/gabime/spdlog 下载最新版本
二、编译 spdlog
spdlog 是 header-only 库,但如果你需要编译为动态/静态库:
使用 CMake 编译
# 克隆仓库gitclone https://github.com/gabime/spdlog.gitcdspdlog# 创建构建目录mkdirbuild&&cdbuild# 配置 CMakecmake..-DCMAKE_BUILD_TYPE=Release# 编译cmake --build.--config Release# 安装(可选)sudocmake --install.编译选项
# 指定编译为静态库cmake..-DSPDLOG_BUILD_SHARED=OFF# 编译并运行测试cmake..-DSPDLOG_BUILD_TESTS=ON cmake --build.--targettest# 编译示例cmake..-DSPDLOG_BUILD_EXAMPLE=ON# 指定安装路径cmake..-DCMAKE_INSTALL_PREFIX=/usr/local三、在项目中使用
CMake 项目集成
方法1:find_package(如果已安装)
find_package(spdlog REQUIRED) target_link_libraries(your_target PRIVATE spdlog::spdlog)方法2:添加为子模块
add_subdirectory(spdlog) target_link_libraries(your_target PRIVATE spdlog::spdlog)方法3:FetchContent(CMake 3.11+)
include(FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY "https://github.com/gabime/spdlog.git" GIT_TAG "v1.x.x" ) FetchContent_MakeAvailable(spdlog) target_link_libraries(your_target PRIVATE spdlog::spdlog)简单使用示例
#include<spdlog/spdlog.h>#include<spdlog/sinks/basic_file_sink.h>intmain(){// 基础使用spdlog::info("Hello, {}!","World");spdlog::warn("This is a warning");// 创建文件日志autofile_logger=spdlog::basic_logger_mt("file_logger","logs.txt");file_logger->info("File log message");// 设置日志级别spdlog::set_level(spdlog::level::debug);return0;}四、编译示例程序
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(SpdlogExample) set(CMAKE_CXX_STANDARD 17) # 方法1:如果spdlog在子目录 add_subdirectory(spdlog) # 方法2:如果已安装 find_package(spdlog REQUIRED) add_executable(example main.cpp) target_link_libraries(example PRIVATE spdlog::spdlog)编译命令:
mkdirbuild&&cdbuild cmake..make./example五、依赖要求
- C++11 或更高版本
- 推荐 CMake 3.10+
- 可选依赖(用于额外功能):
- fmt(格式化库)
- pthread(多线程支持)
注意事项
- 头文件版本:spdlog 是 header-only 库,可以直接复制头文件使用
- 跨平台:支持 Windows、Linux、macOS
- 性能:默认是线程安全的,支持异步日志
- 编译时间:由于是模板库,编译时间可能较长,可以使用预编译头文件优化
选择哪种方式取决于你的项目需求。对于简单项目,直接包含头文件即可;对于大型项目,建议使用包管理器或 CMake 集成。
我来为您详细介绍如何编译 x64 和 Win32 的 Debug/Release 版本的 spdlog 库。
一、环境准备
1. 安装编译工具
# 1. 安装 Visual Studio (确保包含 C++ 开发组件)# 2. 安装 CMake: https://cmake.org/download/# 3. 可选: 安装 vcpkg 用于依赖管理2. 获取 spdlog 源代码
gitclone https://github.com/gabime/spdlog.gitcdspdlog二、使用 CMake 生成不同配置
方法1:使用批处理脚本(推荐)
create_builds.bat:
@echo off setlocal enabledelayedexpansion set SPDLOG_DIR=%cd% set GENERATOR="Visual Studio 17 2022" REM 创建所有构建目录 mkdir build_x64_debug 2>nul mkdir build_x64_release 2>nul mkdir build_win32_debug 2>nul mkdir build_win32_release 2>nul echo 生成 x64 Debug 配置... cd build_x64_debug cmake %SPDLOG_DIR% -G %GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=Debug cd .. echo 生成 x64 Release 配置... cd build_x64_release cmake %SPDLOG_DIR% -G %GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=Release cd .. echo 生成 Win32 Debug 配置... cd build_win32_debug cmake %SPDLOG_DIR% -G %GENERATOR% -A Win32 -DCMAKE_BUILD_TYPE=Debug cd .. echo 生成 Win32 Release 配置... cd build_win32_release cmake %SPDLOG_DIR% -G %GENERATOR% -A Win32 -DCMAKE_BUILD_TYPE=Release cd .. echo 所有配置生成完成! pause方法2:手动命令编译
# 1. x64 Debugmkdirbuild_x64_debug&&cdbuild_x64_debug cmake..-G"Visual Studio 17 2022"-A x64 -DCMAKE_BUILD_TYPE=Debug cmake --build.--config Debugcd..# 2. x64 Releasemkdirbuild_x64_release&&cdbuild_x64_release cmake..-G"Visual Studio 17 2022"-A x64 -DCMAKE_BUILD_TYPE=Release cmake --build.--config Releasecd..# 3. Win32 Debugmkdirbuild_win32_debug&&cdbuild_win32_debug cmake..-G"Visual Studio 17 2022"-A Win32 -DCMAKE_BUILD_TYPE=Debug cmake --build.--config Debugcd..# 4. Win32 Releasemkdirbuild_win32_release&&cdbuild_win32_release cmake..-G"Visual Studio 17 2022"-A Win32 -DCMAKE_BUILD_TYPE=Release cmake --build.--config Releasecd..三、完整的编译脚本
build_all.bat:
@echo off setlocal enabledelayedexpansion echo ============================================ echo SPDLOG 多平台编译脚本 echo ============================================ echo. set SRC_DIR=%~dp0 if "%SRC_DIR%"=="" set SRC_DIR=%cd% set BUILD_TYPES=Debug Release set ARCH_TYPES=x64 Win32 set VS_GENERATOR="Visual Studio 17 2022" REM 编译配置 set CMAKE_OPTIONS=-DSPDLOG_BUILD_SHARED=ON ^ -DSPDLOG_BUILD_EXAMPLE=OFF ^ -DSPDLOG_BUILD_TESTS=OFF ^ -DSPDLOG_INSTALL=ON REM 遍历所有配置 for %%a in (%ARCH_TYPES%) do ( for %%b in (%BUILD_TYPES%) do ( set BUILD_DIR=build_%%a_%%b echo. echo ============================================ echo 编译配置: %%a - %%b echo ============================================ REM 创建构建目录 if exist !BUILD_DIR! ( echo 清理目录: !BUILD_DIR! rmdir /s /q !BUILD_DIR! ) mkdir !BUILD_DIR! cd !BUILD_DIR! REM 生成 CMake 配置 echo 生成 CMake 配置... cmake %SRC_DIR% -G %VS_GENERATOR% -A %%a %CMAKE_OPTIONS% if errorlevel 1 ( echo CMake 配置失败! cd .. exit /b 1 ) REM 编译项目 echo 编译 %%b 版本... cmake --build . --config %%b -- /m if errorlevel 1 ( echo 编译失败! cd .. exit /b 1 ) REM 安装(可选) echo 安装库文件... cmake --install . --config %%b --prefix "install_%%a_%%b" cd .. echo 完成: !BUILD_DIR! ) ) echo. echo ============================================ echo 所有配置编译完成! echo ============================================ echo. echo 输出目录结构: echo ├── build_x64_Debug/ echo │ ├── spdlog.dll (Debug x64) echo │ ├── spdlog.lib echo │ └── install_x64_Debug/ echo ├── build_x64_Release/ echo │ ├── spdlog.dll (Release x64) echo │ ├── spdlog.lib echo │ └── install_x64_Release/ echo ├── build_Win32_Debug/ echo │ ├── spdlog.dll (Debug Win32) echo │ ├── spdlog.lib echo │ └── install_Win32_Debug/ echo └── build_Win32_Release/ echo ├── spdlog.dll (Release Win32) echo ├── spdlog.lib echo └── install_Win32_Release/ echo. pause四、单独编译各版本命令
编译 x64 Debug
mkdir build_x64_debug cd build_x64_debug cmake .. -G "Visual Studio 17 2022" -A x64 ^ -DCMAKE_BUILD_TYPE=Debug ^ -DSPDLOG_BUILD_SHARED=ON cmake --build . --config Debug编译 x64 Release
mkdir build_x64_release cd build_x64_release cmake .. -G "Visual Studio 17 2022" -A x64 ^ -DCMAKE_BUILD_TYPE=Release ^ -DSPDLOG_BUILD_SHARED=ON cmake --build . --config Release编译 Win32 Debug
mkdir build_win32_debug cd build_win32_debug cmake .. -G "Visual Studio 17 2022" -A Win32 ^ -DCMAKE_BUILD_TYPE=Debug ^ -DSPDLOG_BUILD_SHARED=ON cmake --build . --config Debug编译 Win32 Release
mkdir build_win32_release cd build_win32_release cmake .. -G "Visual Studio 17 2022" -A Win32 ^ -DCMAKE_BUILD_TYPE=Release ^ -DSPDLOG_BUILD_SHARED=ON cmake --build . --config Release五、打包为可分发库
package_libs.bat:
@echo off setlocal set VERSION=1.12.0 set PACKAGE_DIR=spdlog-%VERSION%-windows echo 创建打包目录... mkdir %PACKAGE_DIR% mkdir %PACKAGE_DIR%\include mkdir %PACKAGEDIR%\lib mkdir %PACKAGE_DIR%\bin REM 复制头文件 xcopy /E /I include %PACKAGE_DIR%\include\ echo. echo 复制库文件... echo ========================= REM x64 Debug mkdir %PACKAGE_DIR%\lib\x64\debug copy build_x64_debug\Debug\*.lib %PACKAGE_DIR%\lib\x64\debug\ 2>nul mkdir %PACKAGE_DIR%\bin\x64\debug copy build_x64_debug\Debug\*.dll %PACKAGE_DIR%\bin\x64\debug\ 2>nul REM x64 Release mkdir %PACKAGE_DIR%\lib\x64\release copy build_x64_release\Release\*.lib %PACKAGE_DIR%\lib\x64\release\ 2>nul mkdir %PACKAGE_DIR%\bin\x64\release copy build_x64_release\Release\*.dll %PACKAGE_DIR%\bin\x64\release\ 2>nul REM Win32 Debug mkdir %PACKAGE_DIR%\lib\win32\debug copy build_win32_debug\Debug\*.lib %PACKAGE_DIR%\lib\win32\debug\ 2>nul mkdir %PACKAGE_DIR%\bin\win32\debug copy build_win32_debug\Debug\*.dll %PACKAGE_DIR%\bin\win32\debug\ 2>nul REM Win32 Release mkdir %PACKAGE_DIR%\lib\win32\release copy build_win32_release\Release\*.lib %PACKAGE_DIR%\lib\win32\release\ 2>nul mkdir %PACKAGE_DIR%\bin\win32\release copy build_win32_release\Release\*.dll %PACKAGE_DIR%\bin\win32\release\ 2>nul REM 创建配置文件 echo Creating CMake config files... ( echo # SPDLOG Config echo set(SPDLOG_VERSION %VERSION%) echo set(SPDLOG_INCLUDE_DIRS \${CMAKE_CURRENT_LIST_DIR}/../include) echo. echo # Import targets echo if(NOT TARGET spdlog::spdlog) echo add_library(spdlog::spdlog SHARED IMPORTED) echo. echo # x64 Debug echo set_target_properties(spdlog::spdlog PROPERTIES echo IMPORTED_CONFIGURATIONS Debug echo IMPORTED_IMPLIB_DEBUG \${CMAKE_CURRENT_LIST_DIR}/lib/x64/debug/spdlogd.lib echo IMPORTED_LOCATION_DEBUG \${CMAKE_CURRENT_LIST_DIR}/bin/x64/debug/spdlogd.dll echo ) echo. echo # x64 Release echo set_target_properties(spdlog::spdlog PROPERTIES echo IMPORTED_CONFIGURATIONS Release echo IMPORTED_IMPLIB_RELEASE \${CMAKE_CURRENT_LIST_DIR}/lib/x64/release/spdlog.lib echo IMPORTED_LOCATION_RELEASE \${CMAKE_CURRENT_LIST_DIR}/bin/x64/release/spdlog.dll echo ) echo endif() ) > %PACKAGE_DIR%\spdlog-config.cmake echo. echo 打包完成: %PACKAGE_DIR% echo 包含: echo - x64 Debug/Release (DLL + LIB) echo - Win32 Debug/Release (DLL + LIB) echo - 头文件 echo - CMake 配置文件 REM 压缩打包 powershell Compress-Archive -Path %PACKAGE_DIR% -DestinationPath %PACKAGE_DIR%.zip echo. echo 已创建压缩包: %PACKAGE_DIR%.zip pause六、使用注意事项
1. 编译器版本对应关系
Visual Studio 版本 CMake 生成器名称 VS 2022 "Visual Studio 17 2022" VS 2019 "Visual Studio 16 2019" VS 2017 "Visual Studio 15 2017"2. 文件命名规则
Debug 版本: spdlogd.lib / spdlogd.dll Release 版本: spdlog.lib / spdlog.dll3. 在项目中使用
CMakeLists.txt:
# 根据配置选择库文件 if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCH_DIR "x64") else() set(ARCH_DIR "win32") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CONFIG_DIR "debug") set(LIB_NAME "spdlogd") else() set(CONFIG_DIR "release") set(LIB_NAME "spdlog") endif() # 设置库路径 set(SPDLOG_LIB_DIR "${CMAKE_SOURCE_DIR}/libs/spdlog/lib/${ARCH_DIR}/${CONFIG_DIR}") set(SPDLOG_DLL_DIR "${CMAKE_SOURCE_DIR}/libs/spdlog/bin/${ARCH_DIR}/${CONFIG_DIR}") # 添加库 add_library(spdlog SHARED IMPORTED) set_target_properties(spdlog PROPERTIES IMPORTED_IMPLIB "${SPDLOG_LIB_DIR}/${LIB_NAME}.lib" IMPORTED_LOCATION "${SPDLOG_DLL_DIR}/${LIB_NAME}.dll" ) # 包含头文件 target_include_directories(your_target PRIVATE "${CMAKE_SOURCE_DIR}/libs/spdlog/include") target_link_libraries(your_target PRIVATE spdlog)这样你就可以得到完整的 x64/Win32 的 Debug/Release 版本 spdlog 库了!
使用例子
完整的 spdlog 使用示例
这里我提供一个完整的 C++ 项目,包含多个 spdlog 使用场景。
项目结构
spdlog_example/ ├── CMakeLists.txt ├── main.cpp ├── logger_config.h └── advanced_logging.cpp1. CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(SpdlogExample CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 选项:使用静态库还是动态库 option(USE_STATIC_SPDLOG "Use static spdlog library" OFF) # 查找或下载 spdlog find_package(spdlog QUIET) if(NOT spdlog_FOUND) message(STATUS "spdlog not found, downloading...") # 方法1:使用 FetchContent include(FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.12.0 ) FetchContent_MakeAvailable(spdlog) # 或者方法2:下载源码 # file(DOWNLOAD https://github.com/gabime/spdlog/archive/refs/tags/v1.12.0.zip # ${CMAKE_BINARY_DIR}/spdlog.zip) # execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf spdlog.zip) # add_subdirectory(${CMAKE_BINARY_DIR}/spdlog-1.12.0) endif() # 设置编译模式 if(MSVC) add_compile_options(/W4 /EHsc) if(USE_STATIC_SPDLOG) add_definitions(-DSPDLOG_COMPILED_LIB) endif() else() add_compile_options(-Wall -Wextra -pedantic) endif() # 添加可执行文件 add_executable(spdlog_example main.cpp logger_config.h advanced_logging.cpp ) # 链接 spdlog target_link_libraries(spdlog_example PRIVATE spdlog::spdlog) # 添加测试(可选) enable_testing() add_test(NAME SpdlogExample COMMAND spdlog_example) # 安装规则(可选) install(TARGETS spdlog_example RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib )2. logger_config.h - 日志配置类
/** * @file logger_config.h * @brief spdlog 日志配置类 */#ifndefLOGGER_CONFIG_H#defineLOGGER_CONFIG_H#include<spdlog/spdlog.h>#include<spdlog/sinks/basic_file_sink.h>#include<spdlog/sinks/rotating_file_sink.h>#include<spdlog/sinks/daily_file_sink.h>#include<spdlog/sinks/stdout_color_sinks.h>#include<spdlog/sinks/windows_eventlog_sink.h>#include<spdlog/async.h>#include<memory>#include<string>#include<vector>#ifdef_WIN32#include<windows.h>#endif/** * @class LoggerManager * @brief 日志管理器,单例模式 */classLoggerManager{public:// 日志级别枚举enumclassLogLevel{TRACE=spdlog::level::trace,DEBUG=spdlog::level::debug,INFO=spdlog::level::info,WARN=spdlog::level::warn,ERROR=spdlog::level::err,CRITICAL=spdlog::level::critical,OFF=spdlog::level::off};// 日志类型enumclassLoggerType{CONSOLE,FILE,ROTATING_FILE,DAILY_FILE,CONSOLE_AND_FILE,ASYNC,EVENT_LOG// Windows only};/** * @brief 获取单例实例 */staticLoggerManager&instance(){staticLoggerManager manager;returnmanager;}/** * @brief 初始化日志系统 * @param logger_name 日志器名称 * @param type 日志类型 * @param level 日志级别 * @param file_path 文件路径(文件日志需要) * @param max_size 最大文件大小(旋转文件需要,单位MB) * @param max_files 最大文件数(旋转文件需要) */voidinitialize(conststd::string&logger_name="default",LoggerType type=LoggerType::CONSOLE_AND_FILE,LogLevel level=LogLevel::INFO,conststd::string&file_path="logs/app.log",size_t max_size=10,// 10MBsize_t max_files=5);/** * @brief 获取日志器 */std::shared_ptr<spdlog::logger>getLogger()const{returnlogger_;}/** * @brief 设置日志级别 */voidsetLevel(LogLevel level);/** * @brief 设置日志模式(同步/异步) */voidsetAsyncMode(boolasync,size_t queue_size=8192,size_t thread_count=1);/** * @brief 设置日志格式 */voidsetPattern(conststd::string&pattern);/** * @brief 立即刷新日志 */voidflush();/** * @brief 注册自定义日志器 */voidregisterCustomLogger(conststd::shared_ptr<spdlog::logger>&custom_logger);// 禁用拷贝和移动LoggerManager(constLoggerManager&)=delete;LoggerManager&operator=(constLoggerManager&)=delete;private:LoggerManager()=default;~LoggerManager();std::shared_ptr<spdlog::logger>logger_;std::vector<spdlog::sink_ptr>sinks_;boolasync_mode_=false;};// 便捷宏定义#defineLOG_TRACE(...)SPDLOG_LOGGER_TRACE(LoggerManager::instance().getLogger(),__VA_ARGS__)#defineLOG_DEBUG(...)SPDLOG_LOGGER_DEBUG(LoggerManager::instance().getLogger(),__VA_ARGS__)#defineLOG_INFO(...)SPDLOG_LOGGER_INFO(LoggerManager::instance().getLogger(),__VA_ARGS__)#defineLOG_WARN(...)SPDLOG_LOGGER_WARN(LoggerManager::instance().getLogger(),__VA_ARGS__)#defineLOG_ERROR(...)SPDLOG_LOGGER_ERROR(LoggerManager::instance().getLogger(),__VA_ARGS__)#defineLOG_CRITICAL(...)SPDLOG_LOGGER_CRITICAL(LoggerManager::instance().getLogger(),__VA_ARGS__)// 带位置的日志#defineLOG_INFO_LOC(...)SPDLOG_LOGGER_INFO(LoggerManager::instance().getLogger(),"{}:{} - {}",__FILE__,__LINE__,fmt::format(__VA_ARGS__))#endif// LOGGER_CONFIG_H3. logger_config.cpp
/** * @file logger_config.cpp * @brief 日志配置实现 */#include"logger_config.h"#include<filesystem>namespacefs=std::filesystem;LoggerManager::~LoggerManager(){if(logger_){logger_->flush();spdlog::drop(logger_->name());}spdlog::shutdown();}voidLoggerManager::initialize(conststd::string&logger_name,LoggerType type,LogLevel level,conststd::string&file_path,size_t max_size,size_t max_files){// 清理现有日志器if(logger_){spdlog::drop(logger_->name());}sinks_.clear();// 创建 sinkswitch(type){caseLoggerType::CONSOLE:{autoconsole_sink=std::make_shared<spdlog::sinks::stdout_color_sink_mt>();sinks_.push_back(console_sink);break;}caseLoggerType::FILE:{// 确保日志目录存在fs::pathpath(file_path);fs::create_directories(path.parent_path());autofile_sink=std::make_shared<spdlog::sinks::basic_file_sink_mt>(file_path,true);sinks_.push_back(file_sink);break;}caseLoggerType::ROTATING_FILE:{fs::pathpath(file_path);fs::create_directories(path.parent_path());autorotating_sink=std::make_shared<spdlog::sinks::rotating_file_sink_mt>(file_path,max_size*1024*1024,max_files);sinks_.push_back(rotating_sink);break;}caseLoggerType::DAILY_FILE:{fs::pathpath(file_path);fs::create_directories(path.parent_path());autodaily_sink=std::make_shared<spdlog::sinks::daily_file_sink_mt>(file_path,0,0);// 每天午夜创建新文件sinks_.push_back(daily_sink);break;}caseLoggerType::CONSOLE_AND_FILE:{autoconsole_sink=std::make_shared<spdlog::sinks::stdout_color_sink_mt>();sinks_.push_back(console_sink);fs::pathpath(file_path);fs::create_directories(path.parent_path());autofile_sink=std::make_shared<spdlog::sinks::basic_file_sink_mt>(file_path,true);sinks_.push_back(file_sink);break;}caseLoggerType::ASYNC:{setAsyncMode(true);autoconsole_sink=std::make_shared<spdlog::sinks::stdout_color_sink_mt>();sinks_.push_back(console_sink);break;}#ifdef_WIN32caseLoggerType::EVENT_LOG:{autoeventlog_sink=std::make_shared<spdlog::sinks::win_eventlog_sink_mt>(logger_name);sinks_.push_back(eventlog_sink);break;}#endifdefault:throwstd::runtime_error("Unsupported logger type");}// 创建日志器if(async_mode_){logger_=spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(logger_name);}else{logger_=std::make_shared<spdlog::logger>(logger_name,begin(sinks_),end(sinks_));}// 设置日志级别setLevel(level);// 设置日志格式logger_->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%n] [thread %t] %v");// 设置刷新级别logger_->flush_on(spdlog::level::warn);// 注册日志器spdlog::register_logger(logger_);spdlog::info("Logger initialized: {}",logger_name);}voidLoggerManager::setLevel(LogLevel level){if(logger_){logger_->set_level(static_cast<spdlog::level::level_enum>(level));}}voidLoggerManager::setAsyncMode(boolasync,size_t queue_size,size_t thread_count){if(async&&!async_mode_){spdlog::init_thread_pool(queue_size,thread_count);async_mode_=true;}async_mode_=async;}voidLoggerManager::setPattern(conststd::string&pattern){if(logger_){logger_->set_pattern(pattern);}}voidLoggerManager::flush(){if(logger_){logger_->flush();}}voidLoggerManager::registerCustomLogger(conststd::shared_ptr<spdlog::logger>&custom_logger){if(custom_logger){spdlog::register_logger(custom_logger);}}4. main.cpp - 主程序示例
/** * @file main.cpp * @brief spdlog 使用示例 */#include"logger_config.h"#include<spdlog/fmt/ostr.h>// 支持自定义类型格式化#include<spdlog/fmt/bundled/format.h>#include<iostream>#include<thread>#include<vector>#include<chrono>#include<memory>#include<random>// 自定义类型示例structUser{std::string name;intage;std::string email;// 支持 spdlog 格式化输出friendstd::ostream&operator<<(std::ostream&os,constUser&user){returnos<<fmt::format("User{{name={}, age={}, email={}}}",user.name,user.age,user.email);}};// 演示基本日志功能voiddemo_basic_logging(){std::cout<<"=== 基础日志演示 ===\n";LOG_INFO("应用程序启动");LOG_DEBUG("调试信息:{}","这是一条调试消息");LOG_INFO("欢迎使用 spdlog,版本:{}",SPDLOG_VERSION);LOG_WARN("警告:内存使用率达到 {}%",85.5);LOG_ERROR("错误:文件 {} 不存在","config.json");// 带参数的格式化LOG_INFO("用户 {} 在 {} 登录","张三","2024-01-15 10:30:00");// 条件日志intretry_count=3;SPDLOG_LOGGER_INFO(LoggerManager::instance().getLogger(),"重试次数: {}, 状态: {}",retry_count,retry_count>0?"继续":"停止");}// 演示多线程日志voiddemo_multithread_logging(){std::cout<<"\n=== 多线程日志演示 ===\n";std::vector<std::thread>threads;constintthread_count=5;autoworker=[](intid){for(inti=0;i<3;++i){LOG_INFO("线程 {} - 任务 {}",id,i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}};for(inti=0;i<thread_count;++i){threads.emplace_back(worker,i+1);}for(auto&t:threads){t.join();}LOG_INFO("所有线程执行完成");}// 演示性能日志voiddemo_performance_logging(){std::cout<<"\n=== 性能日志演示 ===\n";autostart=std::chrono::high_resolution_clock::now();// 模拟一些工作std::random_device rd;std::mt19937gen(rd());std::uniform_int_distribution<>dis(1,100);intsum=0;constintiterations=1000000;for(inti=0;i<iterations;++i){sum+=dis(gen);}autoend=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end-start);// 使用性能日志级别LOG_INFO("计算完成,迭代次数: {}, 总和: {}, 耗时: {} ms",iterations,sum,duration.count());// 日志级别控制LoggerManager::instance().setLevel(LoggerManager::LogLevel::DEBUG);LOG_DEBUG("详细性能数据: 平均每次迭代耗时: {:.6f} ms",duration.count()/static_cast<double>(iterations));}// 演示自定义类型日志voiddemo_custom_type_logging(){std::cout<<"\n=== 自定义类型日志演示 ===\n";User user{"张三",25,"zhangsan@example.com"};LOG_INFO("用户信息: {}",user);std::vector<User>users={{"李四",30,"lisi@example.com"},{"王五",28,"wangwu@example.com"},{"赵六",35,"zhaoliu@example.com"}};LOG_INFO("用户列表:");for(constauto&u:users){LOG_INFO(" - {}",u);}}// 演示异常日志voiddemo_exception_logging(){std::cout<<"\n=== 异常日志演示 ===\n";try{throwstd::runtime_error("模拟业务异常");}catch(conststd::exception&e){LOG_ERROR("捕获异常: {}, 位置: {}:{}",e.what(),__FILE__,__LINE__);// 记录堆栈信息(需要其他库支持,这里只是示例)LOG_ERROR("异常堆栈:");LOG_ERROR(" - main()");LOG_ERROR(" - demo_exception_logging()");LOG_ERROR(" - std::runtime_error::runtime_error()");}}// 演示不同日志级别voiddemo_log_levels(){std::cout<<"\n=== 日志级别演示 ===\n";auto&logger=LoggerManager::instance();// 显示所有级别的日志logger.setLevel(LoggerManager::LogLevel::TRACE);LOG_TRACE("这是一条 TRACE 级别的日志");LOG_DEBUG("这是一条 DEBUG 级别的日志");LOG_INFO("这是一条 INFO 级别的日志");LOG_WARN("这是一条 WARN 级别的日志");LOG_ERROR("这是一条 ERROR 级别的日志");LOG_CRITICAL("这是一条 CRITICAL 级别的日志");// 设置为只显示 ERROR 及以上logger.setLevel(LoggerManager::LogLevel::ERROR);LOG_INFO("这条 INFO 日志不会被显示");LOG_ERROR("这条 ERROR 日志会被显示");}// 演示文件日志voiddemo_file_logging(){std::cout<<"\n=== 文件日志演示 ===\n";auto&logger=LoggerManager::instance();// 重新初始化为文件日志logger.initialize("file_logger",LoggerManager::LoggerType::FILE,LoggerManager::LogLevel::INFO,"logs/file_demo.log");LOG_INFO("这条日志会写入文件");LOG_WARN("警告信息也会写入文件");LOG_ERROR("错误信息同样写入文件");std::cout<<"日志已写入 logs/file_demo.log 文件\n";}// 演示异步日志voiddemo_async_logging(){std::cout<<"\n=== 异步日志演示 ===\n";auto&logger=LoggerManager::instance();logger.setAsyncMode(true,8192,2);constintmessage_count=1000;autostart=std::chrono::high_resolution_clock::now();std::vector<std::thread>threads;constintthread_count=4;autoworker=[&logger](intid,intcount){for(inti=0;i<count;++i){SPDLOG_LOGGER_INFO(logger.getLogger(),"异步日志 - 线程 {} - 消息 {}",id,i);}};intmessages_per_thread=message_count/thread_count;for(inti=0;i<thread_count;++i){threads.emplace_back(worker,i+1,messages_per_thread);}for(auto&t:threads){t.join();}autoend=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end-start);LOG_INFO("异步日志完成,总共 {} 条消息,耗时 {} ms",message_count,duration.count());// 等待异步队列清空logger.flush();}// 演示日志配置voiddemo_logger_configuration(){std::cout<<"\n=== 日志配置演示 ===\n";auto&logger=LoggerManager::instance();// 1. 自定义日志格式logger.setPattern("[%Y-%m-%d %H:%M:%S] [%l] [%n] %v");LOG_INFO("自定义格式日志");// 2. 恢复默认格式logger.setPattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%n] [thread %t] %v");LOG_INFO("恢复默认格式");// 3. 设置不同的日志级别logger.setLevel(LoggerManager::LogLevel::WARN);LOG_INFO("这条 INFO 日志不会显示");LOG_WARN("这条 WARN 日志会显示");// 4. 刷新日志logger.flush();LOG_INFO("日志已刷新到磁盘");}intmain(){try{// 初始化日志系统LoggerManager::instance().initialize("demo_logger",LoggerManager::LoggerType::CONSOLE_AND_FILE,LoggerManager::LogLevel::INFO,"logs/demo.log");// 设置全局异常处理器spdlog::set_error_handler([](conststd::string&msg){std::cerr<<"spdlog 错误: "<<msg<<std::endl;});// 运行各种演示demo_basic_logging();demo_multithread_logging();demo_performance_logging();demo_custom_type_logging();demo_exception_logging();demo_log_levels();demo_file_logging();demo_async_logging();demo_logger_configuration();// 程序结束日志LOG_INFO("程序正常退出");}catch(conststd::exception&e){std::cerr<<"程序异常: "<<e.what()<<std::endl;return1;}return0;}5. advanced_logging.cpp - 高级功能演示
/** * @file advanced_logging.cpp * @brief spdlog 高级功能演示 */#include"logger_config.h"#include<spdlog/sinks/ostream_sink.h>#include<spdlog/sinks/udp_sink.h>#include<spdlog/sinks/tcp_sink.h>#include<spdlog/sinks/syslog_sink.h>#include<spdlog/sinks/android_sink.h>#include<spdlog/fmt/bin_to_hex.h>#include<sstream>#include<fstream>#include<iomanip>// 演示二进制数据日志voiddemo_binary_logging(){std::cout<<"\n=== 二进制数据日志演示 ===\n";unsignedcharbinary_data[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};LOG_INFO("二进制数据: {}",spdlog::to_hex(binary_data,sizeof(binary_data)));LOG_INFO("带 ASCII 显示: {}",spdlog::to_hex(binary_data,sizeof(binary_data),true));// 格式化显示std::string hex_dump=spdlog::to_hex(binary_data,sizeof(binary_data),8);LOG_INFO("格式化十六进制:\n{}",hex_dump);}// 演示条件编译日志voiddemo_conditional_logging(){std::cout<<"\n=== 条件编译日志演示 ===\n";#ifdef_DEBUGLOG_DEBUG("这是调试版本");#elseLOG_INFO("这是发布版本");#endif// SPDLOG_LOGGER_CALL 宏interror_code=404;SPDLOG_LOGGER_CALL(LoggerManager::instance().getLogger(),spdlog::level::err,"错误代码: {}",error_code);}// 演示流式日志(不推荐,性能较低)voiddemo_stream_logging(){std::cout<<"\n=== 流式日志演示 ===\n";// 注意:流式日志性能较低,建议使用格式化字符串autologger=LoggerManager::instance().getLogger();SPDLOG_LOGGER_INFO(logger,"流式日志: "<<"第一部分 "<<123<<" 第二部分");}// 演示自定义 sinkvoiddemo_custom_sink(){std::cout<<"\n=== 自定义 Sink 演示 ===\n";// 创建 stringstream sinkautooss_sink=std::make_shared<spdlog::sinks::ostream_sink_mt>(std::cout);autocustom_logger=std::make_shared<spdlog::logger>("custom",oss_sink);custom_logger->set_pattern("[custom] %v");custom_logger->info("这是通过自定义 sink 输出的日志");// 注册到管理器LoggerManager::instance().registerCustomLogger(custom_logger);}// 演示速率限制日志voiddemo_rate_limited_logging(){std::cout<<"\n=== 速率限制日志演示 ===\n";// spdlog 本身没有内置速率限制,但可以通过条件控制staticintlog_count=0;staticautolast_log_time=std::chrono::steady_clock::now();autonow=std::chrono::steady_clock::now();autoelapsed=std::chrono::duration_cast<std::chrono::seconds>(now-last_log_time);if(elapsed.count()>=1){// 每秒最多一条LOG_INFO("速率限制日志 - 计数: {}",++log_count);last_log_time=now;}}// 演示结构化日志(JSON 格式)voiddemo_structured_logging(){std::cout<<"\n=== 结构化日志演示 ===\n";// 设置为 JSON 格式LoggerManager::instance().setPattern("{\"timestamp\":\"%Y-%m-%dT%H:%M:%S.%eZ\",""\"level\":\"%l\",""\"thread\":%t,""\"message\":\"%v\"}");LOG_INFO("用户登录成功");LOG_WARN("内存使用率偏高");// 恢复默认格式LoggerManager::instance().setPattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");}// 演示日志轮转voiddemo_log_rotation(){std::cout<<"\n=== 日志轮转演示 ===\n";auto&logger=LoggerManager::instance();// 重新初始化为旋转文件日志logger.initialize("rotation_logger",LoggerManager::LoggerType::ROTATING_FILE,LoggerManager::LogLevel::INFO,"logs/rotation.log",1,// 1MB3);// 保留3个文件// 生成大量日志以触发轮转for(inti=0;i<1000;++i){LOG_INFO("这是第 {} 条日志消息,用于测试日志轮转功能",i);}std::cout<<"已生成测试日志,检查 logs/ 目录下的 rotation.log 文件\n";}// 演示系统日志(Linux/Unix)voiddemo_syslog_logging(){std::cout<<"\n=== 系统日志演示 ===\n";#ifndef_WIN32// Linux/Unix 系统日志try{autosyslog_sink=std::make_shared<spdlog::sinks::syslog_sink_mt>("spdlog_demo");autosyslog_logger=std::make_shared<spdlog::logger>("syslog",syslog_sink);syslog_logger->info("这是一条系统日志消息");LoggerManager::instance().registerCustomLogger(syslog_logger);}catch(constspdlog::spdlog_ex&ex){LOG_WARN("系统日志不可用: {}",ex.what());}#elseLOG_INFO("Windows 系统不支持 Unix 系统日志");#endif}// 演示网络日志(UDP/TCP)voiddemo_network_logging(){std::cout<<"\n=== 网络日志演示 ===\n";LOG_INFO("网络日志功能需要额外的网络配置");LOG_INFO("可以使用 udp_sink 或 tcp_sink 发送日志到远程服务器");// 示例代码(需要服务器支持)/* try { // UDP 日志 auto udp_sink = std::make_shared<spdlog::sinks::udp_sink_mt>("127.0.0.1", 9999); auto udp_logger = std::make_shared<spdlog::logger>("udp", udp_sink); udp_logger->info("UDP 日志消息"); // TCP 日志 auto tcp_sink = std::make_shared<spdlog::sinks::tcp_sink_mt>("127.0.0.1", 9998); auto tcp_logger = std::make_shared<spdlog::logger>("tcp", tcp_sink); tcp_logger->info("TCP 日志消息"); } catch (const std::exception& e) { LOG_ERROR("网络日志错误: {}", e.what()); } */}// 演示日志分析voiddemo_log_analysis(){std::cout<<"\n=== 日志分析演示 ===\n";// 读取日志文件进行分析std::ifstreamlog_file("logs/demo.log");if(log_file.is_open()){std::string line;intinfo_count=0,error_count=0,warn_count=0;while(std::getline(log_file,line)){if(line.find("[info]")!=std::string::npos)info_count++;if(line.find("[error]")!=std::string::npos)error_count++;if(line.find("[warn]")!=std::string::npos)warn_count++;}LOG_INFO("日志分析结果:");LOG_INFO(" INFO 消息: {} 条",info_count);LOG_INFO(" WARN 消息: {} 条",warn_count);LOG_INFO(" ERROR 消息: {} 条",error_count);log_file.close();}else{LOG_WARN("无法打开日志文件进行分析");}}6. 编译和运行
Windows (MSVC):
# 创建构建目录mkdirbuild&&cdbuild# 配置 CMake(根据你的 VS 版本调整)cmake..-G"Visual Studio 17 2022"-A x64# 编译cmake --build.--config Release# 运行.\Release\spdlog_example.exeLinux/Mac:
mkdirbuild&&cdbuild cmake..-DCMAKE_BUILD_TYPE=Releasemake-j4 ./spdlog_example7. 示例输出
程序运行后会:
- 在控制台输出彩色日志
- 在
logs/目录下生成日志文件 - 演示各种日志功能
=== 基础日志演示 === [2024-01-15 10:30:00.123] [INFO] [demo_logger] 应用程序启动 [2024-01-15 10:30:00.124] [INFO] [demo_logger] 欢迎使用 spdlog,版本:1.12.0 [2024-01-15 10:30:00.125] [WARN] [demo_logger] 警告:内存使用率达到 85.5% === 多线程日志演示 === [2024-01-15 10:30:00.126] [INFO] [demo_logger] 线程 1 - 任务 0 [2024-01-15 10:30:00.126] [INFO] [demo_logger] 线程 2 - 任务 0 ...8. 主要特点
这个示例展示了:
- ✅ 多线程安全日志
- ✅ 彩色控制台输出
- ✅ 文件日志(普通、轮转、每日)
- ✅ 异步日志(高性能)
- ✅ 自定义日志格式
- ✅ 结构化日志(JSON)
- ✅ 二进制数据日志
- ✅ 异常日志记录
- ✅ 性能监控日志
- ✅ 条件编译日志
- ✅ 自定义类型支持
- ✅ 日志级别控制
- ✅ 日志刷新策略
你可以根据需要修改和扩展这个示例来满足具体的项目需求。