#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__)