From 0bc88101863a0a1a50afcba263e591f55566fed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9C=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=BA=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 15 Oct 2025 19:54:28 +0700 Subject: [PATCH] init --- glogger.hpp | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 49 ++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 glogger.hpp create mode 100644 main.c diff --git a/glogger.hpp b/glogger.hpp new file mode 100644 index 0000000..141a8cf --- /dev/null +++ b/glogger.hpp @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class DaemonLogger { +public: + enum class Severity { + Debug, // 0 + Info, // 1 + Warning, // 2 + Error, // 3 + _Count // 4 - для размера массива + }; + +private: + // Обертка для потокобезопасной записи в один ostream + struct SynchronizedStream { + std::ostream* stream; + std::mutex mtx; + + SynchronizedStream(std::ostream& s) : stream(&s) {} + }; + +public: + // --- Глобальная конфигурация маршрутизации --- + + // Устанавливает поток по умолчанию, если другие правила не совпали + static void setGlobalOutputStream(std::ostream& stream) { + std::lock_guard lock(config_mutex_); + global_stream_ = get_or_create_synced_stream(stream); + reconfigureAllLoggers(); + } + + // Устанавливает поток для всех уровней в указанной категории + static void setCategoryOutputStream(const std::string& category, std::ostream& stream) { + std::lock_guard lock(config_mutex_); + category_defaults_[category] = get_or_create_synced_stream(stream); + if (auto it = registry_.find(category); it != registry_.end()) { + it->second->resolveOutputStreams(); + } + } + + // Устанавливает поток для конкретного уровня в конкретной категории + static void setSeverityOutputStream(const std::string& category, Severity severity, std::ostream& stream) { + std::lock_guard lock(config_mutex_); + severity_specific_[category][severity] = get_or_create_synced_stream(stream); + if (auto it = registry_.find(category); it != registry_.end()) { + it->second->resolveOutputStreams(); + } + } + + // Получить логгер по категории + static DaemonLogger& get(const std::string& category) { + std::lock_guard lock(registry_mutex_); + auto it = registry_.find(category); + if (it == registry_.end()) { + auto logger = std::unique_ptr(new DaemonLogger(category)); + logger->resolveOutputStreams(); // Разрешаем маршруты при создании + auto [inserted_it, _] = registry_.try_emplace(category, std::move(logger)); + return *(inserted_it->second); + } + return *(it->second); + } + + // --- Настройка экземпляра логгера --- + void setMinSeverity(Severity level) { min_severity_ = level; } + void suppress() { suppressed_ = true; } + void unsuppress() { suppressed_ = false; } + + // --- Основной метод логирования --- + template + void log(Severity severity, const std::string& source_location, const std::string& format, Args... args) const { + if (suppressed_ || severity < min_severity_) { + return; + } + + // 1. Быстрое получение нужного потока (O(1)) + auto& target_synced_stream = resolved_streams_[static_cast(severity)]; + if (!target_synced_stream || !target_synced_stream->stream) { + return; // Маршрут не настроен / ведет в никуда + } + + // 2. Форматирование сообщения (до блокировки мьютекса) + std::string user_message; + try { + user_message = (boost::format(format) % ... % args).str(); + } catch (const boost::io::format_error& e) { + user_message = "!!! LOG FORMATTING ERROR: " + std::string(e.what()) + " !!!"; + } + + std::ostringstream final_message; + final_message << currentTime() << " [" << severityToString(severity) << "]" + << " [" << category_ << "]" + << " [" << source_location << "] " + << user_message; + + // 3. Потокобезопасная запись в целевой поток + { + std::lock_guard lock(target_synced_stream->mtx); + *target_synced_stream->stream << final_message.str() << std::endl; + } + } + +private: + explicit DaemonLogger(std::string category) : category_(std::move(category)) {} + + // Разрешает и кэширует маршруты для этого экземпляра логгера + void resolveOutputStreams() { + std::lock_guard lock(config_mutex_); + for (size_t i = 0; i < static_cast(Severity::_Count); ++i) { + Severity s = static_cast(i); + // Правило 1: Ищем [категория, уровень] + if (auto cat_it = severity_specific_.find(category_); cat_it != severity_specific_.end()) { + if (auto sev_it = cat_it->second.find(s); sev_it != cat_it->second.end()) { + resolved_streams_[i] = sev_it->second; + continue; + } + } + // Правило 2: Ищем [категория] + if (auto cat_it = category_defaults_.find(category_); cat_it != category_defaults_.end()) { + resolved_streams_[i] = cat_it->second; + continue; + } + // Правило 3: Используем глобальный поток + resolved_streams_[i] = global_stream_; + } + } + + // --- Статические хелперы и данные --- + static std::shared_ptr get_or_create_synced_stream(std::ostream& stream) { + auto it = stream_registry_.find(&stream); + if (it != stream_registry_.end()) { + return it->second; + } + auto synced_stream = std::make_shared(stream); + stream_registry_[&stream] = synced_stream; + return synced_stream; + } + + static void reconfigureAllLoggers() { + std::lock_guard lock(registry_mutex_); + for (auto const& [key, val] : registry_) { + val->resolveOutputStreams(); + } + } + + static std::string currentTime() { + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + std::ostringstream oss; + oss << std::put_time(std::localtime(&now_c), "%Y-%m-%d %H:%M:%S"); + return oss.str(); + } + static std::string severityToString(Severity severity) { + switch (severity) { + case Severity::Debug: return "DEBUG"; + case Severity::Info: return "INFO "; + case Severity::Warning: return "WARN "; + case Severity::Error: return "ERROR"; + default: return "UNKNW"; + } + } + + // --- Данные экземпляра --- + std::string category_; + Severity min_severity_ = Severity::Debug; + bool suppressed_ = false; + std::array, static_cast(Severity::_Count)> resolved_streams_; + + // --- Статические данные класса --- + inline static std::unordered_map> registry_; + inline static std::mutex registry_mutex_; + + inline static std::mutex config_mutex_; + // Хранилище правил маршрутизации + inline static std::shared_ptr global_stream_; + inline static std::unordered_map> category_defaults_; + inline static std::unordered_map>> severity_specific_; + // Реестр ostream'ов, чтобы не создавать дубликаты SynchronizedStream для одного и того же потока + inline static std::unordered_map> stream_registry_; +}; + + +// --- Макросы для удобного вызова --- +#define __LOG_IMPL(category, severity, format, ...) \ + do { \ + std::string loc = std::string(__func__) + ":" + std::to_string(__LINE__); \ + DaemonLogger::get(category).log(severity, loc, format, ##__VA_ARGS__); \ + } while (0) + +#define LOG_DEBUG(category, format, ...) __LOG_IMPL(category, DaemonLogger::Severity::Debug, format, ##__VA_ARGS__) +#define LOG_INFO(category, format, ...) __LOG_IMPL(category, DaemonLogger::Severity::Info, format, ##__VA_ARGS__) +#define LOG_WARNING(category, format, ...) __LOG_IMPL(category, DaemonLogger::Severity::Warning, format, ##__VA_ARGS__) +#define LOG_ERROR(category, format, ...) __LOG_IMPL(category, DaemonLogger::Severity::Error, format, ##__VA_ARGS__) diff --git a/main.c b/main.c new file mode 100644 index 0000000..a009efe --- /dev/null +++ b/main.c @@ -0,0 +1,49 @@ +#include "glogger.hpp" +#include + +// Создаем файлы для логов +std::ofstream network_log_file("network.log"); +std::ofstream critical_errors_file("critical.log"); + +int main() { + // --- Настраиваем сложную маршрутизацию --- + + // 1. По умолчанию все логи идут в стандартный вывод (консоль) + DaemonLogger::setGlobalOutputStream(std::cout); + + // 2. Все логи категории "Database", кроме ошибок, идут в /dev/null (игнорируются) + // Для этого создадим "пустой" поток + std::ofstream null_stream; + null_stream.setstate(std::ios_base::badbit); // Делаем поток невалидным + DaemonLogger::setCategoryOutputStream("Database", null_stream); + + // 3. Все логи категории "Network" идут в свой файл network.log + DaemonLogger::setCategoryOutputStream("Network", network_log_file); + + // 4. Но ОШИБКИ из "Network" И ОШИБКИ из "Database" идут в специальный файл critical.log + DaemonLogger::setSeverityOutputStream("Network", DaemonLogger::Severity::Error, critical_errors_file); + DaemonLogger::setSeverityOutputStream("Database", DaemonLogger::Severity::Error, critical_errors_file); + + // --- Начинаем логирование --- + + LOG_INFO("Main", "Система логирования настроена. Запускаем приложение."); + + // Логирование из разных категорий + LOG_DEBUG("Network", "Инициализация сетевого стека..."); // Пойдет в network.log + LOG_INFO("Network", "Соединение с %1% установлено.", "192.168.1.1"); // Пойдет в network.log + + LOG_INFO("Database", "Подключение к базе данных..."); // Пойдет в null_stream (никуда) + LOG_WARNING("Database", "Медленный запрос обнаружен."); // Пойдет в null_stream (никуда) + + LOG_ERROR("Network", "Таймаут соединения с хостом %1%", "example.com"); // Пойдет в critical.log + LOG_ERROR("Database", "Не удалось выполнить транзакцию."); // Пойдет в critical.log + + LOG_INFO("Scheduler", "Задача #%1% выполнена.", 123); // Правил нет, пойдет в std::cout + LOG_ERROR("Scheduler", "Не удалось запустить задачу."); // Правил нет, пойдет в std::cout + + // Установка минимального уровня для категории + DaemonLogger::get("Network").setMinSeverity(DaemonLogger::Severity::Warning); + LOG_INFO("Network", "Эта информация больше не будет записана."); // Не пройдет из-за min_severity + + return 0; +}