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



是否有一种现有的方法可以禁用记录器依赖于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个临时字符串并分配字节。

我的目标是根据log_level 禁用每个Globals::LOGGER.logger_xxxx

我的记录器定义:

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类的多个实例(如果使用singleton,则每个级别的日志记录都可以有一个实例(。这将使获取资源变得更加复杂
  • 而且您将无法动态更改日志级别,考虑到您有set_Log_level方法,这可能是您必须的(在这种情况下,我仍然会想到一种解决方案,在该解决方案中,我将为每个级别提供不同的记录器实例,并根据当前日志级别将其委托给其中的任何一个(

【演示】

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

#include <iostream>  // cout
#include <string>
template <int log_level_ = 4>
struct Logger {
Logger(const std::string &pName_Log_File = "cmd")
{ std::cout << "Logger ctorn"; }
~Logger()
{ std::cout << "Logger dtorn"; }
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();
}

最新更新