在日志记录中禁用函数调用或禁用代码行 C++

Posted

技术标签:

【中文标题】在日志记录中禁用函数调用或禁用代码行 C++【英文标题】:Disable function calls or disable code's lines C++ in logging 【发布时间】:2022-01-17 21:40:50 【问题描述】:

是否存在禁用记录器的每个代码行的现有方法取决于 log_level 而不用 ifndef 包围每个调用?

// 5 debug
// 4 info
// 3 warning
// 2 error
// 1 fatal

我的问题是,例如,即使将 log_level 设置为 3,也只会打印警告记录器和更少,但是我的记录器的每个函数的右值参数都非常耗时,例如:

Globals::LOGGER.logger_DEBUG("MyFunction", "rvalue is " + std::to_string(8));

即使使用log_level = 3,也会调用此函数,不会打印任何内容,但会创建 2 个临时字符串并分配字节。

我的目标是禁用每个 Globals::LOGGER.logger_xxxx 行取决于 log_level

我的记录器定义:

logger.hpp:

#pragma once
#include <string>
#include <iostream>

/**
 * Class used to make logs
*/
class Logger

private:

int _Log_level;

public:
/**
 * Contructor
 * @param Log_level Level of log we want to print
 * @param Name_Log_File Name of the log.txt
*/
Logger(int pLog_level = 4, const std::string &pName_Log_File = "cmd");

/**
 * Destructor
*/
~Logger();

/**
 * Logger printed when the Log_level is 5
 * @param Class_function String that represent the class::function
 * @param Message String that we want to print
*/
void logger_DEBUG(const std::string &pClass_function, const std::string &pMessage);
/**
 * Logger printed when the Log_level is 4 and higher
 * @param Message String that we want to print
*/
void logger_INFO(const std::string &pMessage);
/**
 * Logger printed when the Log_level is 3 and higher
 * @param Class_function String that represent the class::function
 * @param Message String that we want to print
*/
void logger_WARNING(const std::string &pClass_function, const std::string &pMessage);
/**
 * Logger printed when the Log_level is 2 and higher
 * @param Class_function String that represent the class::function
 * @param Message String that we want to print
*/
void logger_ERROR(const std::string &pClass_function, const std::string &pMessage);

/**
 * Getter of the Log_level
*/
int get_Log_level();

/**
 * Setter of the Log_level
 * @param pLog_level
*/
void set_Log_level(const int &pLog_level);

private:
std::string date_time();
;

logger.cpp:

#include "Logger.hpp"
#include <filesystem>
#include <chrono>
#include <ctime>

Logger::Logger(int pLog_level, const std::string &pName_Log_File) : _Log_level(pLog_level)

    std::cout << "LOGGER created" << std::endl;
    if (pName_Log_File != "cmd")
    
        std::filesystem::create_directory("LOG");
        std::string output_file = "./LOG/" + pName_Log_File + ".txt";

        std::freopen(const_cast<char *>(output_file.c_str()), "w", stdout);
    


Logger::~Logger()


void Logger::logger_DEBUG(const std::string &pClass_function, const std::string &pMessage)

    if (this->_Log_level > 4)
    
        std::cout << "[" << this->date_time() << "]"
                  << " | [DEBUG] | [" << pClass_function << "] : " << pMessage << std::endl;
    


void Logger::logger_INFO(const std::string &pMessage)

    if (this->_Log_level > 3)
    
        std::cout << "[" << this->date_time() << "]"
                  << " | [INFO] : " << pMessage << std::endl;
    

void Logger::logger_WARNING(const std::string &pClass_function, const std::string &pMessage)

    if (this->_Log_level > 2)
    
        std::cout << "[" << this->date_time() << "]"
                  << " | [WARNING] | [" << pClass_function << "] : " << pMessage << std::endl;
    

void Logger::logger_ERROR(const std::string &pClass_function, const std::string &pMessage)

    if (this->_Log_level > 1)
    
        std::cout << "[" << this->date_time() << "]"
                  << " | [ERROR] | [" << pClass_function << "] : " << pMessage << std::endl;
    


int Logger::get_Log_level()

    return this->_Log_level;


void Logger::set_Log_level(const int &pLog_level)

    this->_Log_level = pLog_level;


std::string Logger::date_time()

    auto start = std::chrono::system_clock::now();
    std::time_t time = std::chrono::system_clock::to_time_t(start);
    auto res = std::string(std::ctime(&time));
    res.pop_back();
    return res;

为了更好地表达问题: 在我的应用程序 valgrind 中评论记录器的每一行:

total heap usage: 312,852 allocs, 312,852 frees, 7,055,259 bytes allocated

log_level 为 0,不打印,但调用函数,valgrind:

518,672 allocs, 518,672 frees, 23,963,961 bytes allocated

log_level 为 5,打印所有内容,valgrind:

total heap usage: 857,872 allocs, 857,872 frees, 30,917,557 bytes allocated

【问题讨论】:

我的代码使用这种模式:if (log(log::warning)) log &lt;&lt; log::warning("MyFunction") &lt;&lt; "rvalue is " &lt;&lt; rvalue &lt;&lt; log::endl; 这避免了在无路径旅行的情况下产生参数的开销。 (实际实现使用,ick ick,方便macrosWARN("MyFunction", "rvalue is " &lt;&lt; rvalue);。) @Eljay 它似乎可以通过唯一检查 log_level 来阻止参数的构造,我可以承认它是一个比包围除 ifndef 之外的所有内容更好的解决方案,谢谢 【参考方案1】:

更好的解决方案是在日志级别低于阈值时不调用日志函数。

enum LEVEL 
    FATAL = 1,
    ERROR = 2,
    WARN = 3,
    INFO = 4,
    DEBUG = 5,
;

#define MLOG(logger, level, func, msg) \
do  \
    if (logger.get_Log_level() >= level)  \
        logger.logger_##level(func, msg); \
     \
 while(0);

// logger.logger_INFO("", "");
MLOG(logger, INFO, "", "");

顺便说一下,你代码中不同层级的logger逻辑几乎是一样的,你可以用宏来代替。

【讨论】:

谢谢,但是,参数是在宏调用MLOG(logger, INFO, "", ""); 还是在函数调用logger.logger_##level(func, msg); 时分配在堆中的?我对宏没有信心* @Clément 不,如果不需要,没有参数副本;宏MLOG 将在编译的早期阶段被代码do ... while(0); 替换,因此,在运行时,您只需在此处支付get_Log_level() 调用和针对level 对每个logger_&lt;LEVEL&gt; 的检查打电话。 所以这就像实现 Eljay 的解决方案但对代码的阅读更透明? @Clément 没错。实际上,Eljay 在他们的评论中说他们使用了类似 if (logger.get_Log_level()... 的东西,但在最终的实现中,他们使用了方便的宏,比如 MLOG...【参考方案2】:

您可以在日志级别模板化您的 Logger 类。那样:

您可以避免在日志方法中进行参数复制和级别检查。 它还可以让您根据日志级别采用不同的策略(例如,一些日志到文件,一些到控制台)。 另一方面,您最终会拥有Logger 类的多个实例(如果您使用单例,则每个日志级别都可以有一个实例)。这会使对资源的访问变得更加复杂。 而且您将无法即时更改日志级别,考虑到您有 set_Log_level 方法,这可能是您必须的(在这种情况下,我仍然会想一个解决方案,我每个级别都有不同的记录器实例,并根据当前日志级别委托给它们中的任何一个)。

[Demo]

注意:我让每个模板特化都继承自通用模板,以尝试重用一些常见的行为,但它看起来很尴尬;所以这部分肯定需要重新考虑。

#include <iostream>  // cout
#include <string>

template <int log_level_ = 4>
struct Logger 
    Logger(const std::string &pName_Log_File = "cmd")
     std::cout << "Logger ctor\n"; 
    ~Logger()
     std::cout << "Logger dtor\n"; 
    void log(const std::string &pClass_function, const std::string &pMessage)
     std::cout << "[INFO]\n"; 
    int get_log_level()
     return log_level_; 
;

template <>
struct Logger<5> : Logger<> 
    void log()  std::cout << "[DEBUG]\n"; 
;

template <>
struct Logger<3> : Logger<> 
    void log()  std::cout << "[WARNING]\n"; 
;

template <>
struct Logger<2> : Logger<> 
    void log()  std::cout << "[ERROR]\n"; 
;

int main()

    constexpr int ll_debug5;
    constexpr int ll_info4;
    constexpr int ll_warning3;
    constexpr int ll_error2;
    
    Logger<ll_debug> logger_debug;
    logger_debug.log();

    Logger<ll_warning> logger_warning;
    logger_warning.log();

【讨论】:

感谢您的回复,我实际上只有一个记录器实例到命名空间中的全局变量中,并使用 extern 关键字来达到它,我设置日志级别取决于我的贪婪算法'm used to not "over write"。

以上是关于在日志记录中禁用函数调用或禁用代码行 C++的主要内容,如果未能解决你的问题,请参考以下文章

使用 syslog 时禁用/启用日志记录

如何在代码中启用/禁用 spdlog 日志记录?

如何禁用或重新路由 ALSA lib 日志记录

如何在根级别禁用导入的模块日志记录

禁用 CloudWatch 以监控 Lambda 函数的日志

如何在 ExtJS 4.1 中禁用网格中的某些行