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>
146 lines
9.7 KiB
C++
146 lines
9.7 KiB
C++
/*
|
|
* ═══════════════════════════════════════════════════════════════════════════════════
|
|
* 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 <Arduino.h>
|
|
#include <map>
|
|
#include <functional>
|
|
|
|
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<void(const String& topic, const String& payload, int qos)>;
|
|
using SdWriteCallback = std::function<void(const String& line)>;
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// 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<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);
|
|
};
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
// 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
|