diff --git a/src/core/logger.cpp b/src/core/logger.cpp new file mode 100644 index 0000000..54b1fe9 --- /dev/null +++ b/src/core/logger.cpp @@ -0,0 +1,104 @@ +#include "logger.h" + +#include +#include +#include +#include + +mirai::logger::logger(logger_config config) : config_(config){ + sinks_.clear(); + + if (config_.console_enabled) { + auto console_sink = std::make_shared(); + console_sink->set_level(to_spdlog_level(config_.level)); + sinks_.push_back(console_sink); + } + + if (config_.file_enabled) { + auto log_dir = config_.file_path.parent_path(); + if (!log_dir.empty() && !std::filesystem::exists(log_dir)) { + std::filesystem::create_directory(log_dir); + } + + if (config_.file_rotation_enabled) { + auto file_sink = std::make_shared( + config_.file_path.string(), + config_.max_file_size, + config_.max_files + ); + file_sink->set_level(to_spdlog_level(config_.level)); + sinks_.push_back(file_sink); + } else { + auto file_sink = std::make_shared( + config_.file_path.string(), + true + ); + file_sink->set_level(to_spdlog_level(config_.level)); + sinks_.push_back(file_sink); + } + } + + if (config_.async_enabled) { + // 初始化异步日志线程池 + spdlog::init_thread_pool(config_.async_queue_size, 1); + spdlog_logger_ = std::make_shared( + config_.name, + sinks_.begin(), + sinks_.end(), + spdlog::thread_pool(), + spdlog::async_overflow_policy::block + ); + } else { + spdlog_logger_ = std::make_shared( + config_.name, + sinks_.begin(), + sinks_.end() + ); + } + + // 设置日志级别 + spdlog_logger_->set_level(to_spdlog_level(config_.level)); + + // 设置日志格式 + spdlog_logger_->set_pattern(config_.pattern); + + // 设置刷新策略 + spdlog_logger_->flush_on(spdlog::level::warn); + + // 注册到 spdlog + spdlog::register_logger(spdlog_logger_); +} + +mirai::logger::~logger() { + if (spdlog_logger_) { + spdlog_logger_->flush(); + spdlog::drop(spdlog_logger_->name()); + } +} + +void mirai::logger::set_level(log_level level) { + config_.level = level; + if (spdlog_logger_) { + spdlog_logger_->set_level(to_spdlog_level(level)); + } + for (auto& sink : sinks_) { + sink->set_level(to_spdlog_level(level)); + } +} + +void mirai::logger::set_pattern(const std::string& pattern) { + config_.pattern = pattern; + if (spdlog_logger_) { + spdlog_logger_->set_pattern(pattern); + } +} + +bool mirai::logger::should_log(log_level level) const noexcept { + return spdlog_logger_ && spdlog_logger_->should_log(to_spdlog_level(level)); +} + +void mirai::logger::flush() { + if (spdlog_logger_) { + spdlog_logger_->flush(); + } +} diff --git a/src/core/logger.h b/src/core/logger.h new file mode 100644 index 0000000..a25dd6a --- /dev/null +++ b/src/core/logger.h @@ -0,0 +1,347 @@ +#pragma once +#include +#include +#include +#include + +#include "types.h" + +namespace mirai { + enum class log_level : u8 { + trace = 0, + debug = 1, + info = 2, + warn = 3, + error = 4, + critical = 5, + off = 6 + }; + + [[nodiscard]] constexpr std::string_view log_level_to_string(log_level level) noexcept { + switch (level) { + case log_level::trace: return "trace"; + case log_level::debug: return "debug"; + case log_level::info: return "info"; + case log_level::warn: return "warn"; + case log_level::error: return "error"; + case log_level::critical: return "critical"; + case log_level::off: return "off"; + default: return "unknown"; + } + } + + [[nodiscard]] constexpr spdlog::level::level_enum to_spdlog_level(log_level level) noexcept { + switch (level) { + case log_level::trace: return spdlog::level::trace; + case log_level::debug: return spdlog::level::debug; + case log_level::info: return spdlog::level::info; + case log_level::warn: return spdlog::level::warn; + case log_level::error: return spdlog::level::err; + case log_level::critical: return spdlog::level::critical; + case log_level::off: return spdlog::level::off; + default: return spdlog::level::info; + } + } + + struct logger_config { + std::string name{"mirai"}; + log_level level{log_level::info}; + bool console_enabled{true}; + bool file_enabled{true}; + std::filesystem::path file_path{"logs/mirai.log"}; + bool file_rotation_enabled{true}; + size_type max_file_size{10 * 1024 * 1024}; // 10 MB + size_type max_files{5}; + std::string pattern{"[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v"}; + bool include_source_location{true}; + bool async_enabled{false}; + size_type async_queue_size{8192}; + }; + + class logger { + public: + explicit logger(logger_config config = {}); + ~logger(); + + logger(const logger&) = delete; + logger& operator=(const logger&) = delete; + + [[nodiscard]] const auto& name() const noexcept { return config_.name; } + [[nodiscard]] auto level() const noexcept { return config_.level; } + void set_level(log_level level); + void set_pattern(const std::string& pattern); + [[nodiscard]] bool should_log(log_level level) const noexcept; + void flush(); + + template + void trace(fmt::format_string fmt, Args&&... args) { + log(log_level::trace, fmt, std::forward(args)...); + } + + template + void debug(fmt::format_string fmt, Args&&... args) { + log(log_level::debug, fmt, std::forward(args)...); + } + + template + void info(fmt::format_string fmt, Args&&... args) { + log(log_level::info, fmt, std::forward(args)...); + } + + template + void warn(fmt::format_string fmt, Args&&... args) { + log(log_level::warn, fmt, std::forward(args)...); + } + + template + void error(fmt::format_string fmt, Args&&... args) { + log(log_level::error, fmt, std::forward(args)...); + } + + template + void critical(fmt::format_string fmt, Args&&... args) { + log(log_level::critical, fmt, std::forward(args)...); + } + + template + void log(log_level level, fmt::format_string fmt, Args&&... args) { + if (should_log(level)) { + spdlog_logger_->log(to_spdlog_level(level), fmt, std::forward(args)...); + } + } + + template + void log_with_location( + log_level level, + const std::source_location& location, + fmt::format_string fmt, + Args&&... args + ) { + if (should_log(level)) { + std::string message = fmt::format(fmt, std::forward(args)...); + std::string full_message = fmt::format( + "{} [{}:{}:{}]", + message, + location.file_name(), + location.line(), + location.function_name() + ); + spdlog_logger_->log(to_spdlog_level(level), "{}", full_message); + } + } + private: + logger_config config_; + std::shared_ptr spdlog_logger_; + std::vector sinks_; + }; + + inline auto& get_global_logger_ref() { + static std::shared_ptr instance; + return instance; + } + + inline auto& get_global_logger_mutex() { + static std::mutex mutex; + return mutex; + } + + inline void init_logger(const logger_config& config = logger_config{}) { + std::lock_guard lock(get_global_logger_mutex()); + get_global_logger_ref() = std::make_shared(config); + } + + inline void shutdown_logger() { + std::lock_guard lock(get_global_logger_mutex()); + if (auto& logger = get_global_logger_ref()) { + logger->flush(); + logger.reset(); + } + spdlog::shutdown(); + } + + inline auto get_logger() { + std::lock_guard lock(get_global_logger_mutex()); + auto& logger = get_global_logger_ref(); + if (!logger) { + // 自动初始化默认日志器 + logger = std::make_shared();} + return logger; + } + + inline void set_log_level(log_level level) { + if (auto logger = get_logger()) { + logger->set_level(level); + } + } + + inline void flush_logger() { + if (auto logger = get_logger()) { + logger->flush(); + } + } + + /** + * @brief 记录 trace 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_trace(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->trace(fmt, std::forward(args)...); + } + } + + /** + * @brief 记录 debug 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_debug(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->debug(fmt, std::forward(args)...); + } + } + + /** + * @brief 记录 info 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_info(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->info(fmt, std::forward(args)...); + } + } + + /** + * @brief 记录 warn 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_warn(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->warn(fmt, std::forward(args)...); + } + } + + /** + * @brief 记录 error 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_error(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->error(fmt, std::forward(args)...); + } + } + + /** + * @brief 记录 critical 级别日志 + * @tparam Args 参数类型 + * @param fmt 格式字符串 + * @param args 格式参数 + */ + template + void log_critical(fmt::format_string fmt, Args&&... args) { + if (auto logger = get_logger()) { + logger->critical(fmt, std::forward(args)...); + } + } +} + +/** + * @def MIRAI_LOG_TRACE(...) + * @brief 记录 trace 级别日志 + */ +#define MIRAI_LOG_TRACE(...) ::mirai::log_trace(__VA_ARGS__) + +/** + * @def MIRAI_LOG_DEBUG(...) + * @brief 记录 debug 级别日志 + */ +#define MIRAI_LOG_DEBUG(...) ::mirai::log_debug(__VA_ARGS__) + +/** + * @def MIRAI_LOG_INFO(...) + * @brief 记录 info 级别日志 + */ +#define MIRAI_LOG_INFO(...) ::mirai::log_info(__VA_ARGS__) + +/** + * @def MIRAI_LOG_WARN(...) + * @brief 记录 warn 级别日志 + */ +#define MIRAI_LOG_WARN(...) ::mirai::log_warn(__VA_ARGS__) + +/** + * @def MIRAI_LOG_ERROR(...) + * @brief 记录 error 级别日志 + */ +#define MIRAI_LOG_ERROR(...) ::mirai::log_error(__VA_ARGS__) + +/** + * @def MIRAI_LOG_CRITICAL(...) + * @brief 记录 critical 级别日志 + */ +#define MIRAI_LOG_CRITICAL(...) ::mirai::log_critical(__VA_ARGS__) + +/** + * @def MIRAI_LOG(level, ...) + * @brief 记录指定级别的日志 + */ +#define MIRAI_LOG(level, ...) \ +do { \ + if (auto _logger = ::mirai::get_logger()) { \ + _logger->log(level, __VA_ARGS__); \ + } \ +} while (false) + +/** + * @def MIRAI_LOG_IF(condition, level, ...) + * @brief 条件日志记录 + */ +#define MIRAI_LOG_IF(condition, level, ...) \ +do { \ + if (condition) { \ + MIRAI_LOG(level, __VA_ARGS__); \ + } \ +} while (false) + +// Debug 模式专用日志宏 +#if MIRAI_DEBUG + +/** + * @def MIRAI_DLOG_TRACE(...) + * @brief Debug 模式 trace 日志 + */ +#define MIRAI_DLOG_TRACE(...) MIRAI_LOG_TRACE(__VA_ARGS__) + +/** + * @def MIRAI_DLOG_DEBUG(...) + * @brief Debug 模式 debug 日志 + */ +#define MIRAI_DLOG_DEBUG(...) MIRAI_LOG_DEBUG(__VA_ARGS__) + +/** + * @def MIRAI_DLOG_INFO(...) + * @brief Debug 模式 info 日志 + */ +#define MIRAI_DLOG_INFO(...) MIRAI_LOG_INFO(__VA_ARGS__) + +#else + +#define MIRAI_DLOG_TRACE(...) ((void)0) +#define MIRAI_DLOG_DEBUG(...) ((void)0) +#define MIRAI_DLOG_INFO(...) ((void)0) + +#endif \ No newline at end of file