Loguru高级用法:如何自定义回调函数和信号处理
【免费下载链接】loguruA lightweight C++ logging library项目地址: https://gitcode.com/gh_mirrors/log/loguru
Loguru是一个轻量级、高性能的C++日志库,它提供了强大的自定义功能,特别是通过回调函数和信号处理机制,让开发者能够完全掌控日志的行为。本文将详细介绍Loguru的高级用法,特别是如何自定义回调函数和信号处理,帮助您构建更灵活、更可靠的日志系统。🔧
📋 为什么需要自定义回调函数?
Loguru的默认日志输出已经很强大,但有时我们需要更精细的控制。比如:
- 将日志发送到远程服务器
- 实时在GUI界面显示日志
- 根据特定条件过滤或处理日志
- 集成到现有的监控系统中
Loguru的回调机制正是为此而生!它允许您在日志被写入之前或之后执行自定义操作。
🎯 Loguru回调函数核心概念
回调函数类型
在loguru.hpp中,Loguru定义了三种回调函数类型:
// 日志处理回调 typedef void (*log_handler_t)(void* user_data, const Message& message); // 关闭回调 typedef void (*close_handler_t)(void* user_data); // 刷新回调 typedef void (*flush_handler_t)(void* user_data); // 致命错误处理回调 typedef void (*fatal_handler_t)(const Message& message);消息结构体
每个日志消息都包装在Message结构体中,包含完整的信息:
struct Message { Verbosity verbosity; // 日志级别 const char* filename; // 文件名 unsigned line; // 行号 const char* preamble; // 前缀信息(日期、时间、线程等) const char* indentation; // 缩进 const char* prefix; // 断言失败信息 const char* message; // 用户消息 };🔧 自定义回调函数实战指南
1. 添加自定义回调函数
使用loguru::add_callback()函数可以轻松添加自定义回调:
void my_custom_handler(void* user_data, const loguru::Message& message) { // 自定义处理逻辑 // 例如:发送到网络、写入数据库等 } // 添加回调 loguru::add_callback( "my_callback", // 回调ID my_custom_handler, // 处理函数 nullptr, // 用户数据 loguru::Verbosity_INFO, // 最低日志级别 nullptr, // 关闭回调(可选) nullptr // 刷新回调(可选) );2. 实时GUI日志显示示例
假设您正在开发一个图形界面应用程序,需要在界面上实时显示日志:
class LogWindow { public: static void logToGUI(void* user_data, const loguru::Message& msg) { auto window = static_cast<LogWindow*>(user_data); window->appendLog(msg); } void appendLog(const loguru::Message& msg) { // 在GUI线程中更新界面 QString logText = QString("%1: %2") .arg(msg.preamble) .arg(msg.message); ui->logTextEdit->append(logText); } }; // 使用示例 LogWindow window; loguru::add_callback("gui_logger", &LogWindow::logToGUI, &window, loguru::Verbosity_INFO);3. 远程日志服务器集成
将日志实时发送到远程监控服务器:
void sendToRemoteServer(void* user_data, const loguru::Message& message) { auto server = static_cast<RemoteServer*>(user_data); Json::Value logEntry; logEntry["level"] = loguru::get_verbosity_name(message.verbosity); logEntry["file"] = message.filename; logEntry["line"] = message.line; logEntry["message"] = message.message; logEntry["timestamp"] = getCurrentTimestamp(); server->sendLog(logEntry); } // 添加远程日志回调 RemoteServer server; loguru::add_callback("remote_logger", sendToRemoteServer, &server, loguru::Verbosity_WARNING);⚡ 信号处理高级配置
Loguru的信号处理机制
Loguru提供了强大的信号处理功能,可以在程序崩溃时自动记录堆栈跟踪。通过SignalOptions结构体,您可以精确控制信号处理行为:
struct SignalOptions { bool unsafe_signal_handler = true; // 是否启用不安全但有用的信号处理 bool sigabrt = true; // 捕获SIGABRT bool sigbus = true; // 捕获SIGBUS bool sigfpe = true; // 捕获SIGFPE bool sigill = true; // 捕获SIGILL bool sigint = true; // 捕获SIGINT bool sigsegv = true; // 捕获SIGSEGV bool sigterm = true; // 捕获SIGTERM };自定义信号处理配置
在初始化Loguru时配置信号处理:
int main(int argc, char* argv[]) { loguru::Options options; // 自定义信号处理选项 options.signal_options.unsafe_signal_handler = true; // 启用堆栈跟踪 options.signal_options.sigabrt = true; // 捕获abort信号 options.signal_options.sigsegv = true; // 捕获段错误 options.signal_options.sigint = false; // 不捕获Ctrl+C loguru::init(argc, argv, options); // ... 程序逻辑 return 0; }完全禁用信号处理
如果您希望完全控制信号处理:
int main(int argc, char* argv[]) { loguru::Options options; options.signal_options = loguru::SignalOptions::none(); // 禁用所有信号处理 loguru::init(argc, argv, options); // 现在您可以安装自己的信号处理器 // ... }🚨 致命错误处理器
自定义致命错误处理
当发生致命错误(如断言失败)时,Loguru允许您自定义处理逻辑:
void my_fatal_handler(const loguru::Message& message) { // 1. 记录到特殊文件 std::ofstream fatal_log("fatal_errors.log", std::ios::app); fatal_log << "FATAL ERROR: " << message.prefix << message.message << std::endl; // 2. 发送警报 sendAlertToAdmin("程序发生致命错误"); // 3. 可以选择抛出异常而不是终止程序 throw std::runtime_error(std::string(message.prefix) + message.message); } // 设置致命错误处理器 loguru::set_fatal_handler(my_fatal_handler);实际应用场景
- 游戏开发:在游戏崩溃时保存玩家进度和调试信息
- 服务器应用:在服务崩溃时发送通知并尝试优雅重启
- 桌面应用:显示友好的错误对话框而不是直接崩溃
📊 回调函数管理
动态管理回调函数
Loguru提供了完整的回调管理API:
// 添加回调 loguru::add_callback("network_logger", networkHandler, &network, loguru::Verbosity_INFO); // 移除特定回调 loguru::remove_callback("network_logger"); // 移除所有回调 loguru::remove_all_callbacks(); // 获取当前致命错误处理器 auto handler = loguru::get_fatal_handler();回调函数的最佳实践
- 线程安全:Loguru的回调调用是线程安全的,但您的回调函数也需要保证线程安全
- 性能考虑:回调函数应尽可能高效,避免阻塞操作
- 错误处理:回调函数中的异常可能会被Loguru捕获并忽略
- 资源管理:使用
close_handler_t来清理资源
🔄 日志级别自定义回调
自定义日志级别名称
您可以为特定的日志级别定义自定义名称:
const char* my_verbosity_name(loguru::Verbosity verbosity) { switch (verbosity) { case -3: return "💀致命"; case -2: return "❌错误"; case -1: return "⚠️警告"; case 0: return "ℹ️信息"; case 1: return "🔍调试1"; case 2: return "🔍调试2"; default: return nullptr; // 使用默认名称 } } // 设置自定义名称回调 loguru::set_verbosity_to_name_callback(my_verbosity_name);自定义名称到日志级别的映射
loguru::Verbosity my_name_to_verbosity(const char* name) { if (strcmp(name, "致命") == 0) return loguru::Verbosity_FATAL; if (strcmp(name, "错误") == 0) return loguru::Verbosity_ERROR; if (strcmp(name, "警告") == 0) return loguru::Verbosity_WARNING; if (strcmp(name, "信息") == 0) return loguru::Verbosity_INFO; return loguru::Verbosity_INVALID; // 未识别 } loguru::set_name_to_verbosity_callback(my_name_to_verbosity);🎨 实际项目集成示例
完整的项目配置示例
#include <loguru.hpp> #include <iostream> #include <fstream> class LogManager { public: static void init(int argc, char* argv[]) { // 配置信号处理 loguru::Options options; options.signal_options.sigabrt = true; options.signal_options.sigsegv = true; options.signal_options.unsafe_signal_handler = true; loguru::init(argc, argv, options); // 添加文件日志 loguru::add_file("app.log", loguru::Truncate, loguru::Verbosity_MAX); // 添加自定义回调 loguru::add_callback("console", consoleHandler, nullptr, loguru::Verbosity_INFO); // 设置致命错误处理器 loguru::set_fatal_handler(fatalHandler); } private: static void consoleHandler(void*, const loguru::Message& msg) { // 控制台特殊格式化 std::cout << "[" << msg.filename << ":" << msg.line << "] " << msg.message << std::endl; } static void fatalHandler(const loguru::Message& msg) { // 记录致命错误到单独文件 std::ofstream fatal_log("fatal.log", std::ios::app); fatal_log << "=== FATAL ERROR ===" << std::endl; fatal_log << "Time: " << msg.preamble << std::endl; fatal_log << "Message: " << msg.message << std::endl; fatal_log << "==================" << std::endl; } };📝 总结与最佳实践
Loguru回调函数的核心优势
- 灵活性:完全控制日志处理流程
- 可扩展性:轻松集成到现有系统中
- 性能:轻量级设计,不影响程序性能
- 安全性:线程安全的回调机制
使用建议
✅推荐做法:
- 为不同的日志目的地使用不同的回调函数
- 在回调函数中处理资源清理
- 使用适当的日志级别过滤
- 在生产环境中启用信号处理以捕获崩溃信息
❌避免做法:
- 在回调函数中执行耗时操作
- 在回调函数中抛出未处理的异常
- 过度使用自定义日志级别名称
性能优化技巧
- 批量处理:在高频日志场景下,考虑批量发送到远程服务器
- 异步处理:使用队列将日志处理移到后台线程
- 级别过滤:合理设置回调的verbosity级别,避免不必要的处理
- 资源复用:在回调之间共享资源(如网络连接)
通过掌握Loguru的回调函数和信号处理机制,您可以构建出既强大又灵活的日志系统,满足各种复杂的应用场景需求。无论是简单的控制台应用还是复杂的分布式系统,Loguru都能提供恰到好处的日志解决方案。🚀
记住,好的日志系统不仅是记录工具,更是调试和监控的利器。善用Loguru的高级功能,让您的程序更加健壮可靠!
【免费下载链接】loguruA lightweight C++ logging library项目地址: https://gitcode.com/gh_mirrors/log/loguru
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考