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

Disable function calls or disable code's lines C++ in logging

是否有现有的方法来禁用依赖于 log_level 的记录器的每个代码行,而不用 ifndef 包围每个调用?

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

我的问题是,例如,即使将 log_level 设置为 3,显然也只会打印警告记录器和 less,但我的记录器的每个函数的右值参数都在消耗时间,例如:

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

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

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逻辑几乎是一样的,你可以用宏替换它。

您可以在日志级别为您的 Logger class 设置模板。这样:

  • 您将避免在日志方法中进行参数复制和级别检查。
  • 它还可以让您根据日志级别采用不同的策略(例如,一些日志到文件,一些到控制台)。
  • 另一方面,您最终会拥有 Logger class 的多个实例(如果您使用单例,则每个日志记录级别都可以有一个实例)。这将使对资源的访问更加复杂。
  • 而且你不能即时更改日志级别,考虑到你有一个 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_debug{5};
    constexpr int ll_info{4};
    constexpr int ll_warning{3};
    constexpr int ll_error{2};
    
    Logger<ll_debug> logger_debug{};
    logger_debug.log();

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