Home > Blockchain >  Disable function calls or disable code's lines C in logging
Disable function calls or disable code's lines C in logging

Time:12-15

Is there an existing way to disable every code's lines of a logger depends on log_level without surrounding every call by an ifndef ?

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

My problem is that even with a log_level set to 3 for exemple, only the warning logger and less will be printed obviously, but the rvalue arguments of every function of my logger are consuming time, Exemple :

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

Even with a log_level = 3, this function will be called, will print nothing, but will create 2 temporary strings and assigns bytes.

My target is to disable every Globals::LOGGER.logger_xxxx line depends on the log_level

My logger definition :

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;
}

To reprensent in a better way the problem : Commenting every line of the logger in my application valgrind :

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

log_level is 0, nothing is printed but functions are called, valgrind :

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

log_level is 5, everything is printed, valgrind :

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

CodePudding user response:

A better solution would be not to call the log function when the log level is under the threshold.

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, "", "");

By the way, the logic of the logger for different levels in your code is almost the same and you can replace it with macro.

CodePudding user response:

You could template your Logger class on the log level. That way:

  • You would avoid parameter copies and level checks within your log methods.
  • It could also let you adopt different strategies depending on the log level (e.g. some logs to file, some to console).
  • On the other hand, you would end up having multiple instances of your Logger class (you could have one instance for each level of logging if you used singletons). That would make accesses to resources more complicated.
  • And you wouldn't be able to change the log level on the fly, which, considering you have a set_Log_level method, may be a must for you (in that case, I would still think of a solution where I would have different logger instances for each level, and delegate to any of them depending on the current log level).

[Demo]

NOTE: I made each template specialization inherit from the general one to try and reuse some common behaviour, but it looks quite awkward; so that part definitely needs a rethink.

#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();
}
  • Related