在日志记录中禁用函数调用或禁用代码行 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
方法,这对你来说可能是必须的(在那种情况下,我仍然会想到一个解决方案,我将为每个级别设置不同的记录器实例,并根据当前日志级别委托给其中任何一个。
注意:我让每个模板专业化都继承自通用模板,以尝试重用一些常见的行为,但它看起来很尴尬;所以那部分肯定需要重新考虑。
#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();
}
是否有现有的方法来禁用依赖于 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
方法,这对你来说可能是必须的(在那种情况下,我仍然会想到一个解决方案,我将为每个级别设置不同的记录器实例,并根据当前日志级别委托给其中任何一个。
注意:我让每个模板专业化都继承自通用模板,以尝试重用一些常见的行为,但它看起来很尴尬;所以那部分肯定需要重新考虑。
#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();
}