#ifndef LOGGING_H #define LOGGING_H #include #include #include #include // Logging levels #define LOG_ERROR 0 #define LOG_WARN 1 #define LOG_INFO 2 #define LOG_DEBUG 3 #define LOG_TRACE 4 // Set logging arguments // TODO: set these by compiler arguments or env vars or something #define LOGGING LOG_DEBUG // logging level #define LOGGING_COLOR true // enable color #define LOGGING_TIMESTAMP true // enable timestamp #define LOGGING_TIMESTAMP_FMT "%Y-%m-%dT%H:%M:%S%z" // timestamp format (local time) #define LOGGING_POSITION true // display position (only works on C++20 or newer) // Color codes #define ANSI_RESET "\033[0m" #define ANSI_BLACK "\033[30m" /* Black */ #define ANSI_RED "\033[31m" /* Red */ #define ANSI_GREEN "\033[32m" /* Green */ #define ANSI_YELLOW "\033[33m" /* Yellow */ #define ANSI_BLUE "\033[34m" /* Blue */ #define ANSI_MAGENTA "\033[35m" /* Magenta */ #define ANSI_CYAN "\033[36m" /* Cyan */ #define ANSI_WHITE "\033[37m" /* White */ #define ANSI_BOLD "\033[1m" /* Bold */ template void print(T t) { std::cout << t << std::endl; } template void print(T t, Args... args) { std::cout << t; print(args...); } inline void printTimestamp() { #if LOGGING_TIMESTAMP auto now = std::chrono::system_clock::now(); auto time_c = std::chrono::system_clock::to_time_t(now); std::tm time_tm; localtime_r(&time_c, &time_tm); std::cout << std::put_time(&time_tm, LOGGING_TIMESTAMP_FMT) << ": "; #endif } // if we're on C++20 or later, then use the source_location header and add source location to logs #if __cplusplus >= 202002L #include inline void printPosition(std::source_location& location) { #if LOGGING_POSITION std::cout << location.file_name() << ":" << location.function_name << ":" << ANSI_CYAN << location.line() << ANSI_RESET << ": "; #endif } inline void printHeader(std::string name, std::string color, std::source_location& location) { #if LOGGING_COLOR std::cout << ANSI_BOLD << color << "[" << name << "] " << ANSI_RESET; printTimestamp(); printPosition(location); #else printHeader(name); #endif } inline void printHeader(std::string name, std::source_location& location) { std::cout << "[" << name << "] "; printTimestamp(); printPosition(location); } template void trace(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_TRACE printHeader("TRACE", ANSI_CYAN, location); print(args...); #endif } template void debug(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_DEBUG printHeader("DEBUG", ANSI_MAGENTA, location); print(args...); #endif } template void info(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_INFO printHeader("INFO", ANSI_GREEN, location); print(args...); #endif } template void warn(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_WARN printHeader("WARN", ANSI_YELLOW, location); print(args...); #endif } template void error(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_ERROR printHeader("ERROR", ANSI_RED, location); print(args...); #endif } #else inline void printHeader(std::string name, std::string color) { #if LOGGING_COLOR std::cout << ANSI_BOLD << color << "[" << name << "] " << ANSI_RESET; printTimestamp(); #else printHeader(name); #endif } inline void printHeader(std::string name) { std::cout << "[" << name << "] "; printTimestamp(); } template void trace(Args... args) { #if LOGGING >= LOG_TRACE printHeader("TRACE", ANSI_CYAN); print(args...); #endif } template void debug(Args... args) { #if LOGGING >= LOG_DEBUG printHeader("DEBUG", ANSI_MAGENTA); print(args...); #endif } template void info(Args... args) { #if LOGGING >= LOG_INFO printHeader("INFO", ANSI_GREEN); print(args...); #endif } template void warn(Args... args) { #if LOGGING >= LOG_WARN printHeader("WARN", ANSI_YELLOW); print(args...); #endif } template void error(Args... args) { #if LOGGING >= LOG_ERROR printHeader("ERROR", ANSI_RED); print(args...); #endif } #endif #endif //LOGGING_H