feat: Add per-subsystem log tags to all firmware modules

Refactored logging system to require a TAG as first argument on all
LOG_* macros, enabling per-subsystem log filtering and cleaner output.
Each subsystem now defines its own TAG (e.g. "BellEngine", "Player").
Also overhauled Logging.hpp/cpp with improved level control and output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 17:31:28 +02:00
parent c656835d8e
commit fe6b1d871a
26 changed files with 1334 additions and 1074 deletions

View File

@@ -1,14 +1,28 @@
/*
* ═══════════════════════════════════════════════════════════════════════════════════
* LOGGING.HPP - Centralized Logging System
* LOGGING.HPP - Subsystem-Aware Centralized Logging System
* ═══════════════════════════════════════════════════════════════════════════════════
*
*
* 📝 THE INFORMATION CHRONICLER OF VESPER 📝
*
* This header provides a unified logging interface with multiple levels,
* timestamps, and comprehensive debugging support throughout the system.
*
* 📋 VERSION: 2.0 (Enhanced logging system)
*
* 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
* ═══════════════════════════════════════════════════════════════════════════════════
@@ -18,67 +32,114 @@
#define LOGGING_HPP
#include <Arduino.h>
// Forward declaration
class MQTTAsyncClient;
#include <map>
#include <functional>
class Logging {
public:
// Log Levels
// ═══════════════════════════════════════════════════════════════════════════════
// LOG LEVELS
// ═══════════════════════════════════════════════════════════════════════════════
enum LogLevel {
NONE = 0, // No logs
ERROR = 1, // Errors only
WARNING = 2, // Warnings and errors
INFO = 3, // Info, warnings, and errors
DEBUG = 4, // Debug logs. Really high level (full debugging)
VERBOSE = 5 // Nearly every command gets printed
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
};
// MQTT Log Publishing Callback
// ═══════════════════════════════════════════════════════════════════════════════
// CALLBACK TYPES
// ═══════════════════════════════════════════════════════════════════════════════
using MqttPublishCallback = std::function<void(const String& topic, const String& payload, int qos)>;
using SdWriteCallback = std::function<void(const String& line)>;
private:
static LogLevel currentLevel;
static LogLevel mqttLogLevel;
static MqttPublishCallback mqttPublishCallback;
static String mqttLogTopic;
// ═══════════════════════════════════════════════════════════════════════════════
// 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);
public:
// Set the active log level
static void setLevel(LogLevel level);
static LogLevel getSerialLevel();
static LogLevel getMqttLevel();
static LogLevel getSdLevel();
// Get current log level
static LogLevel getLevel();
// 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(); }
// Set MQTT log level (independent from serial logging)
static void setMqttLogLevel(LogLevel level);
// ═══════════════════════════════════════════════════════════════════════════════
// 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);
// Get MQTT log level
static LogLevel getMqttLogLevel();
// Set MQTT callback for publishing logs
// ═══════════════════════════════════════════════════════════════════════════════
// OUTPUT CHANNEL REGISTRATION
// ═══════════════════════════════════════════════════════════════════════════════
static void setMqttPublishCallback(MqttPublishCallback callback, const String& logTopic);
static void setSdWriteCallback(SdWriteCallback callback);
// Logging functions
static void error(const char* format, ...);
static void warning(const char* format, ...);
static void info(const char* format, ...);
static void debug(const char* format, ...);
static void verbose(const char* format, ...);
// ═══════════════════════════════════════════════════════════════════════════════
// 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, ...);
// Check if level is enabled (for conditional logging)
// ═══════════════════════════════════════════════════════════════════════════════
// UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
static bool isLevelEnabled(LogLevel level);
static String levelToString(LogLevel level);
private:
static void log(LogLevel level, const char* levelStr, const char* format, va_list args);
static void publishToMqtt(LogLevel level, const char* levelStr, const char* message);
// 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<String, LogLevel> _serialOverrides;
static std::map<String, LogLevel> _mqttOverrides;
static std::map<String, LogLevel> _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<String, LogLevel>& overrides);
};
// Convenience macros for easier use
#define LOG_ERROR(...) Logging::error(__VA_ARGS__)
#define LOG_WARNING(...) Logging::warning(__VA_ARGS__)
#define LOG_INFO(...) Logging::info(__VA_ARGS__)
#define LOG_DEBUG(...) Logging::debug(__VA_ARGS__)
#define LOG_VERBOSE(...) Logging::verbose(__VA_ARGS__)
// ═══════════════════════════════════════════════════════════════════════════════════
// 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