spdlog文件事件回调调用info()致段错误与双释放问题求助
解决spdlog回调中调用logger引发的段错误与双释放问题
问题背景
在使用spdlog的after_open和before_close文件事件回调时,调用spdlog::get("basic_logger")->info()会触发段错误或双释放崩溃,但直接用fmt::print写入文件流则正常。需要替换为spdlog的info()方法来获取规范的日志格式。
最小复现代码
#include <chrono> #include <exception> #include <cstdio> #include <fmt/core.h> #include <fmt/chrono.h> #include <spdlog/spdlog.h> #include <spdlog/sinks/basic_file_sink.h> int main( ) { spdlog::file_event_handlers handlers; handlers.after_open = [ ]( const spdlog::filename_t filename, std::FILE* const stream ) { // 注释此行可查看before_close回调引发的错误 spdlog::get( "basic_logger" )->info( "\nLogging started..." ); fmt::print( stream, "\n[{}] [{}] Logging started...\n", std::chrono::system_clock::now( ), filename ); }; handlers.before_close = [ ]( const spdlog::filename_t filename, std::FILE* const stream ) { spdlog::get( "basic_logger" )->info( "Logging finished." ); fmt::print( stream, "[{}] [{}] Logging finished.\n", std::chrono::system_clock::now( ), filename ); }; try { auto logger { spdlog::basic_logger_st( "basic_logger", "logs/basic-log.txt", true, handlers ) }; } catch ( const std::exception& sx ) { fmt::print( stderr, "\nSomething went wrong during program startup: log file init failed: {}\n\n", sx.what( ) ); } }
错误信息
段错误时输出:
Segmentation fault (core dumped)
双释放时输出:
free(): double free detected in tcache 2 Aborted (core dumped)
编译命令
g++ -std=c++23 -O3 -march=westmere -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow -fwhole-program -DNDEBUG `pkg-config --cflags spdlog` `pkg-config --libs spdlog` Temp.cpp -o runTemp
问题原因与修复方案
原因分析
after_open回调段错误:创建basic_logger_st时,after_open回调执行时机早于logger注册到spdlog全局注册表的节点,此时spdlog::get("basic_logger")返回空指针,调用info()触发段错误。before_close回调双释放:logger对象离开作用域销毁时,会先触发before_close回调,此时通过spdlog::get获取的logger正处于销毁流程中,内部资源已开始释放,重复操作导致双释放错误。
修复代码
绕过全局注册表,直接通过共享指针捕获logger传递给回调:
#include <chrono> #include <exception> #include <cstdio> #include <fmt/core.h> #include <fmt/chrono.h> #include <spdlog/spdlog.h> #include <spdlog/sinks/basic_file_sink.h> #include <memory> int main( ) { try { // 先创建不带回调的logger,确保初始化完成 auto logger = spdlog::basic_logger_st( "basic_logger", "logs/basic-log.txt", true ); // 获取文件sink指针,设置事件回调 auto file_sink = std::dynamic_pointer_cast<spdlog::sinks::basic_file_sink_mt>(logger->sinks()[0]); if (file_sink) { spdlog::file_event_handlers handlers; // 捕获logger的shared_ptr,确保回调执行时对象有效 handlers.after_open = [logger](const spdlog::filename_t filename, std::FILE* const stream) { logger->info("\nLogging started..."); fmt::print(stream, "\n[{}] [{}] Logging started...\n", std::chrono::system_clock::now(), filename); }; handlers.before_close = [logger](const spdlog::filename_t filename, std::FILE* const stream) { logger->info("Logging finished."); fmt::print(stream, "[{}] [{}] Logging finished.\n", std::chrono::system_clock::now(), filename); }; file_sink->set_event_handlers(std::move(handlers)); } // 正常使用logger logger->info("Program running..."); } catch (const std::exception& sx) { fmt::print(stderr, "\n程序启动出错:日志文件初始化失败:{}\n\n", sx.what()); } }
关键说明
- 先完成logger初始化,再获取对应的文件sink设置回调,避免时序问题。
- 通过lambda捕获logger的
shared_ptr,保证回调执行时logger对象始终有效,不会出现悬空指针或重复释放。
内容的提问来源于stack exchange,提问作者digito_evo
相关产品推荐
相关产品推荐

