You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅地根据执行位置自动填充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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 20:07:05