diff --git a/.gitignore b/.gitignore index 0a70541..b152300 100644 --- a/.gitignore +++ b/.gitignore @@ -108,6 +108,8 @@ CMakeUserPresets.json # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +.idea/ + # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml @@ -730,7 +732,8 @@ $RECYCLE.BIN/ .LSOverride # Icon must end with two \r -Icon +Icon + # Thumbnails ._* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e4b8cfe --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 4.0.2) +project(FeatherLog) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if (MSVC) + add_compile_options(/Wall /utf-8) +else () + add_compile_options(-Wextra -Wpedantic -Wall) +endif () + +set(CMAKE_BINARY_DIR "out") + +set(sources + src/featherlog/log_manager.hpp + src/featherlog/log_outputter.hpp + src/featherlog/logger.hpp + src/featherlog/log_formatter.hpp + src/featherlog/log_level.hpp + src/featherlog/log_message.hpp + src/featherlog/log_manager.cpp) + +# Shared Lib: +if (MSVC) + add_compile_options(/DFEATHER_LOG_EXPORT_DLL) +else () + add_compile_options(-DFEATHER_LOG_EXPORT_DLL) +endif () +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/shared") +add_library(${PROJECT_NAME} SHARED ${sources}) + +# Shared Lib End + +#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/static") +#add_library(${PROJECT_NAME} STATIC ${sources}) + +# Static Lib: +# Static Lib End diff --git a/src/featherlog/log_formatter.hpp b/src/featherlog/log_formatter.hpp new file mode 100644 index 0000000..61587d7 --- /dev/null +++ b/src/featherlog/log_formatter.hpp @@ -0,0 +1,15 @@ +#ifndef FEATHERLOG_LOG_FORMATTER_HPP +#define FEATHERLOG_LOG_FORMATTER_HPP +#include + +namespace flog +{ + class LogFormatter + { + public: + virtual ~LogFormatter() = default; + virtual std::string format() = 0; + }; +} + +#endif \ No newline at end of file diff --git a/src/featherlog/log_level.hpp b/src/featherlog/log_level.hpp new file mode 100644 index 0000000..540bcc2 --- /dev/null +++ b/src/featherlog/log_level.hpp @@ -0,0 +1,21 @@ +#ifndef FEATHERLOG_LOG_LEVEL_HPP +#define FEATHERLOG_LOG_LEVEL_HPP + +#include + +namespace flog +{ + enum class LogLevel : uint8_t + { + TRACE = 0, + DEBUG = 1, + INFO = 2, + WARN = 3, + ERROR = 4, + FATAL = 5, + + OFF = 0xff, + }; +} + +#endif \ No newline at end of file diff --git a/src/featherlog/log_manager.cpp b/src/featherlog/log_manager.cpp new file mode 100644 index 0000000..20736e4 --- /dev/null +++ b/src/featherlog/log_manager.cpp @@ -0,0 +1,103 @@ +#include "log_manager.hpp" + +namespace flog +{ + + + inline std::shared_ptr LogManager::create() + { + // ReSharper disable once CppDFAMemoryLeak + return std::shared_ptr(new LogManager()); + } + + inline LogManager::~LogManager() + { + { + std::lock_guard lock(m_loggersMutex); + m_loggers.clear(); + } + { + std::lock_guard lock(m_logOutputtersMutex); + m_logOutputters.clear(); + } + } + + inline LogLevel LogManager::get_min_log_level() const noexcept + { + return m_minLogLevel.load(std::memory_order_relaxed); + } + + inline void LogManager::set_min_log_level(const LogLevel level) noexcept + { + m_minLogLevel.store(level, std::memory_order_relaxed); + } + + inline void LogManager::add_outputter(std::unique_ptr outputter) + { + std::lock_guard lock(m_logOutputtersMutex); + m_logOutputters.push_back(std::move(outputter)); + } + + inline std::shared_ptr LogManager::get_logger(const std::string& name) + { + std::lock_guard lock(m_loggersMutex); + if (const auto it = m_loggers.find(name); it != m_loggers.end()) + { + return it->second; + } + + const auto logger = std::shared_ptr(new Logger(name, weak_from_this())); + m_loggers.emplace(name, logger); + return logger; + } + + inline size_t LogManager::clean_unused_loggers() + { + std::lock_guard lock(m_loggersMutex); + size_t count = 0; + for (auto it = m_loggers.begin(); it != m_loggers.end();) + { + if (it->second.use_count() == 1) + { + it = m_loggers.erase(it); + count += 1; + } + else + { + ++it; + } + } + return count; + } + + inline void LogManager::print_log(const LogMessage& message) const + { + std::shared_lock lock(m_logOutputtersMutex); + for (auto& outputter : m_logOutputters) + { + outputter->write(message); + } + } + + inline LoggerCallback::LoggerCallback(const std::weak_ptr& logManager) noexcept : m_logManager( + logManager) + { + } + + inline void LoggerCallback::print_log(const LogMessage& message) const + { + if (const auto mgr = m_logManager.lock()) + { + mgr->print_log(message); + } + } + + inline LogLevel LoggerCallback::get_min_log_level() const noexcept + { + if (const auto mgr = m_logManager.lock()) + { + return mgr->get_min_log_level(); + } + return LogLevel::OFF; + } +} diff --git a/src/featherlog/log_manager.hpp b/src/featherlog/log_manager.hpp new file mode 100644 index 0000000..f06ab43 --- /dev/null +++ b/src/featherlog/log_manager.hpp @@ -0,0 +1,59 @@ +#ifndef FEATHERLOG_LOG_MANAGER_HPP +#define FEATHERLOG_LOG_MANAGER_HPP + +#include +#include +#include +#include +#include +#include + +#include "logger.hpp" +#include "log_formatter.hpp" +#include "log_level.hpp" +#include "log_message.hpp" +#include "log_outputter.hpp" + +namespace flog +{ + class LogManager; + + class LogManager : public std::enable_shared_from_this + { + friend class LoggerCallback; + + public: + LogManager(const LogManager&) = delete; + LogManager& operator=(const LogManager&) = delete; + LogManager(LogManager&&) = delete; + LogManager& operator=(LogManager&&) = delete; + + static std::shared_ptr create(); + + ~LogManager(); + + LogLevel get_min_log_level() const noexcept; + void set_min_log_level(LogLevel level) noexcept; + + void add_outputter(std::unique_ptr outputter); + + std::shared_ptr get_logger(const std::string& name); + + size_t clean_unused_loggers(); + + private: + LogManager() noexcept = default; + + void print_log(const LogMessage& message) const; + + std::atomic m_minLogLevel{LogLevel::DEBUG}; + + mutable std::shared_mutex m_logOutputtersMutex; + std::vector> m_logOutputters; + + mutable std::mutex m_loggersMutex; + std::unordered_map> m_loggers; + }; +} + +#endif diff --git a/src/featherlog/log_message.hpp b/src/featherlog/log_message.hpp new file mode 100644 index 0000000..9ecf4b4 --- /dev/null +++ b/src/featherlog/log_message.hpp @@ -0,0 +1,20 @@ +#ifndef FEATHERLOG_LOG_MESSAGE_HPP +#define FEATHERLOG_LOG_MESSAGE_HPP + + +#include +#include + +#include "log_level.hpp" + +namespace flog +{ + struct LogMessage { + std::chrono::system_clock::time_point timestamp; + LogLevel level; + std::string_view logger_name; + std::string text; + }; +} + +#endif \ No newline at end of file diff --git a/src/featherlog/log_outputter.hpp b/src/featherlog/log_outputter.hpp new file mode 100644 index 0000000..798c1dd --- /dev/null +++ b/src/featherlog/log_outputter.hpp @@ -0,0 +1,27 @@ +#ifndef FEATHERLOG_LOG_OUTPUTER_HPP +#define FEATHERLOG_LOG_OUTPUTER_HPP + +#include +#include + +#include "log_formatter.hpp" +#include "log_message.hpp" + +namespace flog +{ + class LogOutputter + { + public: + virtual ~LogOutputter() = default; + + std::unique_ptr set_formatter(const std::unique_ptr& formatter); + std::unique_ptr& get_formatter(); + + virtual void write(const LogMessage& message) = 0; + + protected: + std::unique_ptr m_logFormatter; + }; +} + +#endif \ No newline at end of file diff --git a/src/featherlog/logger.hpp b/src/featherlog/logger.hpp new file mode 100644 index 0000000..958aba9 --- /dev/null +++ b/src/featherlog/logger.hpp @@ -0,0 +1,33 @@ +#ifndef FEATHERLOG_LOGGER_HPP +#define FEATHERLOG_LOGGER_HPP + +#include +#include + +#include "log_manager.hpp" + +namespace flog +{ + class LoggerCallback; + + class Logger + { + friend class LogManager; + + public: + ~Logger() = default; + + template + void info(std::format_string fmt, Args&&... args, + std::source_location loc = std::source_location::current()); + + private: + Logger(const std::string& name, std::weak_ptr manager) : m_name(name), m_logManager(manager) + { } + + std::string m_name; + std::weak_ptr m_logManager; + }; +} + +#endif