From 58ba957b1df896e9932ac3bc44490e554737ae15 Mon Sep 17 00:00:00 2001 From: Brett Date: Thu, 20 Jul 2023 22:38:17 -0400 Subject: [PATCH] working on logging 2.0. Format defined, custom tags added. --- CMakeLists.txt | 10 +- include/blt/config.h.in | 6 + include/blt/math/log_util.h | 2 +- include/blt/profiling/profiler.h | 4 +- include/blt/std/loader.h | 2 +- include/blt/std/logging.h | 209 +++++++++++++------------- include/blt/std/logging_old.h | 153 +++++++++++++++++++ include/blt/std/string.h | 2 +- src/blt/profiling/profiler.cpp | 6 +- src/blt/std/format.cpp | 2 +- src/blt/std/logging.cpp | 242 +----------------------------- src/blt/std/logging_old.cpp | 245 +++++++++++++++++++++++++++++++ src/tests/nbt_tests.h | 2 +- src/tests/profiling_tests.h | 4 +- src/tests/queue_tests.h | 8 +- 15 files changed, 529 insertions(+), 368 deletions(-) create mode 100644 include/blt/std/logging_old.h create mode 100644 src/blt/std/logging_old.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e126a88..4cc750d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) -project(BLT VERSION 0.5.2) +project(BLT VERSION 0.6.0) set(CMAKE_CXX_STANDARD 17) @@ -7,7 +7,13 @@ option(BUILD_STD "Build the BLT standard utilities." ON) option(BUILD_PROFILING "Build the BLT profiler extension" ON) option(BUILD_NBT "Build the BLT NBT + eNBT extension" ON) option(BUILD_TESTS "Build the BLT test set" OFF) -option(BLT_ENABLE_LOGGING "Enable blt::logging" ON) +option(BLT_ENABLE_LOGGING "Enable blt::logging (all macros and will safely disable logging function!)" ON) +option(BLT_ENABLE_TRACE "Enable blt::logging BLT_TRACE macro" ON) +option(BLT_ENABLE_DEBUG "Enable blt::logging BLT_DEBUG macro" ON) +option(BLT_ENABLE_INFO "Enable blt::logging BLT_INFO macro" ON) +option(BLT_ENABLE_WARN "Enable blt::logging BLT_WARN macro" ON) +option(BLT_ENABLE_ERROR "Enable blt::logging BLT_ERROR macro" ON) +option(BLT_ENABLE_FATAL "Enable blt::logging BLT_FATAL macro" ON) if(${BUILD_STD} OR ${BUILD_PROFILING}) file(GLOB_RECURSE STD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/std/*.cpp") diff --git a/include/blt/config.h.in b/include/blt/config.h.in index 1f1c348..79d71d2 100644 --- a/include/blt/config.h.in +++ b/include/blt/config.h.in @@ -3,5 +3,11 @@ #cmakedefine ZLIB_FOUND #cmakedefine BLT_ENABLE_LOGGING +#cmakedefine BLT_ENABLE_TRACE +#cmakedefine BLT_ENABLE_DEBUG +#cmakedefine BLT_ENABLE_INFO +#cmakedefine BLT_ENABLE_WARN +#cmakedefine BLT_ENABLE_ERROR +#cmakedefine BLT_ENABLE_FATAL #endif // BLT_CONFIG_H \ No newline at end of file diff --git a/include/blt/math/log_util.h b/include/blt/math/log_util.h index dec9035..e0c7dbc 100644 --- a/include/blt/math/log_util.h +++ b/include/blt/math/log_util.h @@ -9,7 +9,7 @@ #include #include -#include +#include namespace blt { diff --git a/include/blt/profiling/profiler.h b/include/blt/profiling/profiler.h index f9b3a68..fe0953a 100644 --- a/include/blt/profiling/profiler.h +++ b/include/blt/profiling/profiler.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -54,7 +54,7 @@ namespace blt::profiling { profile getProfile(const std::string& profileName); void printProfile( - const std::string& profileName, logging::LogLevel loggingLevel = logging::LogLevel::NONE, + const std::string& profileName, logging::log_level loggingLevel = logging::log_level::NONE, bool averageHistory = false ); diff --git a/include/blt/std/loader.h b/include/blt/std/loader.h index f0d288e..e754394 100644 --- a/include/blt/std/loader.h +++ b/include/blt/std/loader.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace blt::fs { std::vector getLinesFromFile(const std::string& path); diff --git a/include/blt/std/logging.h b/include/blt/std/logging.h index d2bff9c..e0e727d 100644 --- a/include/blt/std/logging.h +++ b/include/blt/std/logging.h @@ -1,138 +1,99 @@ /* - * Created by Brett on 23/01/23. + * Created by Brett on 20/07/23. * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ -#ifndef BLT_LOGGING_H -#define BLT_LOGGING_H +#ifndef BLT_TESTS_LOGGING2_H +#define BLT_TESTS_LOGGING2_H #include #include +#include #include namespace blt::logging { - enum class LogLevel { + enum class log_level { // low level TRACE0, TRACE1, TRACE2, TRACE3, // normal TRACE, DEBUG, INFO, - WARN, // errors - ERROR, FATAL, + WARN, ERROR, FATAL, // default NONE }; - class ILogHandler { - public: - virtual void log(std::string message, LogLevel level) = 0; + struct log_tag { + // tag without the ${{ or }} + std::string tag; + // function to run, log level and raw user input string are provided + std::function func; }; - struct LOG_PROPERTIES { - bool m_useColor = true; - bool m_logToConsole = true; - bool m_logToFile = true; - // include file + line data? - bool m_logWithData = true; - // print the whole path or just the file name? - bool m_logFullPath = false; - const char* m_directory = "./"; - LogLevel minLevel = LogLevel::TRACE; - - explicit constexpr LOG_PROPERTIES( - bool useColor, bool logToConsole, bool logToFile, const char* directory - ): - m_useColor(useColor), m_logToConsole(logToConsole), m_logToFile(logToFile), - m_directory(directory) {} - - explicit constexpr LOG_PROPERTIES() = default; + struct log_format { + /** + * the log output format is the string which will be used to create the log output string + * + * Available tags: + * - ${{YEAR}} // current year + * - ${{MONTH}} // current month + * - ${{DAY}} // current day + * - ${{HOUR}} // current hour + * - ${{MINUTE}} // current minute + * - ${{SECOND}} // current second + * - ${{MS}} // current millisecond + * - ${{LF}} // log level color (ASCII color code) + * - ${{R}} // ASCII color reset + * - ${{LOG_LEVEL}} // current log level + * - ${{THREAD_NAME}} // current thread name, NOTE: thread names must be set by calling "setThreadName()" from the thread in question! + * - ${{FILE}} // file from which the macro is invoked + * - ${{LINE}} // line in the from which the macro is invoked + * - ${{RAW_STR}} // raw user string without formatting applied (NOTE: format args are not provided!) + * - ${{STR}} // the user supplied string (format applied!) + */ + std::string logOutputFormat = "[${{HOUR}}:${{MINUTE}}:${{SECOND}] ${{LF}}[${{LOG_LEVEL}}]${{R}} ${{STR}}"; }; - struct logger { - LogLevel level; - - void log_internal(const std::string& str) const; - // evil hack, todo: better way -#ifdef BLT_DISABLE_LOGGING - void log(const std::string& str) const { - - } -#else - - void log(const std::string& str) const { - log_internal(str); - } - -#endif - - void flush() const; - - static void flush_all(); - }; - - static logger std_out{LogLevel::NONE}; - - static logger trace{LogLevel::TRACE}; - static logger debug{LogLevel::DEBUG}; - static logger info{LogLevel::INFO}; - static logger warn{LogLevel::WARN}; - static logger error{LogLevel::ERROR}; - static logger fatal{LogLevel::FATAL}; - - static inline logger& getLoggerFromLevel(LogLevel level) { - static logger loggerLevelDecode[11]{trace, trace, trace, trace, trace, debug, info, warn, error, fatal, std_out}; - return loggerLevelDecode[(int)level]; - } - - static inline logger& operator<<(logger& out, const std::string& str) { - out.log(str); - return out; - } + void log(log_level level, const std::string& format, const char* file, int line, ...); template::value>::type* = nullptr> - static inline logger& operator<<(logger& out, T t) { - out.log(std::to_string(t)); - return out; + inline void log(T t, log_level level, const char* file, int line) { + log(std::to_string(t), level, file, line); } - void init(LOG_PROPERTIES properties); - - void log_internal( - const std::string& format, LogLevel level, const char* file, int currentLine, - int auto_line, ... - ); - - // template voodoo magic (SFINAE, "Substitution Failure Is Not An Error") - // https://stackoverflow.com/questions/44848011/c-limit-template-type-to-numbers - // std::enable_if has a member called type if the condition is true. - // What I am doing is saying that if the condition is true then the template has a non type - // template parameter of type type* (which is void* since the default type for enable_if is void) and it's value is nullptr. - // if the condition is false, the substitution fails, and the entire template is silently (no compiler error) discarded from the overload set. - /** - * Logs an is_arithmetic type to the console. - * @tparam T type to log - * @param t the value to log - * @param level logger level to log at - * @param auto_line put a new line at the end if none exists if true - */ - template::value>::type* = nullptr> - inline void log_internal( - T t, LogLevel level, const char* file, int currentLine, int auto_line - ) { - log_internal(std::to_string(t), level, file, currentLine, auto_line); - } - - /** - * Will flush all buffers! This might cause issues with threads! - */ void flush(); - void testLogging(); + void setThreadName(const std::string& name); + } -#ifndef BLT_ENABLE_LOGGING +#ifdef BLT_LOGGING_IMPLEMENTATION + + #include + +namespace blt::logging { + + void log(const std::string& format, log_level level, const char* file, int line, ...) { + + } + + void setThreadName(const std::string& name) { + + } + + void flush() { + std::cerr.flush(); + std::cout.flush(); + } + +} + +#endif + +#if !defined(BLT_ENABLE_LOGGING) || defined(BLT_DISABLE_LOGGING) + #define BLT_LOG(format, level, ...) #define BLT_TRACE(format, ...) #define BLT_DEBUG(format, ...) #define BLT_INFO(format, ...) @@ -140,14 +101,42 @@ namespace blt::logging { #define BLT_ERROR(format, ...) #define BLT_FATAL(format, ...) #else - #ifndef BLT_DISABLE_LOGGING - #define BLT_TRACE(format, ...) log_internal(format, blt::logging::LogLevel::TRACE, __FILE__, __LINE__, true, ##__VA_ARGS__) - #define BLT_DEBUG(format, ...) log_internal(format, blt::logging::LogLevel::DEBUG, __FILE__, __LINE__, true, ##__VA_ARGS__) - #define BLT_INFO(format, ...) log_internal(format, blt::logging::LogLevel::INFO, __FILE__, __LINE__, true, ##__VA_ARGS__) - #define BLT_WARN(format, ...) log_internal(format, blt::logging::LogLevel::WARN, __FILE__, __LINE__, true, ##__VA_ARGS__) - #define BLT_ERROR(format, ...) log_internal(format, blt::logging::LogLevel::ERROR, __FILE__, __LINE__, true, ##__VA_ARGS__) - #define BLT_FATAL(format, ...) log_internal(format, blt::logging::LogLevel::FATAL, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_LOG(format, level, ...) log(format, level, __FILE__, __LINE__, ##__VA_ARGS__) + #ifndef BLT_ENABLE_TRACE + #define BLT_TRACE(format, ...) + #else + #define BLT_TRACE(format, ...) BLT_LOG(format, blt::logging::log_level::TRACE, ##__VA_ARGS__) + #endif + + #ifndef BLT_ENABLE_DEBUG + #define BLT_DEBUG(format, ...) + #else + #define BLT_DEBUG(format, ...) BLT_LOG(format, blt::logging::log_level::DEBUG, ##__VA_ARGS__) + #endif + + #ifndef BLT_ENABLE_INFO + #define BLT_INFO(format, ...) + #else + #define BLT_INFO(format, ...) BLT_LOG(format, blt::logging::log_level::INFO, ##__VA_ARGS__) + #endif + + #ifndef BLT_ENABLE_WARN + #define BLT_WARN(format, ...) + #else + #define BLT_WARN(format, ...) BLT_LOG(format, blt::logging::log_level::WARN, ##__VA_ARGS__) + #endif + + #ifndef BLT_ENABLE_ERROR + #define BLT_ERROR(format, ...) + #else + #define BLT_ERROR(format, ...) BLT_LOG(format, blt::logging::log_level::ERROR, ##__VA_ARGS__) + #endif + + #ifndef BLT_ENABLE_FATAL + #define BLT_FATAL(format, ...) + #else + #define BLT_FATAL(format, ...) BLT_LOG(format, blt::logging::log_level::FATAL, ##__VA_ARGS__) #endif #endif -#endif //BLT_LOGGING_H +#endif //BLT_TESTS_LOGGING2_H diff --git a/include/blt/std/logging_old.h b/include/blt/std/logging_old.h new file mode 100644 index 0000000..39b28e9 --- /dev/null +++ b/include/blt/std/logging_old.h @@ -0,0 +1,153 @@ +/* + * Created by Brett on 23/01/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ + +#ifndef BLT_LOGGING_H +#define BLT_LOGGING_H + +#include +#include +#include + +namespace blt::logging { + + enum class log_level { + // low level + TRACE0, TRACE1, TRACE2, TRACE3, + // normal + TRACE, DEBUG, INFO, + WARN, + // errors + ERROR, FATAL, + // default + NONE + }; + + class ILogHandler { + public: + virtual void log(std::string message, log_level level) = 0; + }; + + struct LOG_PROPERTIES { + bool m_useColor = true; + bool m_logToConsole = true; + bool m_logToFile = true; + // include file + line data? + bool m_logWithData = true; + // print the whole path or just the file name? + bool m_logFullPath = false; + const char* m_directory = "./"; + log_level minLevel = log_level::TRACE; + + explicit constexpr LOG_PROPERTIES( + bool useColor, bool logToConsole, bool logToFile, const char* directory + ): + m_useColor(useColor), m_logToConsole(logToConsole), m_logToFile(logToFile), + m_directory(directory) {} + + explicit constexpr LOG_PROPERTIES() = default; + }; + + struct logger { + log_level level; + + void log_internal(const std::string& str) const; + // evil hack, todo: better way +#ifdef BLT_DISABLE_LOGGING + void log(const std::string& str) const { + + } +#else + + void log(const std::string& str) const { + log_internal(str); + } + +#endif + + void flush() const; + + static void flush_all(); + }; + + static logger std_out{log_level::NONE}; + + static logger trace{log_level::TRACE}; + static logger debug{log_level::DEBUG}; + static logger info{log_level::INFO}; + static logger warn{log_level::WARN}; + static logger error{log_level::ERROR}; + static logger fatal{log_level::FATAL}; + + static inline logger& getLoggerFromLevel(log_level level) { + static logger loggerLevelDecode[11]{trace, trace, trace, trace, trace, debug, info, warn, error, fatal, std_out}; + return loggerLevelDecode[(int)level]; + } + + static inline logger& operator<<(logger& out, const std::string& str) { + out.log(str); + return out; + } + + template::value>::type* = nullptr> + static inline logger& operator<<(logger& out, T t) { + out.log(std::to_string(t)); + return out; + } + + void init(LOG_PROPERTIES properties); + + void log_internal( + const std::string& format, log_level level, const char* file, int currentLine, + int auto_line, ... + ); + + // template voodoo magic (SFINAE, "Substitution Failure Is Not An Error") + // https://stackoverflow.com/questions/44848011/c-limit-template-type-to-numbers + // std::enable_if has a member called type if the condition is true. + // What I am doing is saying that if the condition is true then the template has a non type + // template parameter of type type* (which is void* since the default type for enable_if is void) and it's value is nullptr. + // if the condition is false, the substitution fails, and the entire template is silently (no compiler error) discarded from the overload set. + /** + * Logs an is_arithmetic type to the console. + * @tparam T type to log + * @param t the value to log + * @param level logger level to log at + * @param auto_line put a new line at the end if none exists if true + */ + template::value>::type* = nullptr> + inline void log_internal( + T t, log_level level, const char* file, int currentLine, int auto_line + ) { + log_internal(std::to_string(t), level, file, currentLine, auto_line); + } + + /** + * Will flush all buffers! This might cause issues with threads! + */ + void flush(); + + void testLogging(); +} + +#ifndef BLT_ENABLE_LOGGING + #define BLT_TRACE(format, ...) + #define BLT_DEBUG(format, ...) + #define BLT_INFO(format, ...) + #define BLT_WARN(format, ...) + #define BLT_ERROR(format, ...) + #define BLT_FATAL(format, ...) +#else + #ifndef BLT_DISABLE_LOGGING + #define BLT_TRACE(format, ...) log_internal(format, blt::logging::log_level::TRACE, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_DEBUG(format, ...) log_internal(format, blt::logging::log_level::DEBUG, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_INFO(format, ...) log_internal(format, blt::logging::log_level::INFO, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_WARN(format, ...) log_internal(format, blt::logging::log_level::WARN, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_ERROR(format, ...) log_internal(format, blt::logging::log_level::ERROR, __FILE__, __LINE__, true, ##__VA_ARGS__) + #define BLT_FATAL(format, ...) log_internal(format, blt::logging::log_level::FATAL, __FILE__, __LINE__, true, ##__VA_ARGS__) + #endif +#endif + +#endif //BLT_LOGGING_H diff --git a/include/blt/std/string.h b/include/blt/std/string.h index 69412dc..a0d31bf 100644 --- a/include/blt/std/string.h +++ b/include/blt/std/string.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include namespace blt::string { diff --git a/src/blt/profiling/profiler.cpp b/src/blt/profiling/profiler.cpp index 3c43abc..b70d558 100644 --- a/src/blt/profiling/profiler.cpp +++ b/src/blt/profiling/profiler.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,7 +30,7 @@ namespace blt::profiling { difference(difference), name(std::move(name)), total(total) {} }; - inline void println(const std::vector&& lines, logging::LogLevel level) { + inline void println(const std::vector&& lines, logging::log_level level) { auto& logger = logging::getLoggerFromLevel(level); for (const auto& line : lines) logger << line << "\n"; @@ -104,7 +104,7 @@ namespace blt::profiling { } void printProfile( - const std::string& profileName, logging::LogLevel loggingLevel, bool averageHistory + const std::string& profileName, logging::log_level loggingLevel, bool averageHistory ) { auto& profile = profiles[profileName]; const auto& intervals = profile.intervals; diff --git a/src/blt/std/format.cpp b/src/blt/std/format.cpp index f7e5082..76ca0e4 100644 --- a/src/blt/std/format.cpp +++ b/src/blt/std/format.cpp @@ -5,7 +5,7 @@ */ #include #include -#include "blt/std/logging.h" +#include "blt/std/logging_old.h" std::string createPadding(int padAmount) { std::string padStr; diff --git a/src/blt/std/logging.cpp b/src/blt/std/logging.cpp index f046b7b..129d1f2 100644 --- a/src/blt/std/logging.cpp +++ b/src/blt/std/logging.cpp @@ -1,245 +1,7 @@ /* - * Created by Brett on 23/01/23. + * Created by Brett on 20/07/23. * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ +#define BLT_LOGGING_IMPLEMENTATION #include -#include -#include -#include -#include -#include "blt/std/string.h" -#include "blt/std/format.h" -#include -#include -#include -#include -#include -#include - -// https://en.cppreference.com/w/cpp/utility/variadic -// https://medium.com/swlh/variadic-functions-3419c287a0d2 -// https://publications.gbdirect.co.uk//c_book/chapter9/stdarg.html -// https://cplusplus.com/reference/cstdio/printf/ - -#include - - -namespace blt::logging { - - class LogFileWriter { - private: - std::string m_path; - std::fstream* output; - int currentLines = 0; - static constexpr int MAX_LINES = 100000; - public: - explicit LogFileWriter(const std::string& path): m_path(path){ - auto currentTime = system::getTimeStringFS(); - output = new std::fstream(path + currentTime + ".log", std::ios::out | std::ios::app); - if (!output->good()){ - throw std::runtime_error("Unable to open console filestream!\n"); - } - } - - void writeLine(const std::string& line){ - if (!output->good()){ - std::cerr << "There has been an error in the logging file stream!\n"; - output->clear(); - } - *output << line; - currentLines++; - if (currentLines > MAX_LINES){ - output->flush(); - output->close(); - currentLines = 0; - auto currentTime = system::getTimeStringFS(); - delete(output); - output = new std::fstream(m_path + currentTime + ".log"); - } - } - - ~LogFileWriter() { - delete(output); - } - }; - - void applyFormatting(const std::string& format, std::string& output, va_list& args){ - // args must be copied because they will be consumed by the first vsnprintf - va_list args_copy; - va_copy(args_copy, args); - - auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1; - // some compilers don't even allow you to do stack dynamic arrays. So always allocate on the heap. - // originally if the buffer was small enough the buffer was allocated on the stack because it made no sense to make a heap object - // that will be deleted a couple lines later. - scoped_buffer buffer{static_cast(buffer_size)}; - vsnprintf(*buffer, buffer_size, format.c_str(), args); - output = std::string(*buffer); - - va_end(args_copy); - } - - const char* levelColors[6] = { - "\033[97m", - "\033[36m", - "\033[92m", - "\033[93m", - "\033[91m", - "\033[97;41m" - }; - - const char* levelNames[6] = { - "[BLT_TRACE]: ", - "[BLT_DEBUG]: ", - "[BLT_INFO]: ", - "[BLT_WARN]: ", - "[BLT_ERROR]: ", - "[BLT_FATAL]: ", - }; - - // by default everything is enabled - LOG_PROPERTIES BLT_LOGGING_PROPERTIES {}; - LogFileWriter* writer = new LogFileWriter{"./"}; - - void init(LOG_PROPERTIES properties) { - if (properties.m_logToFile && BLT_LOGGING_PROPERTIES.m_directory != properties.m_directory) { - delete(writer); - writer = new LogFileWriter{properties.m_directory}; - } - if (properties.m_logToFile) - std::filesystem::create_directory(properties.m_directory); - BLT_LOGGING_PROPERTIES = properties; - } - - inline std::string filename(const std::string& path){ - if (BLT_LOGGING_PROPERTIES.m_logFullPath) - return path; - auto paths = blt::string::split(path, "/"); - auto final = paths[paths.size()-1]; - if (final == "/") - return paths[paths.size()-2]; - return final; - } - - inline void log(const std::string& str, bool hasEndingLinefeed, LogLevel level, const char* file, int currentLine, int auto_line){ - if (level < BLT_LOGGING_PROPERTIES.minLevel) - return; - std::string outputString = system::getTimeStringLog(); - - if (BLT_LOGGING_PROPERTIES.m_logWithData && currentLine >= 0) { - outputString += '['; - outputString += filename(file); - outputString += ':'; - outputString += std::to_string(currentLine); - outputString += "] "; - } - - outputString += levelNames[(int)level]; - outputString += str; - - std::string fileString = outputString; - - if (BLT_LOGGING_PROPERTIES.m_useColor) { - outputString = levelColors[(int)level] + outputString; - outputString += "\033[0m"; - } - - if (hasEndingLinefeed || auto_line) { - outputString += "\n"; - fileString += "\n"; - } - - if (BLT_LOGGING_PROPERTIES.m_logToConsole) { - if (level > LogLevel::WARN) - std::cerr << outputString; - else - std::cout << outputString; - } - - if (BLT_LOGGING_PROPERTIES.m_logToFile) { - writer->writeLine(fileString); - } - } - - void log_internal(const std::string& format, LogLevel level, const char* file, int currentLine, int auto_line, ...) { - va_list args; - va_start(args, auto_line); - - std::string formattedString; - applyFormatting(format, formattedString, args); - - bool hasEndingLinefeed = false; - - if (formattedString.length() > 0) - hasEndingLinefeed = formattedString[formattedString.length() - 1] == '\n'; - - if (hasEndingLinefeed) - formattedString = formattedString.substr(0, formattedString.length()-1); - - log(formattedString, hasEndingLinefeed, level, file, currentLine, auto_line); - - va_end(args); - } - - // stores an association between thread -> log level -> current line buffer - std::unordered_map> thread_local_strings; - - void logger::log_internal(const std::string& str) const { - auto id = std::this_thread::get_id(); - auto th_str = thread_local_strings[id][level]; - th_str += str; - - if (blt::string::contains(str, "\n")){ - // make sure new lines are properly formatted to prevent danging lines. Ie "[trace]: .... [debug]: ...." - bool hasEndingLinefeed = str[str.length()-1] == '\n'; - if (level == LogLevel::NONE) { - std::cout << th_str; - } else - logging::log(th_str, false, level, "", -1, !hasEndingLinefeed); - thread_local_strings[id][level] = ""; - } else { - thread_local_strings[id][level] = th_str; - } - } - - void flushLogger_internal(std::thread::id id, LogLevel level){ - auto th_str = thread_local_strings[id][level]; - if (th_str.empty()) - return; - bool hasEndingLinefeed = th_str[th_str.length() - 1] == '\n'; - logging::log(th_str, false, level, "", -1, !hasEndingLinefeed); - thread_local_strings[id][level] = ""; - } - - void logger::flush() const { - for (const auto& id : thread_local_strings) { - flushLogger_internal(id.first, level); - } - } - - void logger::flush_all() { - for (const auto& id : thread_local_strings) { - for (const auto& level : thread_local_strings[id.first]){ - flushLogger_internal(id.first, level.first); - } - } - } - - void flush() { - logger::flush_all(); - std::cerr.flush(); - std::cout.flush(); - } - - void testLogging() { - trace << "Trace Test!\n"; - debug << "Debug Test!\n"; - info << "Info Test!\n"; - warn << "Warn Test!\n"; - error << "Error Test!\n"; - fatal << "Fatal Test!\n"; - } -} - - diff --git a/src/blt/std/logging_old.cpp b/src/blt/std/logging_old.cpp new file mode 100644 index 0000000..a06c2e1 --- /dev/null +++ b/src/blt/std/logging_old.cpp @@ -0,0 +1,245 @@ +/* + * Created by Brett on 23/01/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ +#include +#include +#include +#include +#include +#include "blt/std/string.h" +#include "blt/std/format.h" +#include +#include +#include +#include +#include +#include + +// https://en.cppreference.com/w/cpp/utility/variadic +// https://medium.com/swlh/variadic-functions-3419c287a0d2 +// https://publications.gbdirect.co.uk//c_book/chapter9/stdarg.html +// https://cplusplus.com/reference/cstdio/printf/ + +#include + + +namespace blt::logging { + + class LogFileWriter { + private: + std::string m_path; + std::fstream* output; + int currentLines = 0; + static constexpr int MAX_LINES = 100000; + public: + explicit LogFileWriter(const std::string& path): m_path(path){ + auto currentTime = system::getTimeStringFS(); + output = new std::fstream(path + currentTime + ".log", std::ios::out | std::ios::app); + if (!output->good()){ + throw std::runtime_error("Unable to open console filestream!\n"); + } + } + + void writeLine(const std::string& line){ + if (!output->good()){ + std::cerr << "There has been an error in the logging file stream!\n"; + output->clear(); + } + *output << line; + currentLines++; + if (currentLines > MAX_LINES){ + output->flush(); + output->close(); + currentLines = 0; + auto currentTime = system::getTimeStringFS(); + delete(output); + output = new std::fstream(m_path + currentTime + ".log"); + } + } + + ~LogFileWriter() { + delete(output); + } + }; + + void applyFormatting(const std::string& format, std::string& output, va_list& args){ + // args must be copied because they will be consumed by the first vsnprintf + va_list args_copy; + va_copy(args_copy, args); + + auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1; + // some compilers don't even allow you to do stack dynamic arrays. So always allocate on the heap. + // originally if the buffer was small enough the buffer was allocated on the stack because it made no sense to make a heap object + // that will be deleted a couple lines later. + scoped_buffer buffer{static_cast(buffer_size)}; + vsnprintf(*buffer, buffer_size, format.c_str(), args); + output = std::string(*buffer); + + va_end(args_copy); + } + + const char* levelColors[6] = { + "\033[97m", + "\033[36m", + "\033[92m", + "\033[93m", + "\033[91m", + "\033[97;41m" + }; + + const char* levelNames[6] = { + "[BLT_TRACE]: ", + "[BLT_DEBUG]: ", + "[BLT_INFO]: ", + "[BLT_WARN]: ", + "[BLT_ERROR]: ", + "[BLT_FATAL]: ", + }; + + // by default everything is enabled + LOG_PROPERTIES BLT_LOGGING_PROPERTIES {}; + LogFileWriter* writer = new LogFileWriter{"./"}; + + void init(LOG_PROPERTIES properties) { + if (properties.m_logToFile && BLT_LOGGING_PROPERTIES.m_directory != properties.m_directory) { + delete(writer); + writer = new LogFileWriter{properties.m_directory}; + } + if (properties.m_logToFile) + std::filesystem::create_directory(properties.m_directory); + BLT_LOGGING_PROPERTIES = properties; + } + + inline std::string filename(const std::string& path){ + if (BLT_LOGGING_PROPERTIES.m_logFullPath) + return path; + auto paths = blt::string::split(path, "/"); + auto final = paths[paths.size()-1]; + if (final == "/") + return paths[paths.size()-2]; + return final; + } + + inline void log(const std::string& str, bool hasEndingLinefeed, log_level level, const char* file, int currentLine, int auto_line){ + if (level < BLT_LOGGING_PROPERTIES.minLevel) + return; + std::string outputString = system::getTimeStringLog(); + + if (BLT_LOGGING_PROPERTIES.m_logWithData && currentLine >= 0) { + outputString += '['; + outputString += filename(file); + outputString += ':'; + outputString += std::to_string(currentLine); + outputString += "] "; + } + + outputString += levelNames[(int)level]; + outputString += str; + + std::string fileString = outputString; + + if (BLT_LOGGING_PROPERTIES.m_useColor) { + outputString = levelColors[(int)level] + outputString; + outputString += "\033[0m"; + } + + if (hasEndingLinefeed || auto_line) { + outputString += "\n"; + fileString += "\n"; + } + + if (BLT_LOGGING_PROPERTIES.m_logToConsole) { + if (level > log_level::WARN) + std::cerr << outputString; + else + std::cout << outputString; + } + + if (BLT_LOGGING_PROPERTIES.m_logToFile) { + writer->writeLine(fileString); + } + } + + void log_internal(const std::string& format, log_level level, const char* file, int currentLine, int auto_line, ...) { + va_list args; + va_start(args, auto_line); + + std::string formattedString; + applyFormatting(format, formattedString, args); + + bool hasEndingLinefeed = false; + + if (formattedString.length() > 0) + hasEndingLinefeed = formattedString[formattedString.length() - 1] == '\n'; + + if (hasEndingLinefeed) + formattedString = formattedString.substr(0, formattedString.length()-1); + + log(formattedString, hasEndingLinefeed, level, file, currentLine, auto_line); + + va_end(args); + } + + // stores an association between thread -> log level -> current line buffer + std::unordered_map> thread_local_strings; + + void logger::log_internal(const std::string& str) const { + auto id = std::this_thread::get_id(); + auto th_str = thread_local_strings[id][level]; + th_str += str; + + if (blt::string::contains(str, "\n")){ + // make sure new lines are properly formatted to prevent danging lines. Ie "[trace]: .... [debug]: ...." + bool hasEndingLinefeed = str[str.length()-1] == '\n'; + if (level == log_level::NONE) { + std::cout << th_str; + } else + logging::log(th_str, false, level, "", -1, !hasEndingLinefeed); + thread_local_strings[id][level] = ""; + } else { + thread_local_strings[id][level] = th_str; + } + } + + void flushLogger_internal(std::thread::id id, log_level level){ + auto th_str = thread_local_strings[id][level]; + if (th_str.empty()) + return; + bool hasEndingLinefeed = th_str[th_str.length() - 1] == '\n'; + logging::log(th_str, false, level, "", -1, !hasEndingLinefeed); + thread_local_strings[id][level] = ""; + } + + void logger::flush() const { + for (const auto& id : thread_local_strings) { + flushLogger_internal(id.first, level); + } + } + + void logger::flush_all() { + for (const auto& id : thread_local_strings) { + for (const auto& level : thread_local_strings[id.first]){ + flushLogger_internal(id.first, level.first); + } + } + } + + void flush() { + logger::flush_all(); + std::cerr.flush(); + std::cout.flush(); + } + + void testLogging() { + trace << "Trace Test!\n"; + debug << "Debug Test!\n"; + info << "Info Test!\n"; + warn << "Warn Test!\n"; + error << "Error Test!\n"; + fatal << "Fatal Test!\n"; + } +} + + diff --git a/src/tests/nbt_tests.h b/src/tests/nbt_tests.h index bb2580f..45a9194 100644 --- a/src/tests/nbt_tests.h +++ b/src/tests/nbt_tests.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/tests/profiling_tests.h b/src/tests/profiling_tests.h index 485a4ad..2d49f28 100644 --- a/src/tests/profiling_tests.h +++ b/src/tests/profiling_tests.h @@ -8,7 +8,7 @@ #define BLT_TESTS_PROFILING_TESTS_H #include "blt/profiling/profiler.h" -#include "blt/std/logging.h" +#include "blt/std/logging_old.h" #include "blt/std/time.h" #include "blt/std/format.h" @@ -38,7 +38,7 @@ static void runProfilingAndTableTests() { BLT_END_INTERVAL("Help", "UnderSet" + std::to_string(i)); } - BLT_PRINT_PROFILE("Help", blt::logging::LogLevel::TRACE); + BLT_PRINT_PROFILE("Help", blt::logging::log_level::TRACE); BLT_TRACE(""); blt::string::TableFormatter formatter; diff --git a/src/tests/queue_tests.h b/src/tests/queue_tests.h index 59b6e3b..b251438 100644 --- a/src/tests/queue_tests.h +++ b/src/tests/queue_tests.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -140,9 +140,9 @@ static inline void test_queues() { fill_queues(); random_access(); - BLT_PRINT_PROFILE("Insert", blt::logging::LogLevel::INFO, true); - BLT_PRINT_PROFILE("Access", blt::logging::LogLevel::INFO, true); - BLT_PRINT_PROFILE("Random", blt::logging::LogLevel::INFO, true); + BLT_PRINT_PROFILE("Insert", blt::logging::log_level::INFO, true); + BLT_PRINT_PROFILE("Access", blt::logging::log_level::INFO, true); + BLT_PRINT_PROFILE("Random", blt::logging::log_level::INFO, true); } #endif //BLT_TESTS_QUEUE_TESTS_H