mirror of
https://github.com/juzzlin/Heimer.git
synced 2025-06-11 00:05:21 +08:00
Update SimpleLogger
This commit is contained in:
parent
5b3615cdae
commit
9ffe90f9da
@ -1,3 +1,19 @@
|
|||||||
|
2.0.0
|
||||||
|
=====
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
* Fix GitHub Issue #5: Add support for tags
|
||||||
|
* Add support for customized timestamp format
|
||||||
|
* Add ISODateTime option to TimestampMode
|
||||||
|
|
||||||
|
Other:
|
||||||
|
|
||||||
|
* Add SimpleLogger::setTimestampSeparator()
|
||||||
|
* Rename init() to initialize()
|
||||||
|
* Rename Logger to SimpleLogger
|
||||||
|
* Require C++17
|
||||||
|
|
||||||
1.4.0
|
1.4.0
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
project(SimpleLogger)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
cmake_policy(VERSION 3.10)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8.12)
|
project(SimpleLogger)
|
||||||
cmake_policy(VERSION 2.8.12)
|
|
||||||
|
|
||||||
option(BUILD_TESTS "Build unit tests" ON)
|
option(BUILD_TESTS "Build unit tests" ON)
|
||||||
|
|
||||||
@ -12,16 +12,11 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
MinSizeRel." FORCE)
|
MinSizeRel." FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
||||||
|
|
||||||
# CMAKE_CXX_STANDARD supported only by versions >= 3.1
|
|
||||||
if (CMAKE_VERSION VERSION_LESS "3.1")
|
|
||||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
|
|
||||||
endif ()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(LIBRARY_NAME "SimpleLogger")
|
set(LIBRARY_NAME "SimpleLogger")
|
||||||
|
49
src/contrib/SimpleLogger/Jenkinsfile
vendored
49
src/contrib/SimpleLogger/Jenkinsfile
vendored
@ -1,31 +1,34 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent none
|
||||||
stages {
|
stages {
|
||||||
stage('CMake Debug build') {
|
stage('Matrix Build') {
|
||||||
agent {
|
matrix {
|
||||||
docker {
|
axes {
|
||||||
image 'juzzlin/qt5-18.04:latest'
|
axis {
|
||||||
args '--privileged -t -v $WORKSPACE:/SimpleLogger'
|
name 'IMAGE'
|
||||||
|
values 'qt5-20.04', 'qt6-22.04', 'qt6-24.04'
|
||||||
|
}
|
||||||
|
axis {
|
||||||
|
name 'BUILD_TYPE'
|
||||||
|
values 'Debug', 'Release'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
stages {
|
||||||
steps {
|
stage('Build and Test') {
|
||||||
sh "mkdir -p build-debug"
|
agent any
|
||||||
sh "cd build-debug && cmake -DCMAKE_BUILD_TYPE=Debug .."
|
steps {
|
||||||
sh "cd build-debug && cmake --build . --target all -- -j3 && ctest"
|
script {
|
||||||
}
|
docker.image("juzzlin/${IMAGE}:latest").inside('--privileged -t -v $WORKSPACE:/Argengine') {
|
||||||
}
|
def buildDir = "build-${BUILD_TYPE.toLowerCase()}-${IMAGE}"
|
||||||
stage('CMake Release build') {
|
sh "mkdir -p ${buildDir}"
|
||||||
agent {
|
sh "cd ${buildDir} && cmake -GNinja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .."
|
||||||
docker {
|
sh "cd ${buildDir} && cmake --build . && ctest"
|
||||||
image 'juzzlin/qt5-18.04:latest'
|
}
|
||||||
args '--privileged -t -v $WORKSPACE:/SimpleLogger'
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
|
||||||
sh "mkdir -p build-release"
|
|
||||||
sh "cd build-release && cmake -DCMAKE_BUILD_TYPE=Release .."
|
|
||||||
sh "cd build-release && cmake --build . --target all -- -j3 && ctest"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ Looking for a simple logger for your C++ project? `SimpleLogger` might be for yo
|
|||||||
|
|
||||||
* Based on RAII
|
* Based on RAII
|
||||||
* Configurable level symbols
|
* Configurable level symbols
|
||||||
* Datetime / EPOCH timestamps
|
* Datetime, ISO Datetime, EPOCH, and custom timestamp formats
|
||||||
* Logging levels: `Trace`, `Debug`, `Info`, `Warning`, `Error`, `Fatal`
|
* Logging levels: `Trace`, `Debug`, `Info`, `Warning`, `Error`, `Fatal`
|
||||||
* Log to file and/or console
|
* Log to file and/or console
|
||||||
* Thread-safe
|
* Thread-safe
|
||||||
@ -34,7 +34,7 @@ include_directories(SimpleLogger/src)
|
|||||||
Link to the library:
|
Link to the library:
|
||||||
|
|
||||||
```
|
```
|
||||||
target_link_libraries(${YOUR_TARGET_NAME} SimpleLogger)
|
target_link_libraries(${YOUR_TARGET_NAME} SimpleLogger_static)
|
||||||
```
|
```
|
||||||
|
|
||||||
In your code:
|
In your code:
|
||||||
@ -78,7 +78,7 @@ Outputs something like this:
|
|||||||
```
|
```
|
||||||
using juzzlin::L;
|
using juzzlin::L;
|
||||||
|
|
||||||
L::init("/tmp/myLog.txt");
|
L::initialize("/tmp/myLog.txt");
|
||||||
|
|
||||||
L().info() << "Something happened";
|
L().info() << "Something happened";
|
||||||
```
|
```
|
||||||
@ -88,7 +88,7 @@ L().info() << "Something happened";
|
|||||||
```
|
```
|
||||||
using juzzlin::L;
|
using juzzlin::L;
|
||||||
|
|
||||||
L::init("/tmp/myLog.txt");
|
L::initialize("/tmp/myLog.txt");
|
||||||
L::enableEchoMode(false);
|
L::enableEchoMode(false);
|
||||||
|
|
||||||
L().info() << "Something happened";
|
L().info() << "Something happened";
|
||||||
@ -111,6 +111,20 @@ Outputs something like this:
|
|||||||
|
|
||||||
`Sat Oct 13 22:38:42 2018 D: A debug thing happened`
|
`Sat Oct 13 22:38:42 2018 D: A debug thing happened`
|
||||||
|
|
||||||
|
## Log with a tag
|
||||||
|
|
||||||
|
```
|
||||||
|
using juzzlin::L;
|
||||||
|
|
||||||
|
L::setLoggingLevel(L::Level::Info);
|
||||||
|
|
||||||
|
L("MyTag").info() << "Something happened";
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs something like this:
|
||||||
|
|
||||||
|
`Sat Oct 13 22:38:42 2018 I: MyTag: Something happened`
|
||||||
|
|
||||||
## Set custom level symbols
|
## Set custom level symbols
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -126,14 +140,15 @@ Outputs something like this:
|
|||||||
|
|
||||||
`Sat Oct 13 22:38:42 2018 <DEBUG> A debug thing happened`
|
`Sat Oct 13 22:38:42 2018 <DEBUG> A debug thing happened`
|
||||||
|
|
||||||
## Set timestamp mode and optional custom separator
|
## Set timestamp mode and optional timestamp separator
|
||||||
|
|
||||||
Possible modes: `None`, `EpochSeconds`, `EpochMilliseconds`, `EpochMicroseconds`, `DateTime`.
|
Possible timestamp modes: `None`, `EpochSeconds`, `EpochMilliseconds`, `EpochMicroseconds`, `DateTime`, `ISODateTime`.
|
||||||
|
|
||||||
```
|
```
|
||||||
using juzzlin::L;
|
using juzzlin::L;
|
||||||
|
|
||||||
L::setTimestampMode(L::TimestampMode::EpochMilliseconds, " ## ");
|
L::setTimestampMode(L::TimestampMode::EpochMilliseconds);
|
||||||
|
L::setTimestampSeparator(" ## ");
|
||||||
|
|
||||||
L().info() << "Something happened";
|
L().info() << "Something happened";
|
||||||
```
|
```
|
||||||
@ -142,6 +157,23 @@ Outputs something like this:
|
|||||||
|
|
||||||
`1562955750677 ## I: Something happened`
|
`1562955750677 ## I: Something happened`
|
||||||
|
|
||||||
|
## Set custom timestamp format
|
||||||
|
|
||||||
|
By setting a custom timestamp format the timestamp mode is set to `Custom`:
|
||||||
|
|
||||||
|
```
|
||||||
|
using juzzlin::L;
|
||||||
|
|
||||||
|
L::setCustomTimestampFormat("%H:%M:%S_%Y-%m-%d");
|
||||||
|
L::setTimestampSeparator(" ## ");
|
||||||
|
|
||||||
|
L().info() << "Something happened";
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs something like this:
|
||||||
|
|
||||||
|
`12:34:58_2024-07-06 ## I: Something happened`
|
||||||
|
|
||||||
## Set custom output stream
|
## Set custom output stream
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -153,7 +185,7 @@ L::setStream(L::Level::Info, ssI);
|
|||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
C++11
|
C++17
|
||||||
|
|
||||||
# Licence
|
# Licence
|
||||||
|
|
||||||
|
@ -27,338 +27,398 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
|
||||||
#include <QDebug>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace juzzlin {
|
namespace juzzlin {
|
||||||
|
|
||||||
class Logger::Impl
|
class SimpleLogger::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Impl();
|
Impl();
|
||||||
|
|
||||||
|
Impl(const std::string & tag);
|
||||||
|
|
||||||
~Impl();
|
~Impl();
|
||||||
|
|
||||||
std::ostringstream & trace();
|
std::ostringstream & traceStream();
|
||||||
|
|
||||||
std::ostringstream & debug();
|
std::ostringstream & debugStream();
|
||||||
|
|
||||||
std::ostringstream & info();
|
std::ostringstream & infoStream();
|
||||||
|
|
||||||
std::ostringstream & warning();
|
std::ostringstream & warningStream();
|
||||||
|
|
||||||
std::ostringstream & error();
|
std::ostringstream & errorStream();
|
||||||
|
|
||||||
std::ostringstream & fatal();
|
std::ostringstream & fatalStream();
|
||||||
|
|
||||||
static void enableEchoMode(bool enable);
|
static void enableEchoMode(bool enable);
|
||||||
|
|
||||||
static void setLevelSymbol(Logger::Level level, std::string symbol);
|
static void setLevelSymbol(SimpleLogger::Level level, std::string symbol);
|
||||||
|
|
||||||
static void setLoggingLevel(Logger::Level level);
|
static void setLoggingLevel(SimpleLogger::Level level);
|
||||||
|
|
||||||
static void setTimestampMode(Logger::TimestampMode timestampMode, std::string separator);
|
static void setCustomTimestampFormat(std::string format);
|
||||||
|
|
||||||
|
static void setTimestampMode(SimpleLogger::TimestampMode timestampMode);
|
||||||
|
|
||||||
|
static void setTimestampSeparator(std::string separator);
|
||||||
|
|
||||||
static void setStream(Level level, std::ostream & stream);
|
static void setStream(Level level, std::ostream & stream);
|
||||||
|
|
||||||
static void init(std::string filename, bool append);
|
static void initialize(std::string filename, bool append);
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
std::ostringstream & getStream(Logger::Level level);
|
std::ostringstream & prepareStreamForLoggingLevel(SimpleLogger::Level level);
|
||||||
|
|
||||||
void prefixTimestamp();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string currentDateTime(std::chrono::time_point<std::chrono::system_clock> now, const std::string & dateTimeFormat) const;
|
||||||
|
|
||||||
|
void flushFileIfOpen();
|
||||||
|
|
||||||
|
void flushEchoIfEnabled();
|
||||||
|
|
||||||
|
void prefixWithLevelAndTag(SimpleLogger::Level level);
|
||||||
|
|
||||||
|
void prefixWithTimestamp();
|
||||||
|
|
||||||
|
bool shouldFlush() const;
|
||||||
|
|
||||||
static bool m_echoMode;
|
static bool m_echoMode;
|
||||||
|
|
||||||
static Logger::Level m_level;
|
static SimpleLogger::Level m_level;
|
||||||
|
|
||||||
static Logger::TimestampMode m_timestampMode;
|
static SimpleLogger::TimestampMode m_timestampMode;
|
||||||
|
|
||||||
static std::string m_timestampSeparator;
|
static std::string m_timestampSeparator;
|
||||||
|
|
||||||
static std::ofstream m_fout;
|
static std::string m_customTimestampFormat;
|
||||||
|
|
||||||
using SymbolMap = std::map<Logger::Level, std::string>;
|
static std::ofstream m_fileStream;
|
||||||
|
|
||||||
|
using SymbolMap = std::map<SimpleLogger::Level, std::string>;
|
||||||
static SymbolMap m_symbols;
|
static SymbolMap m_symbols;
|
||||||
|
|
||||||
using StreamMap = std::map<Logger::Level, std::ostream *>;
|
using StreamMap = std::map<SimpleLogger::Level, std::ostream *>;
|
||||||
static StreamMap m_streams;
|
static StreamMap m_streams;
|
||||||
|
|
||||||
static std::recursive_mutex m_mutex;
|
static std::recursive_mutex m_mutex;
|
||||||
|
|
||||||
Logger::Level m_activeLevel = Logger::Level::Info;
|
SimpleLogger::Level m_activeLevel = SimpleLogger::Level::Info;
|
||||||
|
|
||||||
std::lock_guard<std::recursive_mutex> m_lock;
|
std::lock_guard<std::recursive_mutex> m_lock;
|
||||||
|
|
||||||
std::ostringstream m_oss;
|
std::string m_tag;
|
||||||
|
|
||||||
|
std::ostringstream m_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Logger::Impl::m_echoMode = true;
|
bool SimpleLogger::Impl::m_echoMode = true;
|
||||||
|
|
||||||
Logger::Level Logger::Impl::m_level = Logger::Level::Info;
|
SimpleLogger::Level SimpleLogger::Impl::m_level = SimpleLogger::Level::Info;
|
||||||
|
|
||||||
Logger::TimestampMode Logger::Impl::m_timestampMode = Logger::TimestampMode::DateTime;
|
SimpleLogger::TimestampMode SimpleLogger::Impl::m_timestampMode = SimpleLogger::TimestampMode::DateTime;
|
||||||
|
|
||||||
std::string Logger::Impl::m_timestampSeparator = ": ";
|
std::string SimpleLogger::Impl::m_timestampSeparator = ": ";
|
||||||
|
|
||||||
std::ofstream Logger::Impl::m_fout;
|
std::string SimpleLogger::Impl::m_customTimestampFormat;
|
||||||
|
|
||||||
|
std::ofstream SimpleLogger::Impl::m_fileStream;
|
||||||
|
|
||||||
// Default level symbols
|
// Default level symbols
|
||||||
Logger::Impl::SymbolMap Logger::Impl::m_symbols = {
|
SimpleLogger::Impl::SymbolMap SimpleLogger::Impl::m_symbols = {
|
||||||
{Logger::Level::Trace, "T:"},
|
{ SimpleLogger::Level::Trace, "T:" },
|
||||||
{Logger::Level::Debug, "D:"},
|
{ SimpleLogger::Level::Debug, "D:" },
|
||||||
{Logger::Level::Info, "I:"},
|
{ SimpleLogger::Level::Info, "I:" },
|
||||||
{Logger::Level::Warning, "W:"},
|
{ SimpleLogger::Level::Warning, "W:" },
|
||||||
{Logger::Level::Error, "E:"},
|
{ SimpleLogger::Level::Error, "E:" },
|
||||||
{Logger::Level::Fatal, "F:"}
|
{ SimpleLogger::Level::Fatal, "F:" }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default streams
|
// Default streams
|
||||||
Logger::Impl::StreamMap Logger::Impl::m_streams = {
|
SimpleLogger::Impl::StreamMap SimpleLogger::Impl::m_streams = {
|
||||||
{Logger::Level::Trace, &std::cout},
|
{ SimpleLogger::Level::Trace, &std::cout },
|
||||||
{Logger::Level::Debug, &std::cout},
|
{ SimpleLogger::Level::Debug, &std::cout },
|
||||||
{Logger::Level::Info, &std::cout},
|
{ SimpleLogger::Level::Info, &std::cout },
|
||||||
{Logger::Level::Warning, &std::cerr},
|
{ SimpleLogger::Level::Warning, &std::cerr },
|
||||||
{Logger::Level::Error, &std::cerr},
|
{ SimpleLogger::Level::Error, &std::cerr },
|
||||||
{Logger::Level::Fatal, &std::cerr}
|
{ SimpleLogger::Level::Fatal, &std::cerr }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::recursive_mutex Logger::Impl::m_mutex;
|
std::recursive_mutex SimpleLogger::Impl::m_mutex;
|
||||||
|
|
||||||
Logger::Impl::Impl()
|
SimpleLogger::Impl::Impl()
|
||||||
: m_lock(Logger::Impl::m_mutex)
|
: m_lock(m_mutex)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Impl::~Impl()
|
SimpleLogger::Impl::Impl(const std::string & tag)
|
||||||
|
: m_lock(m_mutex)
|
||||||
|
, m_tag(tag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleLogger::Impl::~Impl()
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::enableEchoMode(bool enable)
|
void SimpleLogger::Impl::enableEchoMode(bool enable)
|
||||||
{
|
{
|
||||||
Impl::m_echoMode = enable;
|
m_echoMode = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::getStream(Logger::Level level)
|
std::ostringstream & SimpleLogger::Impl::prepareStreamForLoggingLevel(SimpleLogger::Level level)
|
||||||
{
|
{
|
||||||
m_activeLevel = level;
|
m_activeLevel = level;
|
||||||
Impl::prefixTimestamp();
|
prefixWithTimestamp();
|
||||||
m_oss << Impl::m_symbols[level] << " ";
|
prefixWithLevelAndTag(level);
|
||||||
return m_oss;
|
return m_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::setLevelSymbol(Level level, std::string symbol)
|
void SimpleLogger::Impl::setLevelSymbol(Level level, std::string symbol)
|
||||||
{
|
{
|
||||||
Impl::m_symbols[level] = symbol;
|
m_symbols[level] = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::setLoggingLevel(Logger::Level level)
|
void SimpleLogger::Impl::setLoggingLevel(SimpleLogger::Level level)
|
||||||
{
|
{
|
||||||
Impl::m_level = level;
|
m_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::setTimestampMode(TimestampMode timestampMode, std::string separator)
|
void SimpleLogger::Impl::setCustomTimestampFormat(std::string customTimestampFormat)
|
||||||
{
|
{
|
||||||
Impl::m_timestampMode = timestampMode;
|
m_customTimestampFormat = customTimestampFormat;
|
||||||
Impl::m_timestampSeparator = separator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::prefixTimestamp()
|
void SimpleLogger::Impl::setTimestampMode(TimestampMode timestampMode)
|
||||||
{
|
{
|
||||||
std::string timeStr;
|
m_timestampMode = timestampMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::Impl::setTimestampSeparator(std::string separator)
|
||||||
|
{
|
||||||
|
m_timestampSeparator = separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SimpleLogger::Impl::currentDateTime(std::chrono::time_point<std::chrono::system_clock> now, const std::string & dateTimeFormat) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
const auto rawTime = std::chrono::system_clock::to_time_t(now);
|
||||||
|
oss << std::put_time(std::localtime(&rawTime), dateTimeFormat.c_str());
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::Impl::prefixWithLevelAndTag(SimpleLogger::Level level)
|
||||||
|
{
|
||||||
|
m_message << m_symbols[level] << (!m_tag.empty() ? " " + m_tag + ":" : "") << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::Impl::prefixWithTimestamp()
|
||||||
|
{
|
||||||
|
std::string timestamp;
|
||||||
|
|
||||||
using std::chrono::duration_cast;
|
using std::chrono::duration_cast;
|
||||||
using std::chrono::system_clock;
|
using std::chrono::system_clock;
|
||||||
|
|
||||||
switch (Impl::m_timestampMode)
|
switch (m_timestampMode) {
|
||||||
{
|
case SimpleLogger::TimestampMode::None:
|
||||||
case Logger::TimestampMode::None:
|
|
||||||
break;
|
break;
|
||||||
case Logger::TimestampMode::DateTime:
|
case SimpleLogger::TimestampMode::DateTime: {
|
||||||
{
|
timestamp = currentDateTime(system_clock::now(), "%a %b %e %H:%M:%S %Y");
|
||||||
time_t rawTime;
|
} break;
|
||||||
time(&rawTime);
|
case SimpleLogger::TimestampMode::ISODateTime: {
|
||||||
timeStr = ctime(&rawTime);
|
timestamp = currentDateTime(system_clock::now(), "%Y-%m-%dT%H:%M:%S");
|
||||||
timeStr.erase(timeStr.length() - 1);
|
} break;
|
||||||
}
|
case SimpleLogger::TimestampMode::EpochSeconds:
|
||||||
|
timestamp = std::to_string(duration_cast<std::chrono::seconds>(system_clock::now().time_since_epoch()).count());
|
||||||
break;
|
break;
|
||||||
case Logger::TimestampMode::EpochSeconds:
|
case SimpleLogger::TimestampMode::EpochMilliseconds:
|
||||||
timeStr = std::to_string(duration_cast<std::chrono::seconds>(system_clock::now().time_since_epoch()).count());
|
using std::chrono::duration_cast;
|
||||||
|
timestamp = std::to_string(duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||||
break;
|
break;
|
||||||
case Logger::TimestampMode::EpochMilliseconds:
|
case SimpleLogger::TimestampMode::EpochMicroseconds:
|
||||||
timeStr = std::to_string(duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count());
|
using std::chrono::duration_cast;
|
||||||
|
timestamp = std::to_string(duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count());
|
||||||
break;
|
break;
|
||||||
case Logger::TimestampMode::EpochMicroseconds:
|
case SimpleLogger::TimestampMode::Custom:
|
||||||
timeStr = std::to_string(duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count());
|
timestamp = currentDateTime(system_clock::now(), m_customTimestampFormat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timeStr.empty())
|
if (!timestamp.empty()) {
|
||||||
{
|
m_message << timestamp << m_timestampSeparator;
|
||||||
m_oss << timeStr << m_timestampSeparator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::flush()
|
bool SimpleLogger::Impl::shouldFlush() const
|
||||||
{
|
{
|
||||||
if (m_activeLevel < m_level)
|
return m_activeLevel >= m_level && !m_message.str().empty();
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_oss.str().size())
|
void SimpleLogger::Impl::flushFileIfOpen()
|
||||||
{
|
{
|
||||||
return;
|
if (m_fileStream.is_open()) {
|
||||||
|
m_fileStream << m_message.str() << std::endl;
|
||||||
|
m_fileStream.flush();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Impl::m_fout.is_open())
|
void SimpleLogger::Impl::flushEchoIfEnabled()
|
||||||
{
|
{
|
||||||
Impl::m_fout << m_oss.str() << std::endl;
|
if (m_echoMode) {
|
||||||
Impl::m_fout.flush();
|
if (auto && stream = m_streams[m_activeLevel]; stream) {
|
||||||
}
|
*stream << m_message.str() << std::endl;
|
||||||
|
|
||||||
if (Impl::m_echoMode)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_ANDROID
|
|
||||||
qDebug() << m_oss.str().c_str();
|
|
||||||
#else
|
|
||||||
auto stream = Impl::m_streams[m_activeLevel];
|
|
||||||
if (stream) {
|
|
||||||
*stream << m_oss.str() << std::endl;
|
|
||||||
stream->flush();
|
stream->flush();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::init(std::string filename, bool append)
|
void SimpleLogger::Impl::flush()
|
||||||
{
|
{
|
||||||
if (!filename.empty())
|
if (shouldFlush()) {
|
||||||
{
|
flushFileIfOpen();
|
||||||
Impl::m_fout.open(filename, append ? std::ofstream::out | std::ofstream::app : std::ofstream::out);
|
flushEchoIfEnabled();
|
||||||
if (!Impl::m_fout.is_open())
|
}
|
||||||
{
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::Impl::initialize(std::string filename, bool append)
|
||||||
|
{
|
||||||
|
if (!filename.empty()) {
|
||||||
|
m_fileStream.open(filename, append ? std::ofstream::out | std::ofstream::app : std::ofstream::out);
|
||||||
|
if (!m_fileStream.is_open()) {
|
||||||
throw std::runtime_error("ERROR!!: Couldn't open '" + filename + "' for write.\n");
|
throw std::runtime_error("ERROR!!: Couldn't open '" + filename + "' for write.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::trace()
|
std::ostringstream & SimpleLogger::Impl::traceStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Trace);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::debug()
|
std::ostringstream & SimpleLogger::Impl::debugStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Debug);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::info()
|
std::ostringstream & SimpleLogger::Impl::infoStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Info);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::warning()
|
std::ostringstream & SimpleLogger::Impl::warningStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Warning);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::error()
|
std::ostringstream & SimpleLogger::Impl::errorStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Error);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::Impl::fatal()
|
std::ostringstream & SimpleLogger::Impl::fatalStream()
|
||||||
{
|
{
|
||||||
return getStream(Logger::Level::Fatal);
|
return prepareStreamForLoggingLevel(SimpleLogger::Level::Fatal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Impl::setStream(Level level, std::ostream & stream)
|
void SimpleLogger::Impl::setStream(Level level, std::ostream & stream)
|
||||||
{
|
{
|
||||||
Logger::Impl::m_streams[level] = &stream;
|
m_streams[level] = &stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Logger()
|
SimpleLogger::SimpleLogger()
|
||||||
: m_impl(new Logger::Impl)
|
: m_impl(std::make_unique<SimpleLogger::Impl>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::init(std::string filename, bool append)
|
SimpleLogger::SimpleLogger(const std::string & tag)
|
||||||
|
: m_impl(std::make_unique<SimpleLogger::Impl>(tag))
|
||||||
{
|
{
|
||||||
Impl::init(filename, append);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::enableEchoMode(bool enable)
|
void SimpleLogger::initialize(std::string filename, bool append)
|
||||||
|
{
|
||||||
|
Impl::initialize(filename, append);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::enableEchoMode(bool enable)
|
||||||
{
|
{
|
||||||
Impl::enableEchoMode(enable);
|
Impl::enableEchoMode(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::setLoggingLevel(Level level)
|
void SimpleLogger::setLoggingLevel(Level level)
|
||||||
{
|
{
|
||||||
Impl::setLoggingLevel(level);
|
Impl::setLoggingLevel(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::setLevelSymbol(Level level, std::string symbol)
|
void SimpleLogger::setLevelSymbol(Level level, std::string symbol)
|
||||||
{
|
{
|
||||||
Impl::setLevelSymbol(level, symbol);
|
Impl::setLevelSymbol(level, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::setTimestampMode(TimestampMode timestampMode, std::string separator)
|
void SimpleLogger::setTimestampMode(TimestampMode timestampMode)
|
||||||
{
|
{
|
||||||
Impl::setTimestampMode(timestampMode, separator);
|
Impl::setTimestampMode(timestampMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::setStream(Level level, std::ostream & stream)
|
void SimpleLogger::setCustomTimestampFormat(std::string customTimestampFormat)
|
||||||
|
{
|
||||||
|
Impl::setTimestampMode(TimestampMode::Custom);
|
||||||
|
Impl::setCustomTimestampFormat(customTimestampFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::setTimestampSeparator(std::string timestampSeparator)
|
||||||
|
{
|
||||||
|
Impl::setTimestampSeparator(timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleLogger::setStream(Level level, std::ostream & stream)
|
||||||
{
|
{
|
||||||
Impl::setStream(level, stream);
|
Impl::setStream(level, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::trace()
|
std::ostringstream & SimpleLogger::trace()
|
||||||
{
|
{
|
||||||
return m_impl->trace();
|
return m_impl->traceStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::debug()
|
std::ostringstream & SimpleLogger::debug()
|
||||||
{
|
{
|
||||||
return m_impl->debug();
|
return m_impl->debugStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::info()
|
std::ostringstream & SimpleLogger::info()
|
||||||
{
|
{
|
||||||
return m_impl->info();
|
return m_impl->infoStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::warning()
|
std::ostringstream & SimpleLogger::warning()
|
||||||
{
|
{
|
||||||
return m_impl->warning();
|
return m_impl->warningStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::error()
|
std::ostringstream & SimpleLogger::error()
|
||||||
{
|
{
|
||||||
return m_impl->error();
|
return m_impl->errorStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream & Logger::fatal()
|
std::ostringstream & SimpleLogger::fatal()
|
||||||
{
|
{
|
||||||
return m_impl->fatal();
|
return m_impl->fatalStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Logger::version()
|
std::string SimpleLogger::version()
|
||||||
{
|
{
|
||||||
return "1.4.0";
|
return "2.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::~Logger() = default;
|
SimpleLogger::~SimpleLogger() = default;
|
||||||
|
|
||||||
} // juzzlin
|
} // juzzlin
|
||||||
|
@ -22,10 +22,9 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#ifndef JUZZLIN_LOGGER_HPP
|
#ifndef JUZZLIN_SIMPLE_LOGGER_HPP
|
||||||
#define JUZZLIN_LOGGER_HPP
|
#define JUZZLIN_SIMPLE_LOGGER_HPP
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -36,14 +35,14 @@ namespace juzzlin {
|
|||||||
*
|
*
|
||||||
* using juzzlin::L;
|
* using juzzlin::L;
|
||||||
*
|
*
|
||||||
* L::init("myLog.txt");
|
* L::initialize("myLog.txt");
|
||||||
*
|
*
|
||||||
* Example logging:
|
* Example logging:
|
||||||
*
|
*
|
||||||
* L().info() << "Initialization finished.";
|
* L().info() << "Initialization finished.";
|
||||||
* L().error() << "Foo happened!";
|
* L().error() << "Foo happened!";
|
||||||
*/
|
*/
|
||||||
class Logger
|
class SimpleLogger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Level
|
enum class Level
|
||||||
@ -63,20 +62,26 @@ public:
|
|||||||
DateTime,
|
DateTime,
|
||||||
EpochSeconds,
|
EpochSeconds,
|
||||||
EpochMilliseconds,
|
EpochMilliseconds,
|
||||||
EpochMicroseconds
|
EpochMicroseconds,
|
||||||
|
ISODateTime,
|
||||||
|
Custom
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Constructor.
|
//! Constructor.
|
||||||
Logger();
|
SimpleLogger();
|
||||||
|
|
||||||
|
//! Constructor.
|
||||||
|
//! \param tag Tag that will be added to the message.
|
||||||
|
SimpleLogger(const std::string & tag);
|
||||||
|
|
||||||
//! Destructor.
|
//! Destructor.
|
||||||
~Logger();
|
~SimpleLogger();
|
||||||
|
|
||||||
/*! Initialize the logger.
|
/*! Initialize the logger.
|
||||||
* \param filename Log to filename. Disabled if empty.
|
* \param filename Log to filename. Disabled if empty.
|
||||||
* \param append The existing log will be appended if true.
|
* \param append The existing log will be appended if true.
|
||||||
* Throws on error. */
|
* Throws on error. */
|
||||||
static void init(std::string filename, bool append = false);
|
static void initialize(std::string filename, bool append = false);
|
||||||
|
|
||||||
//! Enable/disable echo mode.
|
//! Enable/disable echo mode.
|
||||||
//! \param enable Echo everything if true. Default is false.
|
//! \param enable Echo everything if true. Default is false.
|
||||||
@ -93,8 +98,15 @@ public:
|
|||||||
|
|
||||||
//! Set/enable timestamp mode.
|
//! Set/enable timestamp mode.
|
||||||
//! \param timestampMode Timestamp mode enumeration.
|
//! \param timestampMode Timestamp mode enumeration.
|
||||||
|
static void setTimestampMode(TimestampMode timestampMode);
|
||||||
|
|
||||||
|
//! Set custom timestamp format. Sets timestamp mode to TimestampMode::Custom.
|
||||||
|
//! \param customTimestampFormat Timestamp format e.g. "%Y-%m-%dT%H:%M:%S".
|
||||||
|
static void setCustomTimestampFormat(std::string customTimestampFormat);
|
||||||
|
|
||||||
|
//! Set/enable timestamp separator.
|
||||||
//! \param separator Separator string outputted after timestamp.
|
//! \param separator Separator string outputted after timestamp.
|
||||||
static void setTimestampMode(TimestampMode timestampMode, std::string separator = " ");
|
static void setTimestampSeparator(std::string separator);
|
||||||
|
|
||||||
//! Set specific stream.
|
//! Set specific stream.
|
||||||
//! \param level The level.
|
//! \param level The level.
|
||||||
@ -123,15 +135,15 @@ public:
|
|||||||
std::ostringstream & fatal();
|
std::ostringstream & fatal();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger(const Logger & r) = delete;
|
SimpleLogger(const SimpleLogger &) = delete;
|
||||||
Logger & operator=(const Logger & r) = delete;
|
SimpleLogger & operator=(const SimpleLogger &) = delete;
|
||||||
|
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_impl;
|
std::unique_ptr<Impl> m_impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
using L = Logger;
|
using L = SimpleLogger;
|
||||||
|
|
||||||
} // juzzlin
|
} // namespace juzzlin
|
||||||
|
|
||||||
#endif // JUZZLIN_LOGGER_HPP
|
#endif // JUZZLIN_SIMPLE_LOGGER_HPP
|
||||||
|
@ -22,30 +22,38 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "simple_logger.hpp"
|
#include "../../simple_logger.hpp"
|
||||||
|
|
||||||
// Don't compile asserts away
|
// Don't compile asserts away
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
int main(int, char **)
|
namespace juzzlin::FileTest {
|
||||||
|
|
||||||
|
void verifyLogFile(const std::string & logFile, const std::string & message, const std::string & timestampSeparator, int expectedLines)
|
||||||
{
|
{
|
||||||
using juzzlin::L;
|
std::ifstream fin { logFile };
|
||||||
|
assert(fin.is_open());
|
||||||
|
|
||||||
const auto logFile = "file_test.log";
|
int lines = 0;
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(fin, line)) {
|
||||||
|
assert(line.find(message) != std::string::npos);
|
||||||
|
assert(line.find(timestampSeparator) != std::string::npos);
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
|
||||||
L::init(logFile);
|
assert(lines == expectedLines);
|
||||||
L::enableEchoMode(true);
|
}
|
||||||
L::setLoggingLevel(L::Level::Trace);
|
|
||||||
|
|
||||||
|
void testAllLoggingLevels_allMessagesShouldBeFoundInFile(const std::string & logFileName, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
const std::string message = "Hello, world!";
|
const std::string message = "Hello, world!";
|
||||||
const std::string timestampSeparator = " ## ";
|
|
||||||
L::setTimestampMode(L::TimestampMode::DateTime, timestampSeparator);
|
|
||||||
|
|
||||||
L().trace() << message;
|
L().trace() << message;
|
||||||
L().debug() << message;
|
L().debug() << message;
|
||||||
@ -54,19 +62,28 @@ int main(int, char **)
|
|||||||
L().error() << message;
|
L().error() << message;
|
||||||
L().fatal() << message;
|
L().fatal() << message;
|
||||||
|
|
||||||
std::ifstream fin{logFile};
|
verifyLogFile(logFileName, message, timestampSeparator, 6);
|
||||||
assert(fin.is_open());
|
}
|
||||||
|
|
||||||
int lines = 0;
|
void initializeLoggger(const std::string & logFileName, const std::string & timestampSeparator)
|
||||||
std::string line;
|
{
|
||||||
while (std::getline(fin, line))
|
L::initialize(logFileName);
|
||||||
{
|
L::enableEchoMode(true);
|
||||||
assert(line.find(message) != std::string::npos);
|
L::setLoggingLevel(L::Level::Trace);
|
||||||
assert(line.find(timestampSeparator) != std::string::npos);
|
L::setTimestampMode(L::TimestampMode::DateTime);
|
||||||
lines++;
|
L::setTimestampSeparator(timestampSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(lines == 6);
|
} // namespace juzzlin::FileTest
|
||||||
|
|
||||||
|
int main(int, char **)
|
||||||
|
{
|
||||||
|
const std::string logFileName = "file_test.log";
|
||||||
|
const std::string timestampSeparator = " ## ";
|
||||||
|
|
||||||
|
juzzlin::FileTest::initializeLoggger(logFileName, timestampSeparator);
|
||||||
|
|
||||||
|
juzzlin::FileTest::testAllLoggingLevels_allMessagesShouldBeFoundInFile(logFileName, timestampSeparator);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -22,87 +22,377 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "simple_logger.hpp"
|
#include "../../simple_logger.hpp"
|
||||||
|
|
||||||
// Don't compile asserts away
|
// Don't compile asserts away
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
void assertMessage(std::stringstream & stream, std::string message, std::string timestampSeparator)
|
namespace juzzlin::StreamTest {
|
||||||
|
|
||||||
|
void assertString(std::stringstream & stream, const std::string & message)
|
||||||
{
|
{
|
||||||
assert(stream.str().find(message) != std::string::npos);
|
if (stream.str().find(message) == std::string::npos) {
|
||||||
assert(stream.str().find(timestampSeparator) != std::string::npos);
|
throw std::runtime_error("ERROR!!: '" + message + "' not found in '" + stream.str() + "'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertNotMessage(std::stringstream & stream, std::string message, std::string timestampSeparator)
|
void assertMessage(std::stringstream & stream, const std::string & message, const std::string & timestampSeparator)
|
||||||
{
|
{
|
||||||
assert(stream.str().find(message) == std::string::npos);
|
assertString(stream, message);
|
||||||
assert(stream.str().find(timestampSeparator) == std::string::npos);
|
assertString(stream, timestampSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int, char **)
|
void assertNotString(std::stringstream & stream, const std::string & message)
|
||||||
{
|
{
|
||||||
using juzzlin::L;
|
if (stream.str().find(message) != std::string::npos) {
|
||||||
|
throw std::runtime_error("ERROR!!: '" + message + "' was found in '" + stream.str() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
L::enableEchoMode(true);
|
void assertNotMessage(std::stringstream & stream, const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
const std::string message = "Hello, world!";
|
assertNotString(stream, message);
|
||||||
const std::string timestampSeparator = " ## ";
|
assertNotString(stream, timestampSeparator);
|
||||||
L::setTimestampMode(L::TimestampMode::DateTime, timestampSeparator);
|
}
|
||||||
|
|
||||||
|
void testFatal_noneLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
L::setLoggingLevel(L::Level::None);
|
L::setLoggingLevel(L::Level::None);
|
||||||
std::stringstream ssF;
|
std::stringstream ss;
|
||||||
L::setStream(L::Level::Fatal, ssF);
|
L::setStream(L::Level::Fatal, ss);
|
||||||
L().fatal() << message;
|
L().fatal() << message;
|
||||||
assertNotMessage(ssF, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFatal_fatalLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Fatal, ss);
|
||||||
L::setLoggingLevel(L::Level::Fatal);
|
L::setLoggingLevel(L::Level::Fatal);
|
||||||
L().fatal() << message;
|
L().fatal() << message;
|
||||||
assertMessage(ssF, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ssE;
|
void testError_higherLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
L::setStream(L::Level::Error, ssE);
|
{
|
||||||
|
L::setLoggingLevel(L::Level::Fatal);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Error, ss);
|
||||||
L().error() << message;
|
L().error() << message;
|
||||||
assertNotMessage(ssE, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testError_errorLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Error, ss);
|
||||||
L::setLoggingLevel(L::Level::Error);
|
L::setLoggingLevel(L::Level::Error);
|
||||||
L().error() << message;
|
L().error() << message;
|
||||||
assertMessage(ssE, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ssW;
|
void testWarning_higherLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
L::setStream(L::Level::Warning, ssW);
|
{
|
||||||
|
L::setLoggingLevel(L::Level::Error);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Warning, ss);
|
||||||
L().warning() << message;
|
L().warning() << message;
|
||||||
assertNotMessage(ssW, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testWarning_warningLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Warning, ss);
|
||||||
L::setLoggingLevel(L::Level::Warning);
|
L::setLoggingLevel(L::Level::Warning);
|
||||||
L().warning() << message;
|
L().warning() << message;
|
||||||
assertMessage(ssW, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ssI;
|
void testInfo_higherLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
L::setStream(L::Level::Info, ssI);
|
{
|
||||||
|
L::setLoggingLevel(L::Level::Warning);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
L().info() << message;
|
L().info() << message;
|
||||||
assertNotMessage(ssI, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testInfo_infoLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
L::setLoggingLevel(L::Level::Info);
|
L::setLoggingLevel(L::Level::Info);
|
||||||
L().info() << message;
|
L().info() << message;
|
||||||
assertMessage(ssI, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ssD;
|
void testDebug_higherLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
L::setStream(L::Level::Debug, ssD);
|
{
|
||||||
|
L::setLoggingLevel(L::Level::Info);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Debug, ss);
|
||||||
L().debug() << message;
|
L().debug() << message;
|
||||||
assertNotMessage(ssD, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDebug_debugLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Debug, ss);
|
||||||
L::setLoggingLevel(L::Level::Debug);
|
L::setLoggingLevel(L::Level::Debug);
|
||||||
L().debug() << message;
|
L().debug() << message;
|
||||||
assertMessage(ssD, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ssT;
|
void testTrace_higherLoggingLevel_shouldNotPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
L::setStream(L::Level::Trace, ssT);
|
{
|
||||||
|
L::setLoggingLevel(L::Level::Debug);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Trace, ss);
|
||||||
L().trace() << message;
|
L().trace() << message;
|
||||||
assertNotMessage(ssT, message, timestampSeparator);
|
assertNotMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTrace_traceLoggingLevel_shouldPrintMessage(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Trace, ss);
|
||||||
L::setLoggingLevel(L::Level::Trace);
|
L::setLoggingLevel(L::Level::Trace);
|
||||||
L().trace() << message;
|
L().trace() << message;
|
||||||
assertMessage(ssT, message, timestampSeparator);
|
assertMessage(ss, message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_fatalLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Fatal, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Fatal);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_errorLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Error, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Error);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_warningLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Warning, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Warning);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_infoLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Info);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_debugLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Debug, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Debug);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTag_traceLevel_shouldPrintTag(const std::string & message, const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Trace, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Trace);
|
||||||
|
const std::string tag = "TAG";
|
||||||
|
L(tag).fatal() << message;
|
||||||
|
assertMessage(ss, tag + ": " + message, timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeLogger(const std::string & timestampSeparator)
|
||||||
|
{
|
||||||
|
L::enableEchoMode(true);
|
||||||
|
L::setTimestampMode(L::TimestampMode::DateTime);
|
||||||
|
L::setTimestampSeparator(timestampSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_none_shouldNotPrintTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::None);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L::setLoggingLevel(L::Level::Info);
|
||||||
|
L().info() << message;
|
||||||
|
assertString(ss, message);
|
||||||
|
assertNotString(ss, "##");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_dateTime_shouldPrintDateTimeTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::DateTime);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Example of expected format: "Mon Jan 1 00:00:00 2021"
|
||||||
|
// Regex to match date-time format (handling single-digit day with optional space)
|
||||||
|
std::regex dateTimeRegex(R"(\w{3} \w{3} \s?\d{1,2} \d{2}:\d{2}:\d{2} \d{4} ##)");
|
||||||
|
assert(std::regex_search(ss.str(), dateTimeRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_epochSeconds_shouldPrintEpochSecondsTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::EpochSeconds);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Regex to match epoch seconds format
|
||||||
|
const std::regex epochSecondsRegex(R"(\d{10} ##)");
|
||||||
|
assert(std::regex_search(ss.str(), epochSecondsRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_epochMilliseconds_shouldPrintEpochMillisecondsTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::EpochMilliseconds);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Regex to match epoch milliseconds format
|
||||||
|
const std::regex epochMillisecondsRegex(R"(\d{13} ##)");
|
||||||
|
assert(std::regex_search(ss.str(), epochMillisecondsRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_epochMicroseconds_shouldPrintEpochMicrosecondsTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::EpochMicroseconds);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Regex to match epoch microseconds format
|
||||||
|
const std::regex epochMicrosecondsRegex(R"(\d{16} ##)");
|
||||||
|
assert(std::regex_search(ss.str(), epochMicrosecondsRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_ISODateTime_shouldPrintISODateTimeTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setTimestampMode(L::TimestampMode::ISODateTime);
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Regex to match ISO 8601 date-time format
|
||||||
|
const std::regex isoDateTimeRegex(R"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:.\d+)?Z? ##)");
|
||||||
|
assert(std::regex_search(ss.str(), isoDateTimeRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTimestampMode_Custom_shouldPrintCustomTimestamp(const std::string & message)
|
||||||
|
{
|
||||||
|
L::setCustomTimestampFormat("%H:%M:%S_%Y-%m-%d");
|
||||||
|
std::stringstream ss;
|
||||||
|
L::setStream(L::Level::Info, ss);
|
||||||
|
L().info() << message;
|
||||||
|
assert(ss.str().find(message) != std::string::npos);
|
||||||
|
|
||||||
|
// Regex to match custom format
|
||||||
|
const std::regex isoDateTimeRegex(R"(\d{2}:\d{2}:\d{2}_\d{4}-\d{2}-\d{2} ##)");
|
||||||
|
assert(std::regex_search(ss.str(), isoDateTimeRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void runTests()
|
||||||
|
{
|
||||||
|
const std::string message = "Hello world!";
|
||||||
|
const std::string timestampSeparator = " ## ";
|
||||||
|
|
||||||
|
initializeLogger(timestampSeparator);
|
||||||
|
|
||||||
|
testFatal_noneLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testFatal_fatalLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testError_higherLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testError_errorLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testWarning_higherLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testWarning_warningLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testInfo_higherLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testInfo_infoLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testDebug_higherLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testDebug_debugLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTrace_higherLoggingLevel_shouldNotPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTrace_traceLoggingLevel_shouldPrintMessage(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_fatalLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_errorLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_warningLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_infoLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_debugLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTag_traceLevel_shouldPrintTag(message, timestampSeparator);
|
||||||
|
|
||||||
|
testTimestampMode_none_shouldNotPrintTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_dateTime_shouldPrintDateTimeTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_epochSeconds_shouldPrintEpochSecondsTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_epochMilliseconds_shouldPrintEpochMillisecondsTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_epochMicroseconds_shouldPrintEpochMicrosecondsTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_ISODateTime_shouldPrintISODateTimeTimestamp(message);
|
||||||
|
|
||||||
|
testTimestampMode_Custom_shouldPrintCustomTimestamp(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace juzzlin::StreamTest
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
juzzlin::StreamTest::runTests();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,10 @@ static void initLogger()
|
|||||||
using juzzlin::L;
|
using juzzlin::L;
|
||||||
|
|
||||||
const QString logPath { QDir::tempPath() + QDir::separator() + "heimer-" + std::to_string(Utils::tsMs()).c_str() + ".log" };
|
const QString logPath { QDir::tempPath() + QDir::separator() + "heimer-" + std::to_string(Utils::tsMs()).c_str() + ".log" };
|
||||||
L::init(logPath.toStdString());
|
L::initialize(logPath.toStdString());
|
||||||
L::enableEchoMode(true);
|
L::enableEchoMode(true);
|
||||||
L::setTimestampMode(L::TimestampMode::DateTime, " ");
|
L::setTimestampMode(L::TimestampMode::ISODateTime);
|
||||||
|
L::setTimestampSeparator(" ");
|
||||||
const std::map<L::Level, std::string> symbols = {
|
const std::map<L::Level, std::string> symbols = {
|
||||||
{ L::Level::Debug, "D" },
|
{ L::Level::Debug, "D" },
|
||||||
{ L::Level::Error, "E" },
|
{ L::Level::Error, "E" },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user