在日志记录中禁用函数调用或禁用代码行 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 << log::warning("MyFunction") << "rvalue is " << rvalue << log::endl;
这避免了在无路径旅行的情况下产生参数的开销。 (实际实现使用,ick ick,方便macros:WARN("MyFunction", "rvalue is " << 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_<LEVEL>
的检查打电话。
所以这就像实现 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++的主要内容,如果未能解决你的问题,请参考以下文章