/* * ═══════════════════════════════════════════════════════════════════════════════════ * LOGGING.HPP - Subsystem-Aware Centralized Logging System * ═══════════════════════════════════════════════════════════════════════════════════ * * 📝 THE INFORMATION CHRONICLER OF VESPER 📝 * * Three independent output channels, each with their own level: * • Serial — USB debugging, local connection * • MQTT — Remote troubleshooting via web dashboard * • SD — Persistent log storage for post-mortem analysis * * Per-subsystem filtering: each subsystem tag can have its own level * overrides per channel. If no override is set, the global channel * level applies. Set a tag's level to NONE on a specific channel to * silence it entirely on that channel (e.g. MQTT internals on MQTT). * * Usage in each .cpp file: * #define TAG "BellEngine" // one line at the top * LOG_INFO(TAG, "Ring scheduled"); // all calls include the tag * * The JSON payload sent over MQTT includes the subsystem field: * {"level":"WARNING","subsystem":"BellEngine","message":"...","timestamp":12345} * * 📋 VERSION: 3.0 (Subsystem-aware logging) * 📅 DATE: 2025 * 👨‍💻 AUTHOR: Advanced Bell Systems * ═══════════════════════════════════════════════════════════════════════════════════ */ #ifndef LOGGING_HPP #define LOGGING_HPP #include #include #include class Logging { public: // ═══════════════════════════════════════════════════════════════════════════════ // LOG LEVELS // ═══════════════════════════════════════════════════════════════════════════════ enum LogLevel { NONE = 0, // No output ERROR = 1, // Errors only WARNING = 2, // Warnings and errors INFO = 3, // Info, warnings, errors DEBUG = 4, // Debug detail VERBOSE = 5 // Everything }; // ═══════════════════════════════════════════════════════════════════════════════ // CALLBACK TYPES // ═══════════════════════════════════════════════════════════════════════════════ using MqttPublishCallback = std::function; using SdWriteCallback = std::function; // ═══════════════════════════════════════════════════════════════════════════════ // GLOBAL CHANNEL LEVELS // Set the baseline level for each output channel. // Per-subsystem overrides take precedence when set. // ═══════════════════════════════════════════════════════════════════════════════ static void setSerialLevel(LogLevel level); static void setMqttLevel(LogLevel level); static void setSdLevel(LogLevel level); static LogLevel getSerialLevel(); static LogLevel getMqttLevel(); static LogLevel getSdLevel(); // Legacy compatibility (maps to serial level) static void setLevel(LogLevel level) { setSerialLevel(level); } static LogLevel getLevel() { return getSerialLevel(); } static void setMqttLogLevel(LogLevel level) { setMqttLevel(level); } static LogLevel getMqttLogLevel() { return getMqttLevel(); } // ═══════════════════════════════════════════════════════════════════════════════ // PER-SUBSYSTEM LEVEL OVERRIDES // Call these at startup to silence or focus specific subsystems per channel. // Pass NONE to completely silence a subsystem on a channel. // Pass a level to cap that subsystem at that level on that channel. // ═══════════════════════════════════════════════════════════════════════════════ static void setSubsystemSerialLevel(const char* tag, LogLevel level); static void setSubsystemMqttLevel(const char* tag, LogLevel level); static void setSubsystemSdLevel(const char* tag, LogLevel level); // ═══════════════════════════════════════════════════════════════════════════════ // OUTPUT CHANNEL REGISTRATION // ═══════════════════════════════════════════════════════════════════════════════ static void setMqttPublishCallback(MqttPublishCallback callback, const String& logTopic); static void setSdWriteCallback(SdWriteCallback callback); // ═══════════════════════════════════════════════════════════════════════════════ // LOGGING FUNCTIONS (tag = subsystem name, e.g. "BellEngine") // ═══════════════════════════════════════════════════════════════════════════════ static void error(const char* tag, const char* format, ...); static void warning(const char* tag, const char* format, ...); static void info(const char* tag, const char* format, ...); static void debug(const char* tag, const char* format, ...); static void verbose(const char* tag, const char* format, ...); // ═══════════════════════════════════════════════════════════════════════════════ // UTILITIES // ═══════════════════════════════════════════════════════════════════════════════ static bool isLevelEnabled(LogLevel level); static String levelToString(LogLevel level); private: // Global channel levels static LogLevel _serialLevel; static LogLevel _mqttLevel; static LogLevel _sdLevel; // Per-subsystem overrides per channel (tag -> level) // A value of NONE means "suppress this subsystem on this channel entirely" static std::map _serialOverrides; static std::map _mqttOverrides; static std::map _sdOverrides; // Output channel callbacks static MqttPublishCallback _mqttCallback; static SdWriteCallback _sdCallback; static String _mqttLogTopic; // Core internal methods static void log(LogLevel level, const char* levelStr, const char* tag, const char* format, va_list args); static void publishToMqtt(LogLevel level, const char* levelStr, const char* tag, const char* message); static void writeToSd(LogLevel level, const char* levelStr, const char* tag, const char* message); // Resolve effective level for a tag on a channel static LogLevel resolveLevel(const char* tag, LogLevel globalLevel, const std::map& overrides); }; // ═══════════════════════════════════════════════════════════════════════════════════ // MACROS // Each .cpp file defines: #define TAG "SubsystemName" // Then uses: LOG_INFO(TAG, "message %d", value) // ═══════════════════════════════════════════════════════════════════════════════════ #define LOG_ERROR(tag, ...) Logging::error(tag, __VA_ARGS__) #define LOG_WARNING(tag, ...) Logging::warning(tag, __VA_ARGS__) #define LOG_INFO(tag, ...) Logging::info(tag, __VA_ARGS__) #define LOG_DEBUG(tag, ...) Logging::debug(tag, __VA_ARGS__) #define LOG_VERBOSE(tag, ...) Logging::verbose(tag, __VA_ARGS__) #endif