#ifndef LOGGING_H #define LOGGING_H #include <iostream> #include <iomanip> #include <chrono> #include <ctime> // 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 <typename T> void print(T t) { std::cout << t << std::endl; } template <typename T, typename... Args> 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 <source_location> 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 <typename... Args, typename Sl = std::source_location> void trace(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_TRACE printHeader("TRACE", ANSI_CYAN, location); print(args...); #endif } template <typename... Args, typename Sl = std::source_location> void debug(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_DEBUG printHeader("DEBUG", ANSI_MAGENTA, location); print(args...); #endif } template <typename... Args, typename Sl = std::source_location> void info(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_INFO printHeader("INFO", ANSI_GREEN, location); print(args...); #endif } template <typename... Args, typename Sl = std::source_location> void warn(Args... args, Sl location = std::source_location::current()) { #if LOGGING >= LOG_WARN printHeader("WARN", ANSI_YELLOW, location); print(args...); #endif } template <typename... Args, typename Sl = std::source_location> 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 <typename... Args> void trace(Args... args) { #if LOGGING >= LOG_TRACE printHeader("TRACE", ANSI_CYAN); print(args...); #endif } template <typename... Args> void debug(Args... args) { #if LOGGING >= LOG_DEBUG printHeader("DEBUG", ANSI_MAGENTA); print(args...); #endif } template <typename... Args> void info(Args... args) { #if LOGGING >= LOG_INFO printHeader("INFO", ANSI_GREEN); print(args...); #endif } template <typename... Args> void warn(Args... args) { #if LOGGING >= LOG_WARN printHeader("WARN", ANSI_YELLOW); print(args...); #endif } template <typename... Args> void error(Args... args) { #if LOGGING >= LOG_ERROR printHeader("ERROR", ANSI_RED); print(args...); #endif } #endif #endif //LOGGING_H