diff --git a/CHANGELOG.md b/CHANGELOG.md index f298734..8e19f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for line ending normalization +- `Kappa::Logger::SetLevel` to dynamically adjust logging verbosity +- `Kappa::LogLevel` enum for type-safe log level management +- Refactored `Kappa::Logger` using PIMPL pattern to hide `spdlog` implementation details from public API +- CMake presets for easier configuration ### Changed diff --git a/CMakeLists.txt b/CMakeLists.txt index 91f4e70..1981171 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ find_package(spdlog CONFIG REQUIRED) add_library(Kappa STATIC src/Application.cpp + src/Logger.cpp src/Window.cpp src/WindowStatePersistence.cpp src/Texture.cpp) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..2e99bec --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,102 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 25, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/build/${presetName}" + }, + { + "name": "native-base", + "hidden": true, + "inherits": "base", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + } + } + }, + { + "name": "single-config-base", + "hidden": true, + "inherits": "native-base", + "generator": "Ninja" + }, + { + "name": "multi-config-base", + "hidden": true, + "inherits": "native-base", + "generator": "Visual Studio 17 2022", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "ninja-debug", + "displayName": "Ninja Debug", + "inherits": "single-config-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "SH3DS_ENABLE_SANITIZERS": "OFF" + } + }, + { + "name": "ninja-release", + "displayName": "Ninja Release", + "inherits": "single-config-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "visual-studio", + "displayName": "Visual Studio", + "inherits": "multi-config-base" + } + ], + "buildPresets": [ + { + "name": "ninja-debug", + "configurePreset": "ninja-debug" + }, + { + "name": "ninja-release", + "configurePreset": "ninja-release" + }, + { + "name": "visual-studio-debug", + "configurePreset": "visual-studio", + "configuration": "Debug" + }, + { + "name": "visual-studio-release", + "configurePreset": "visual-studio", + "configuration": "Release" + } + ], + "testPresets": [ + { + "name": "ninja-debug", + "configurePreset": "ninja-debug", + "output": { + "outputOnFailure": true + } + }, + { + "name": "visual-studio-debug", + "configurePreset": "visual-studio", + "configuration": "Debug", + "output": { + "outputOnFailure": true + } + } + ] +} diff --git a/include/Kappa/Logger.h b/include/Kappa/Logger.h index 97eb4bc..cd39cec 100644 --- a/include/Kappa/Logger.h +++ b/include/Kappa/Logger.h @@ -1,15 +1,27 @@ #pragma once -#include #include #include #include - -#include -#include +#include +#include namespace Kappa { + /** + * @brief Logging levels. + */ + enum class LogLevel + { + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4, + Critical = 5, + Off = 6 + }; + /** * @brief Type-safe logging wrapper around spdlog. */ @@ -20,31 +32,13 @@ namespace Kappa * @brief Returns the logger instance. * @return Logger instance */ - static Logger &Get() - { - static Logger instance(spdlog::stdout_color_mt(GetLoggerName())); - return instance; - } + static Logger &Get(); /** * @brief Sets the logger name (must be called before first Get()). * @param name Logger name to display in logs */ - static void SetLoggerName(const std::string &name) - { - GetLoggerName() = name; - } - - private: - /** - * @brief Returns the logger name (default: "Kappa"). - * @return Logger name - */ - static std::string &GetLoggerName() - { - static std::string loggerName = "Kappa"; - return loggerName; - } + static void SetLoggerName(const std::string &name); public: /** @@ -54,11 +48,9 @@ namespace Kappa * @param format Format string * @param args Format arguments */ - template - void Trace(const std::source_location &loc, const std::string &format, Args &&...args) + template void Trace(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->trace( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); + LogInternal(LogLevel::Trace, loc, format, std::make_format_args(args...)); } /** @@ -68,11 +60,9 @@ namespace Kappa * @param format Format string * @param args Format arguments */ - template - void Debug(const std::source_location &loc, const std::string &format, Args &&...args) + template void Debug(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->debug( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); + LogInternal(LogLevel::Debug, loc, format, std::make_format_args(args...)); } /** @@ -82,10 +72,9 @@ namespace Kappa * @param format Format string * @param args Format arguments */ - template void Info(const std::source_location &loc, const std::string &format, Args &&...args) + template void Info(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->info( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); + LogInternal(LogLevel::Info, loc, format, std::make_format_args(args...)); } /** @@ -95,10 +84,9 @@ namespace Kappa * @param format Format string * @param args Format arguments */ - template void Warn(const std::source_location &loc, const std::string &format, Args &&...args) + template void Warn(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->warn( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); + LogInternal(LogLevel::Warn, loc, format, std::make_format_args(args...)); } /** @@ -108,11 +96,9 @@ namespace Kappa * @param format Format string * @param args Format arguments */ - template - void Error(const std::source_location &loc, const std::string &format, Args &&...args) + template void Error(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->error( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); + LogInternal(LogLevel::Error, loc, format, std::make_format_args(args...)); } /** @@ -123,74 +109,37 @@ namespace Kappa * @param args Format arguments */ template - void Critical(const std::source_location &loc, const std::string &format, Args &&...args) + void Critical(const std::source_location &loc, std::string_view format, Args &&...args) { - logger_->critical( - "[{}:{}] {}", GetFileName(loc), loc.line(), std::vformat(format, std::make_format_args(args...))); - } - - /** - * @brief Returns the spdlog instance. - * @return spdlog logger - */ - spdlog::logger &GetSpdlog() - { - return *logger_; - } - const spdlog::logger &GetSpdlog() const - { - return *logger_; + LogInternal(LogLevel::Critical, loc, format, std::make_format_args(args...)); } /** * @brief Flushes the logger. */ - void Flush() - { - logger_->flush(); - } + void Flush(); /** * @brief Sets the log level. * @param level Log level */ - void SetLevel(spdlog::level::level_enum level) - { - logger_->set_level(level); - } + void SetLevel(LogLevel level); private: - /** - * @brief Constructs logger. - * @param spdlogger spdlog logger instance - */ - explicit Logger(std::shared_ptr spdlogger) : logger_(std::move(spdlogger)) - { - } + Logger(); + ~Logger(); - /** - * @brief Extracts filename from full path. - * @param loc Source location - * @return Filename without path - */ - static std::string_view GetFileName(const std::source_location &loc) - { - const char *path = loc.file_name(); - const char *filename = path; - - for (const char *p = path; *p; ++p) - { - if (*p == '/' || *p == '\\') - { - filename = p + 1; - } - } - return filename; - } + void LogInternal(LogLevel level, + const std::source_location &loc, + std::string_view format, + std::format_args args); - std::shared_ptr logger_; - }; + static std::string &GetLoggerName(); + static std::string_view GetFileName(const std::source_location &loc); + struct Impl; + std::unique_ptr impl_; + }; } // namespace Kappa /** diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 0000000..249618a --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,73 @@ +#include "Kappa/Logger.h" + +#include +#include + +namespace Kappa +{ + struct Logger::Impl + { + std::shared_ptr logger; + }; + + Logger::Logger() : impl_(std::make_unique()) + { + impl_->logger = spdlog::stdout_color_mt(GetLoggerName()); + // Default to info level + impl_->logger->set_level(spdlog::level::info); + } + + Logger::~Logger() = default; + + Logger &Logger::Get() + { + static Logger instance; + return instance; + } + + void Logger::SetLoggerName(const std::string &name) + { + GetLoggerName() = name; + } + + std::string &Logger::GetLoggerName() + { + static std::string loggerName = "Kappa"; + return loggerName; + } + + void Logger::SetLevel(LogLevel level) + { + impl_->logger->set_level(static_cast(level)); + } + + void Logger::Flush() + { + impl_->logger->flush(); + } + + void Logger::LogInternal(LogLevel level, + const std::source_location &loc, + std::string_view format, + std::format_args args) + { + auto message = std::vformat(format, args); + impl_->logger->log( + static_cast(level), "[{}:{}] {}", GetFileName(loc), loc.line(), message); + } + + std::string_view Logger::GetFileName(const std::source_location &loc) + { + const char *path = loc.file_name(); + const char *filename = path; + + for (const char *p = path; *p; ++p) + { + if (*p == '/' || *p == '\\') + { + filename = p + 1; + } + } + return filename; + } +} // namespace Kappa diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e407d73..849ae1a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.26) # Gradually re-enable tests to find the culprit add_executable(TestKappaCore TestSimple.cpp + TestLogger.cpp TestEventBus.cpp # ✅ Passed (15 tests) TestLayer.cpp # ✅ Passed (15 tests) TestWindow.cpp # Testing Window structures diff --git a/tests/TestLogger.cpp b/tests/TestLogger.cpp new file mode 100644 index 0000000..15eb04b --- /dev/null +++ b/tests/TestLogger.cpp @@ -0,0 +1,35 @@ +#include +#include + +using namespace Kappa; + +TEST(LoggerTest, SetLevel) +{ + // Default level should be Info + LOG_INFO("This is an info message"); + + Logger::Get().SetLevel(LogLevel::Debug); + LOG_DEBUG("This is a debug message - should be visible"); + + Logger::Get().SetLevel(LogLevel::Warn); + LOG_DEBUG("This is a debug message - should NOT be visible"); + LOG_WARN("This is a warning message - should be visible"); +} + +TEST(LoggerTest, AllLevels) +{ + Logger::Get().SetLevel(LogLevel::Trace); + LOG_TRACE("Trace"); + LOG_DEBUG("Debug"); + LOG_INFO("Info"); + LOG_WARN("Warn"); + LOG_ERROR("Error"); + LOG_CRITICAL("Critical"); +} + +TEST(LoggerTest, Formatting) +{ + LOG_INFO("Hello {}!", "World"); + LOG_INFO("Value: {}", 42); + LOG_INFO("Hex: {:x}", 255); +}