Search code examples
c++loggingheap-memoryvalgrindallocation

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


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


Solution

  • 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.