如何优雅地根据执行位置自动填充Logger成员函数的caller参数?
解决方案:自动填充Logger的caller参数
你可以通过上下文绑定的轻量包装实现自动填充caller,同时保持Logger的单例特性,以下是几种优雅的实现方式:
方案1:在Logger内部实现上下文日志类
直接扩展原Logger类,新增嵌套的上下文日志类,通过Logger的成员函数生成绑定了caller的实例,无需额外独立类:
#include <string> #include <iostream> class Logger { public: void error(const std::string& caller, const std::string& msg) { std::cout << "[ERROR][" << caller << "]: " << msg << std::endl; } void warn(const std::string& caller, const std::string& msg) { std::cout << "[WARN][" << caller << "]: " << msg << std::endl; } void info(const std::string& caller, const std::string& msg) { std::cout << "[INFO][" << caller << "]: " << msg << std::endl; } void debug(const std::string& caller, const std::string& msg) { std::cout << "[DEBUG][" << caller << "]: " << msg << std::endl; } // 嵌套的上下文日志类,自动绑定caller class ContextualLogger { private: Logger* parent_logger_; std::string caller_; public: ContextualLogger(Logger* parent, const std::string& caller) : parent_logger_(parent), caller_(caller) {} // 对外暴露不带caller的日志方法 void error(const std::string& msg) { parent_logger_->error(caller_, msg); } void warn(const std::string& msg) { parent_logger_->warn(caller_, msg); } void info(const std::string& msg) { parent_logger_->info(caller_, msg); } void debug(const std::string& msg) { parent_logger_->debug(caller_, msg); } }; // 生成绑定caller的上下文日志实例 ContextualLogger with_caller(const std::string& caller) { return ContextualLogger(this, caller); } };
使用方式
每个业务类持有ContextualLogger实例,调用日志方法时无需手动传入caller:
class Foo { private: Logger::ContextualLogger logger_; public: Foo(Logger* global_log) : logger_(global_log->with_caller("Foo")) { logger_.info("Hello"); // 自动填充caller="Foo" logger_.warn("Something needs attention"); } }; class Bar { private: Logger::ContextualLogger logger_; public: Bar(Logger* global_log) : logger_(global_log->with_caller("Bar")) { logger_.debug("Initializing Bar"); } };
方案2:独立的Logger包装器类
如果不想修改原Logger类,可以写一个轻量包装器,持有单例Logger的指针并绑定caller:
class LoggerWrapper { private: Logger* logger_; std::string caller_; public: LoggerWrapper(Logger* logger, const std::string& caller) : logger_(logger), caller_(caller) {} void error(const std::string& msg) { logger_->error(caller_, msg); } void warn(const std::string& msg) { logger_->warn(caller_, msg); } void info(const std::string& msg) { logger_->info(caller_, msg); } void debug(const std::string& msg) { logger_->debug(caller_, msg); } };
使用方式
class Foo { private: LoggerWrapper logger_; public: Foo(Logger* global_log) : logger_(global_log, "Foo") { logger_.info("Hello"); } };
方案3:Lambda绑定(适合简单场景)
如果业务类只用到少数日志级别,可以直接用lambda绑定caller,省去包装类的代码:
#include <functional> class Foo { private: std::function<void(const std::string&)> log_info; std::function<void(const std::string&)> log_warn; public: Foo(Logger* log) { log_info = [log](const std::string& msg) { log->info("Foo", msg); }; log_warn = [log](const std::string& msg) { log->warn("Foo", msg); }; log_info("Hello from Foo"); log_warn("Low memory warning"); } };
这些方案的核心都是复用同一个Logger单例,通过绑定caller的中间对象实现自动填充,完全符合你的需求。
内容的提问来源于stack exchange,提问作者SillyGoose
相关产品推荐
相关产品推荐

