diff options
| author | hachem <im@hachem.wtf> | 2025-08-13 19:25:30 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-13 19:25:30 +0200 |
| commit | 1ac0f136629ce4046a5f6529cf3d1e51e96df408 (patch) | |
| tree | ea42cf71c3a7760d1454f90dca36147d353a3581 /src/core/log.h | |
| parent | 23086a5d33acc66414f1169bf22abe78b3383f94 (diff) | |
feat: logging system
Diffstat (limited to 'src/core/log.h')
| -rw-r--r-- | src/core/log.h | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/src/core/log.h b/src/core/log.h new file mode 100644 index 0000000..77324f5 --- /dev/null +++ b/src/core/log.h @@ -0,0 +1,154 @@ +#pragma once + +#include <string> +#include <memory> +#include <vector> +#include <sstream> +#include <iostream> +#include <fstream> +#include <chrono> +#include <iomanip> +#include <mutex> +#include <string_view> + +namespace Donut +{ + enum class LogLevel + { + trace = 0, + Info = 2, + Warn = 3, + Err = 4, + Fatal = 5 + }; + + class Logger + { + public: + static void init(); + static void shutdown(); + + static std::shared_ptr<Logger> get_logger(); + + template<typename... Args> + static void trace(const std::string_view& format, const Args&... args) + { + auto logger = get_logger(); + if (logger) logger->log_message(LogLevel::trace, format, args...); + } + + template<typename... Args> + static void info(const std::string_view& format, const Args&... args) + { + auto logger = get_logger(); + if (logger) logger->log_message(LogLevel::Info, format, args...); + } + + template<typename... Args> + static void warn(const std::string_view& format, const Args&... args) + { + auto logger = get_logger(); + if (logger) logger->log_message(LogLevel::Warn, format, args...); + } + + template<typename... Args> + static void error(const std::string_view& format, const Args&... args) + { + auto logger = get_logger(); + if (logger) logger->log_message(LogLevel::Err, format, args...); + } + + template<typename... Args> + static void fatal(const std::string_view& format, const Args&... args) + { + auto logger = get_logger(); + if (logger) logger->log_message(LogLevel::Fatal, format, args...); + } + + template<typename... Args> + void log_message(LogLevel level, const std::string_view& format, const Args&... args) + { + if (level < m_log_level) + return; + + std::lock_guard<std::mutex> lock(m_mutex); + std::string message = format_string(format, args...); + std::string full_message = get_time_stamp() + " [" + get_log_level_string(level) + "] " + message; + + if (m_console_output) + { + set_console_color(level); + std::cout << full_message << std::endl; + reset_console_color(); + } + + if (m_file_output && m_log_file.is_open()) + { + m_log_file << full_message << std::endl; + m_log_file.flush(); + } + } + + template<typename... Args> + std::string format_string(const std::string_view& format, const Args&... args) + { + std::string result = format.data(); + std::vector<std::string> arg_strings = { to_string(args)... }; + + size_t arg_index = 0; + size_t pos = 0; + + while ((pos = result.find("{}", pos)) != std::string::npos && arg_index < arg_strings.size()) + { + result.replace(pos, 2, arg_strings[arg_index]); + pos += arg_strings[arg_index].length(); + arg_index++; + } + + return result; + } + + template<typename T> + std::string to_string(const T& value) + { + std::stringstream ss; + ss << value; + return ss.str(); + } + + void set_log_level(LogLevel level) { m_log_level = level; } + void enable_console_output(bool enable) { m_console_output = enable; } + void enable_file_output(bool enable) { m_file_output = enable; } + void set_log_file(const std::string& filename); + public: + Logger(); + ~Logger(); + + std::string get_time_stamp(); + std::string get_log_level_string(LogLevel level); + void set_console_color(LogLevel level); + void reset_console_color(); + private: + LogLevel m_log_level; + bool m_console_output; + bool m_file_output; + std::ofstream m_log_file; + std::mutex m_mutex; + + static std::shared_ptr<Logger> s_logger; + }; +} + +#if defined(DONUT_DEBUG) + #define DONUT_TRACE(format, ...) ::Donut::Logger::trace(format, ##__VA_ARGS__) + #define DONUT_INFO(format, ...) ::Donut::Logger::info(format, ##__VA_ARGS__) + #define DONUT_WARN(format, ...) ::Donut::Logger::warn(format, ##__VA_ARGS__) + #define DONUT_ERROR(format, ...) ::Donut::Logger::error(format, ##__VA_ARGS__) + #define DONUT_FATAL(format, ...) ::Donut::Logger::fatal(format, ##__VA_ARGS__) +#else + #define DONUT_TRACE(format, ...) {} + #define DONUT_INFO(format, ...) {} + #define DONUT_WARN(format, ...) {} + #define DONUT_ERROR(format, ...) {} + #define DONUT_FATAL(format, ...) {} +#endif |
