diff --git a/include/blt/profiling/profiler.h b/include/blt/profiling/profiler.h index b7d9f60..ff47163 100644 --- a/include/blt/profiling/profiler.h +++ b/include/blt/profiling/profiler.h @@ -10,6 +10,7 @@ #include #include #include +#include /** * Defines several disableable macros (#define BLT_DISABLE_PROFILING). If you do not use these macros profiling will not be disableable @@ -39,8 +40,8 @@ namespace blt::profiling { CaptureInterval getInterval(const std::string& profileName, const std::string& intervalName); Profile getProfile(const std::string& profileName); - void printProfile(const std::string& profileName, int loggingLevel = -1); - void printOrderedProfile(const std::string& profileName, int loggingLevel = -1); + void printProfile(const std::string& profileName, blt::logging::LOG_LEVEL loggingLevel = logging::NONE); + void printOrderedProfile(const std::string& profileName, logging::LOG_LEVEL loggingLevel = logging::NONE); void discardProfiles(); void discardIntervals(const std::string& profileName); diff --git a/include/blt/std/format.h b/include/blt/std/format.h index 0a0846e..51e5dd8 100644 --- a/include/blt/std/format.h +++ b/include/blt/std/format.h @@ -12,6 +12,7 @@ #include namespace blt::string { + // TODO template the padding functions: /** * Ensure that string str has expected length, pad after the string otherwise. diff --git a/include/blt/std/logging.h b/include/blt/std/logging.h index be9e5d7..42763b3 100644 --- a/include/blt/std/logging.h +++ b/include/blt/std/logging.h @@ -8,17 +8,22 @@ #define BLT_LOGGING_H #include +#include namespace blt::logging { enum LOG_LEVEL { - TRACE = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4, FATAL = 5 + TRACE = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4, FATAL = 5, NONE = 6 }; 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 = TRACE; @@ -30,7 +35,7 @@ namespace blt::logging { struct logger { LOG_LEVEL level; - void logi(const std::string& str) const; + void log_internal(const std::string& str) const; // evil hack, todo: better way #ifdef BLT_DISABLE_LOGGING void log(const std::string& str) const { @@ -38,23 +43,21 @@ namespace blt::logging { } #else void log(const std::string& str) const { - logi(str); + log_internal(str); } #endif void flush() const; + static void flush_all(); }; + static logger std_out{NONE}; + static logger tlog{TRACE}; static logger dlog{DEBUG}; static logger ilog{INFO}; static logger wlog{WARN}; static logger elog{ERROR}; static logger flog{FATAL}; - - /** - * Always in order of the enum! - */ - static logger loggerLevelDecode[6]{tlog, dlog, ilog, wlog, elog, flog}; static logger trace{TRACE}; static logger debug{DEBUG}; @@ -63,73 +66,42 @@ namespace blt::logging { static logger error{ERROR}; static logger fatal{FATAL}; + static inline logger& getLoggerFromLevel(LOG_LEVEL level) { + static logger loggerLevelDecode[7]{tlog, dlog, ilog, wlog, elog, flog, std_out}; + return loggerLevelDecode[level]; + } + static inline logger& operator<<(logger& out, const std::string& str) { out.log(str); return out; } - static inline logger& operator<<(logger& out, const char chr) { - out.log(std::to_string(chr)); - return out; - } - - static inline logger& operator<<(logger& out, const unsigned char i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const short i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const unsigned short i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const int i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const unsigned int i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const long i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const unsigned long i) { - out.log(std::to_string(i)); - return out; - } - - static inline logger& operator<<(logger& out, const float f) { - out.log(std::to_string(f)); - return out; - } - - static inline logger& operator<<(logger& out, const double f) { - out.log(std::to_string(f)); + 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(const std::string& format, LOG_LEVEL level, int auto_line, ...); - void log(int i, LOG_LEVEL level, int auto_line); - void log(long i, LOG_LEVEL level, int auto_line); - void log(unsigned int i, LOG_LEVEL level, int auto_line); - void log(unsigned long i, LOG_LEVEL level, int auto_line); - void log(char i, LOG_LEVEL level, int auto_line); - void log(unsigned char i, LOG_LEVEL level, int auto_line); - void log(short i, LOG_LEVEL level, int auto_line); - void log(unsigned short i, LOG_LEVEL level, int auto_line); - void log(float f, LOG_LEVEL level, int auto_line); - void log(double f, LOG_LEVEL level, int auto_line); + 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! */ @@ -144,26 +116,12 @@ namespace blt::logging { #define BLT_ERROR(format, ...) #define BLT_FATAL(format, ...) #else - /*#define BLT_TRACE(format, ...) log(format, blt::logging::TRACE, false, ##__VA_ARGS__); - #define BLT_DEBUG(format, ...) log(format, blt::logging::DEBUG, false, ##__VA_ARGS__); - #define BLT_INFO(format, ...) log(format, blt::logging::INFO, false, ##__VA_ARGS__); - #define BLT_WARN(format, ...) log(format, blt::logging::WARN, false, ##__VA_ARGS__); - #define BLT_ERROR(format, ...) log(format, blt::logging::ERROR, false, ##__VA_ARGS__); - #define BLT_FATAL(format, ...) log(format, blt::logging::FATAL, false, ##__VA_ARGS__); - - #define BLT_TRACE_LN(format, ...) log(format, blt::logging::TRACE, true, ##__VA_ARGS__); - #define BLT_DEBUG_LN(format, ...) log(format, blt::logging::DEBUG, true, ##__VA_ARGS__); - #define BLT_INFO_LN(format, ...) log(format, blt::logging::INFO, true, ##__VA_ARGS__); - #define BLT_WARN_LN(format, ...) log(format, blt::logging::WARN, true, ##__VA_ARGS__); - #define BLT_ERROR_LN(format, ...) log(format, blt::logging::ERROR, true, ##__VA_ARGS__); - #define BLT_FATAL_LN(format, ...) log(format, blt::logging::FATAL, true, ##__VA_ARGS__);*/ - - #define BLT_TRACE(format, ...) log(format, blt::logging::TRACE, true, ##__VA_ARGS__); - #define BLT_DEBUG(format, ...) log(format, blt::logging::DEBUG, true, ##__VA_ARGS__); - #define BLT_INFO(format, ...) log(format, blt::logging::INFO, true, ##__VA_ARGS__); - #define BLT_WARN(format, ...) log(format, blt::logging::WARN, true, ##__VA_ARGS__); - #define BLT_ERROR(format, ...) log(format, blt::logging::ERROR, true, ##__VA_ARGS__); - #define BLT_FATAL(format, ...) log(format, blt::logging::FATAL, true, ##__VA_ARGS__); + #define BLT_TRACE(format, ...) log_internal(format, blt::logging::TRACE, __FILE__, __LINE__, true, ##__VA_ARGS__); + #define BLT_DEBUG(format, ...) log_internal(format, blt::logging::DEBUG, __FILE__, __LINE__, true, ##__VA_ARGS__); + #define BLT_INFO(format, ...) log_internal(format, blt::logging::INFO, __FILE__, __LINE__, true, ##__VA_ARGS__); + #define BLT_WARN(format, ...) log_internal(format, blt::logging::WARN, __FILE__, __LINE__, true, ##__VA_ARGS__); + #define BLT_ERROR(format, ...) log_internal(format, blt::logging::ERROR, __FILE__, __LINE__, true, ##__VA_ARGS__); + #define BLT_FATAL(format, ...) log_internal(format, blt::logging::FATAL, __FILE__, __LINE__, true, ##__VA_ARGS__); #endif -#endif //BLT_TESTS_LOGGING_H +#endif //BLT_LOGGING_H diff --git a/src/blt/profiling/profiler.cpp b/src/blt/profiling/profiler.cpp index cf220b9..2eac68c 100644 --- a/src/blt/profiling/profiler.cpp +++ b/src/blt/profiling/profiler.cpp @@ -46,18 +46,13 @@ namespace blt::profiling { return profiles[profileName]; } - inline void print(const std::vector& lines, int level) { - if (level < 0) { - for (const auto& line : lines) - std::cout << line; - } else { - auto& logger = logging::loggerLevelDecode[level]; - for (const auto& line : lines) - logger << line; - } + inline void print(const std::vector& lines, logging::LOG_LEVEL level) { + auto& logger = logging::getLoggerFromLevel(level); + for (const auto& line : lines) + logger << line; } - void printProfile(const std::string& profileName, int loggingLevel) { + void printProfile(const std::string& profileName, blt::logging::LOG_LEVEL loggingLevel) { string::TableFormatter formatter {profileName}; formatter.addColumn({"Interval"}); formatter.addColumn({"Time (ns)"}); @@ -91,7 +86,7 @@ namespace blt::profiling { return container1.difference < container2.difference; } - void printOrderedProfile(const std::string& profileName, int loggingLevel) { + void printOrderedProfile(const std::string& profileName, logging::LOG_LEVEL loggingLevel) { const auto& profile = profiles[profileName]; const auto& intervals = profile.intervals; const auto& points = profile.points; diff --git a/src/blt/std/logging.cpp b/src/blt/std/logging.cpp index 83687e9..8916d40 100644 --- a/src/blt/std/logging.cpp +++ b/src/blt/std/logging.cpp @@ -9,6 +9,7 @@ #include #include #include "blt/std/string.h" +#include "blt/std/format.h" #include #include #include @@ -107,10 +108,28 @@ namespace blt::logging { BLT_LOGGING_PROPERTIES = properties; } - inline void log(const std::string& str, bool hasEndingLinefeed, LOG_LEVEL level, int auto_line){ + 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(); + bool includeExtras = BLT_LOGGING_PROPERTIES.m_logWithData && currentLine >= 0; + if (includeExtras) { + outputString += "["; + outputString += filename(file); + outputString += ":"; + outputString += std::to_string(currentLine); + outputString += "] "; + } outputString += levelNames[level]; outputString += str; @@ -138,7 +157,7 @@ namespace blt::logging { } } - void log(const std::string& format, LOG_LEVEL level, int auto_line, ...) { + 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); @@ -150,91 +169,57 @@ namespace blt::logging { if (hasEndingLinefeed) formattedString = formattedString.substr(0, formattedString.length()-1); - log(formattedString, hasEndingLinefeed, level, auto_line); + log(formattedString, hasEndingLinefeed, level, file, currentLine, auto_line); va_end(args); } - void log(int i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } + // stores an association between thread -> log level -> current line buffer + std::unordered_map> thread_local_strings; - void log(long i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(unsigned int i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(unsigned long i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(char i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(unsigned char i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(short i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(unsigned short i, LOG_LEVEL level, int auto_line) { - log(std::to_string(i), false, level, true); - } - - void log(float f, LOG_LEVEL level, int auto_line) { - log(std::to_string(f), false, level, true); - } - - void log(double f, LOG_LEVEL level, int auto_line) { - log(std::to_string(f), false, level, true); - } - - std::unordered_map thread_local_strings; - - void logger::logi(const std::string& str) const { + void logger::log_internal(const std::string& str) const { auto id = std::this_thread::get_id(); - auto th_str = thread_local_strings[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'; - logging::log(th_str, false, level, !hasEndingLinefeed); - thread_local_strings[id] = ""; + if (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] = th_str; + 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) { - auto th_str = id.second; - bool hasEndingLinefeed = th_str[th_str.length() - 1] == '\n'; - logging::log(th_str, false, level, !hasEndingLinefeed); - thread_local_strings[id.first] = ""; + 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() { - // TODO: this will prevent proper level output. Please fixme - tlog.flush(); - dlog.flush(); - ilog.flush(); - wlog.flush(); - elog.flush(); - flog.flush(); - trace.flush(); - debug.flush(); - info.flush(); - warn.flush(); - error.flush(); - fatal.flush(); + logger::flush_all(); std::cerr.flush(); std::cout.flush(); } diff --git a/src/tests/main.cpp b/src/tests/main.cpp index a81fdca..7e865a8 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -14,5 +14,12 @@ int main() { nbt_tests(); + blt::logging::tlog << "Test Output!\n"; + blt::logging::tlog << 5; + blt::logging::flog << 5; + blt::logging::ilog << 5; + + blt::logging::flush(); + return 0; } \ No newline at end of file diff --git a/src/tests/nbt_tests.h b/src/tests/nbt_tests.h index 123924c..efa3295 100644 --- a/src/tests/nbt_tests.h +++ b/src/tests/nbt_tests.h @@ -113,9 +113,9 @@ inline void nbt_read_tests(){ } BLT_PRINT_ORDERED("nbt read"); - BLT_TRACE(""); + BLT_TRACE("{BLANK_LINE}"); BLT_PRINT_ORDERED("nbt read block"); - BLT_TRACE(""); + BLT_TRACE("{BLANK_LINE}"); BLT_PRINT_ORDERED("nbt read individual"); delete[] read_buffer;