Skip to content

Commit

Permalink
- Adding option to also redirect the logs to an external file (set L…
Browse files Browse the repository at this point in the history
…OGGER_WRITE_OUTFILE to 1)

 - Outfile name can be set with LOGGER_OUTFILE_NAME_FORMAT. default: "{EXE}_{TIME}.log"
 - Outfile folder can also be set with LOGGER_OUTFILE_FOLDER. default "." -> if the folder can't be found, the log file won't be created
 - Updating the way setMaxLogLevel can be set
 - Now every printout operations are handled by the StreamBufferSupervisor
  • Loading branch information
nadrino committed Apr 26, 2021
1 parent 006493f commit 3a29ddd
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 255 deletions.
7 changes: 3 additions & 4 deletions example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include "Logger.h"


int main(){

LogInfo << "-----------------------------------------" << std::endl;
Expand Down Expand Up @@ -87,8 +86,8 @@ int main(){
LogInfo << "-----------------------------------------" << std::endl;
LogInfo << "You can set the maximum log level of a given source file." << std::endl;
LogInfo << "-----------------------------------------" << std::endl;
Logger::setMaxLogLevel(Logger::LogLevel::INFO);
LogInfo << "Here for example, we have set the max log level to INFO (4):" << std::endl;
Logger::setMaxLogLevel(LogInfo);
LogInfo << "Here for example, we have set the max log level to INFO:" << std::endl;
LogInfo << "It means than all printouts from FATAL (0) to INFO (4) will be printed," << std::endl;
LogInfo << "but not the DEBUG (5) and TRACE (6)." << std::endl;
LogWarning << "-> You can see me :) (but not the next message which is a DEBUG)" << std::endl;
Expand All @@ -97,7 +96,7 @@ int main(){
LogAlert << "* This is because simple-cpp-logger is a header-only library." << std::endl;
LogAlert << "* In fact, all (static) members of the Logger are defined within a given source file." << std::endl;
LogAlert << "* To apply this parameter globally, set the variable in your cmake file." << std::endl;
Logger::setMaxLogLevel(Logger::LogLevel::TRACE);
LogTrace.setMaxLogLevel();
LogInfo << "For a global application, set it with cmake:" << std::endl;
LogInfo << "\"-D LOGGER_MAX_LOG_LEVEL_PRINTED=6\" -> TRACE by default" << std::endl;

Expand Down
21 changes: 13 additions & 8 deletions include/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ namespace {
//! Setters
// Keep in mind that every parameter you set will be applied only in the context of the source file you're in
// It is an inherent feature as a **header-only** library
static void setMaxLogLevel(int maxLogLevel_);
static void setMaxLogLevel(const LogLevel &maxLogLevel_);
static void setMaxLogLevel(const Logger& logger_); // Example: Logger::setMaxLogLevel(LogDebug);
static void setMaxLogLevel(); // Example: LogDebug.setMaxLogLevel();
static void setEnableColors(bool enableColors_);
static void setPropagateColorsOnUserHeader(bool propagateColorsOnUserHeader_);
static void setPrefixLevel(const PrefixLevel &prefixLevel_);
Expand All @@ -74,23 +74,26 @@ namespace {
template<typename T> Logger &operator<<(const T &data);
Logger &operator<<(std::ostream &(*f)(std::ostream &));


// Macro-Related Methods
// Those intended to be called using the above preprocessor macros
Logger(const LogLevel &logLevel_, char const * fileName_, const int &lineNumber_);
virtual ~Logger();

// Deprecated (left here for compatibility)
static void setMaxLogLevel(int maxLogLevel_);
static void setMaxLogLevel(const LogLevel &maxLogLevel_);

protected:

static void buildCurrentPrefix();
static void formatUserHeaderStr(std::string &strBuffer_);

static void hookStreamBuffer();
static std::string getLogLevelColorStr(const LogLevel &selectedLogLevel_);
static std::string getLogLevelStr(const LogLevel &selectedLogLevel_);

template<typename ... Args> static void printFormat(const char *fmt_str, Args ... args );

// Setup Methods
static void setupStreamBufferSupervisor();
static void setupOutputFile();

private:

Expand All @@ -99,6 +102,7 @@ namespace {
static bool _disablePrintfLineJump_;
static bool _propagateColorsOnUserHeader_;
static bool _cleanLineBeforePrint_;
static bool _writeInOutputFile_;
static LogLevel _maxLogLevel_;
static PrefixLevel _prefixLevel_;
static std::string _userHeaderStr_;
Expand All @@ -110,9 +114,10 @@ namespace {
static int _currentLineNumber_;
static std::string _currentPrefix_;
static bool _isNewLine_;
static std::ostream& _outputStream_;
static std::mutex _loggerMutex_;
static LoggerUtils::LastCharBuffer* _lastCharKeeper_;
static LoggerUtils::StreamBufferSupervisor* _streamBufferSupervisorPtr_;
static LoggerUtils::StreamBufferSupervisor _streamBufferSupervisor_;
static std::string _outputFileName_;

};

Expand Down
134 changes: 83 additions & 51 deletions include/implementation/Logger.impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@
#include <iomanip>
#include <algorithm>
#include <memory> // For std::unique_ptr
#include <Logger.h>


#include <cstdio>
#include <iostream>
namespace {

// Setters
void Logger::setMaxLogLevel(int maxLogLevel_) {
Logger::setMaxLogLevel(static_cast<Logger::LogLevel>(maxLogLevel_));
void Logger::setMaxLogLevel(const Logger& logger_){
// _currentLogLevel_ is set by the constructor,
// so when you provide "LogDebug" as an argument the _currentLogLevel_ is automatically updated
// Stricto sensu: the argument is just a placeholder for silently updating _currentLogLevel_
_maxLogLevel_ = _currentLogLevel_;
}
void Logger::setMaxLogLevel(const LogLevel &maxLogLevel_) {
_maxLogLevel_ = maxLogLevel_;
void Logger::setMaxLogLevel(){
// same technique as other, but this time with no arguments
_maxLogLevel_ = _currentLogLevel_;
}
void Logger::setEnableColors(bool enableColors_) {
_enableColors_ = enableColors_;
Expand Down Expand Up @@ -66,37 +72,20 @@ namespace {

// User Methods
void Logger::quietLineJump() {
_outputStream_ << std::endl;
}

// C-tor D-tor
Logger::Logger(const LogLevel &logLevel_, char const *fileName_, const int &lineNumber_) {

hookStreamBuffer(); // hook the stream buffer to an object we can handle
if (logLevel_ != _currentLogLevel_) _isNewLine_ = true; // force reprinting the prefix if the verbosity has changed

// Lock while this object is created
_loggerMutex_.lock();

// static members
_currentLogLevel_ = logLevel_;
_currentFileName_ = fileName_;
_currentLineNumber_ = lineNumber_;
}
Logger::~Logger() {
_loggerMutex_.unlock();
*_streamBufferSupervisorPtr_ << std::endl;
}


//! Non-static Methods
// For printf-style calls
template<typename... TT> void Logger::operator()(const char *fmt_str, TT &&... args) {

if (_currentLogLevel_ > _maxLogLevel_) return;

Logger::printFormat(fmt_str, std::forward<TT>(args)...);
if (not _disablePrintfLineJump_ and fmt_str[strlen(fmt_str) - 1] != '\n') {
_outputStream_ << std::endl;
_isNewLine_ = true;
}
*_streamBufferSupervisorPtr_ << std::endl;
_isNewLine_ = true;
}

}
template<typename T> Logger &Logger::operator<<(const T &data) {
Expand All @@ -118,12 +107,39 @@ namespace {
// Handling std::endl
if (_currentLogLevel_ > _maxLogLevel_) return *this;

_outputStream_ << f;
*_streamBufferSupervisorPtr_ << f;
_isNewLine_ = true;

return *this;
}

// C-tor D-tor
Logger::Logger(const LogLevel &logLevel_, char const *fileName_, const int &lineNumber_) {

setupStreamBufferSupervisor(); // hook the stream buffer to an object we can handle
if (logLevel_ != _currentLogLevel_) _isNewLine_ = true; // force reprinting the prefix if the verbosity has changed

// Lock while this object is created
_loggerMutex_.lock();

// static members
_currentLogLevel_ = logLevel_;
_currentFileName_ = fileName_;
_currentLineNumber_ = lineNumber_;
}
Logger::~Logger() {
_loggerMutex_.unlock();
}

// Deprecated (left here for compatibility)
void Logger::setMaxLogLevel(int maxLogLevel_) {
Logger::setMaxLogLevel(static_cast<Logger::LogLevel>(maxLogLevel_));
}
void Logger::setMaxLogLevel(const LogLevel &maxLogLevel_) {
_maxLogLevel_ = maxLogLevel_;
}


// Protected Methods
void Logger::buildCurrentPrefix() {

Expand Down Expand Up @@ -265,17 +281,6 @@ namespace {

}
}
void Logger::hookStreamBuffer(){

if(_lastCharKeeper_ != nullptr) return;
_lastCharKeeper_ = new LoggerUtils::LastCharBuffer(); // this object can't be deleted -> that's why we can't directly override with the logger class
std::streambuf* cbuf = _outputStream_.rdbuf(); // back up cout's streambuf
_outputStream_.flush();
_lastCharKeeper_->setStreamBuffer(cbuf);
_outputStream_.rdbuf(_lastCharKeeper_); // reassign your streambuf to cout

}

template<typename ... Args> void Logger::printFormat(const char *fmt_str, Args ... args ){

std::string formattedString;
Expand Down Expand Up @@ -306,45 +311,72 @@ namespace {

// let the last line jump be handle by the user (or the parent function)
if (i_line != (slicedString.size() - 1)) {
_outputStream_ << std::endl;
*_streamBufferSupervisorPtr_ << std::endl;
}

} // for each line
} // If multiline
else{

// If '\r' is detected, trigger Newline to reprint the header
if( _lastCharKeeper_->getLastChar() == '\r' ){
if(_streamBufferSupervisorPtr_->getLastChar() == '\r' ){
// Clean the line if the option is enabled and the terminal width is measurable
if( _cleanLineBeforePrint_ and LoggerUtils::getTerminalWidth() != 0){
_outputStream_ << LoggerUtils::repeatString(" ", LoggerUtils::getTerminalWidth()-1) << "\r";
*_streamBufferSupervisorPtr_ << LoggerUtils::repeatString(" ", LoggerUtils::getTerminalWidth()-1) << "\r";
}
_isNewLine_ = true;
}

// Start printing
if(_isNewLine_ or _lastCharKeeper_->getLastChar() == '\n'){
if(_isNewLine_ or _streamBufferSupervisorPtr_->getLastChar() == '\n'){
Logger::buildCurrentPrefix();
_outputStream_ << _currentPrefix_;
*_streamBufferSupervisorPtr_ << _currentPrefix_;
_isNewLine_ = false;
}

if (_enableColors_ and _currentLogLevel_ == LogLevel::FATAL)
_outputStream_ << LoggerUtils::formatString("%s", getLogLevelColorStr(LogLevel::FATAL).c_str());
if (_enableColors_ and _currentLogLevel_ == LogLevel::FATAL){
*_streamBufferSupervisorPtr_ << LoggerUtils::formatString("%s", getLogLevelColorStr(LogLevel::FATAL).c_str());
}

_outputStream_ << formattedString;
*_streamBufferSupervisorPtr_ << formattedString;

if (_enableColors_ and _currentLogLevel_ == LogLevel::FATAL)
_outputStream_ << LoggerUtils::formatString("\033[0m");
*_streamBufferSupervisorPtr_ << LoggerUtils::formatString("\033[0m");
} // else multiline

}

// Setup Methods
void Logger::setupStreamBufferSupervisor(){

if(_streamBufferSupervisorPtr_ != nullptr) return;
_streamBufferSupervisorPtr_ = new LoggerUtils::StreamBufferSupervisor(); // this object can't be deleted -> that's why we can't directly override with the logger class
Logger::setupOutputFile();

}
void Logger::setupOutputFile(){
if( not _writeInOutputFile_ or not _outputFileName_.empty() ){
return;
}
_outputFileName_ = LOGGER_OUTFILE_FOLDER;
_outputFileName_ += "/";
_outputFileName_ += LOGGER_OUTFILE_NAME_FORMAT;
LoggerUtils::replaceSubstringInsideInputString(_outputFileName_, "{EXE}", LoggerUtils::getExecutableName());
time_t rawTime = std::time(nullptr);
struct tm timeInfo = *localtime(&rawTime);
std::stringstream ss;
ss << std::put_time(&timeInfo, "%Y%m%d_%H%M%S");
LoggerUtils::replaceSubstringInsideInputString(_outputFileName_, "{TIME}", ss.str());
_streamBufferSupervisorPtr_->openOutFileStream(_outputFileName_);
}



// Private Members
bool Logger::_enableColors_ = LOGGER_ENABLE_COLORS;
bool Logger::_propagateColorsOnUserHeader_ = LOGGER_ENABLE_COLORS_ON_USER_HEADER;
bool Logger::_cleanLineBeforePrint_ = LOGGER_CLEAR_LINE_BEFORE_PRINT;
bool Logger::_writeInOutputFile_ = LOGGER_WRITE_OUTFILE;
bool Logger::_disablePrintfLineJump_ = false;
Logger::LogLevel Logger::_maxLogLevel_(static_cast<Logger::LogLevel>(LOGGER_MAX_LOG_LEVEL_PRINTED));
Logger::PrefixLevel Logger::_prefixLevel_(static_cast<Logger::PrefixLevel>(LOGGER_PREFIX_LEVEL));
Expand All @@ -356,9 +388,9 @@ namespace {
std::string Logger::_currentFileName_;
int Logger::_currentLineNumber_{-1};
bool Logger::_isNewLine_{true};
std::ostream& Logger::_outputStream_ = std::cout;
std::mutex Logger::_loggerMutex_;
LoggerUtils::LastCharBuffer* Logger::_lastCharKeeper_{nullptr};
LoggerUtils::StreamBufferSupervisor* Logger::_streamBufferSupervisorPtr_{nullptr};
std::string Logger::_outputFileName_;

}

Expand Down
12 changes: 12 additions & 0 deletions include/implementation/LoggerParameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@
#define LOGGER_CLEAR_LINE_BEFORE_PRINT 0
#endif

#ifndef LOGGER_WRITE_OUTFILE
#define LOGGER_WRITE_OUTFILE 0
#endif

#ifndef LOGGER_OUTFILE_NAME_FORMAT
#define LOGGER_OUTFILE_NAME_FORMAT "{EXE}_{TIME}.log"
#endif

#ifndef LOGGER_OUTFILE_FOLDER
#define LOGGER_OUTFILE_FOLDER "."
#endif

#endif //SIMPLE_CPP_LOGGER_LOGGERPARAMETERS_H
Loading

0 comments on commit 3a29ddd

Please sign in to comment.