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:
@@ -3,6 +3,8 @@
|
||||
*/
|
||||
|
||||
#include "CommunicationRouter.hpp"
|
||||
|
||||
#define TAG "CommRouter"
|
||||
#include "../../ConfigManager/ConfigManager.hpp"
|
||||
#include "../../OTAManager/OTAManager.hpp"
|
||||
#include "../../Networking/Networking.hpp"
|
||||
@@ -39,11 +41,11 @@ CommunicationRouter::CommunicationRouter(ConfigManager& configManager,
|
||||
CommunicationRouter::~CommunicationRouter() {}
|
||||
|
||||
void CommunicationRouter::begin() {
|
||||
LOG_INFO("Initializing Communication Router v4.0 (Modular)");
|
||||
LOG_INFO(TAG, "Initializing Communication Router v4.0 (Modular)");
|
||||
|
||||
// 🔥 CRITICAL: Initialize WebSocket FIRST to ensure it's always set up
|
||||
// Even if MQTT fails, we want WebSocket to work!
|
||||
LOG_INFO("Setting up WebSocket server...");
|
||||
LOG_INFO(TAG, "Setting up WebSocket server...");
|
||||
|
||||
// Initialize WebSocket server
|
||||
_wsServer.begin();
|
||||
@@ -54,11 +56,11 @@ void CommunicationRouter::begin() {
|
||||
// 🔥 CRITICAL FIX: Attach WebSocket handler to AsyncWebServer
|
||||
// This MUST happen before any potential failures!
|
||||
_server.addHandler(&_webSocket);
|
||||
LOG_INFO("✅ WebSocket handler attached to AsyncWebServer on /ws");
|
||||
LOG_INFO(TAG, "✅ WebSocket handler attached to AsyncWebServer on /ws");
|
||||
|
||||
//Now initialize MQTT client (can fail without breaking WebSocket)
|
||||
try {
|
||||
LOG_INFO("Setting up MQTT client...");
|
||||
LOG_INFO(TAG, "Setting up MQTT client...");
|
||||
_mqttClient.begin();
|
||||
_mqttClient.setCallback([this](const String& topic, const String& payload) {
|
||||
onMqttMessage(topic, payload);
|
||||
@@ -73,23 +75,36 @@ void CommunicationRouter::begin() {
|
||||
logTopic
|
||||
);
|
||||
|
||||
// Apply MQTT log level from config
|
||||
uint8_t mqttLogLevel = _configManager.getMqttLogLevel();
|
||||
Logging::setMqttLogLevel((Logging::LogLevel)mqttLogLevel);
|
||||
LOG_INFO("MQTT logging enabled with level %d on topic: %s", mqttLogLevel, logTopic.c_str());
|
||||
// Apply log levels from config for all three channels
|
||||
Logging::setSerialLevel((Logging::LogLevel)_configManager.getSerialLogLevel());
|
||||
Logging::setMqttLevel((Logging::LogLevel)_configManager.getMqttLogLevel());
|
||||
Logging::setSdLevel((Logging::LogLevel)_configManager.getSdLogLevel());
|
||||
LOG_INFO(TAG, "Log levels applied — Serial:%d MQTT:%d SD:%d",
|
||||
_configManager.getSerialLogLevel(),
|
||||
_configManager.getMqttLogLevel(),
|
||||
_configManager.getSdLogLevel());
|
||||
|
||||
LOG_INFO("✅ MQTT client initialized");
|
||||
// Silence MQTT-internal subsystems on the MQTT channel to prevent log storms.
|
||||
// These systems generate logs while sending logs — suppress them over MQTT only.
|
||||
Logging::setSubsystemMqttLevel("MQTTClient", Logging::NONE);
|
||||
Logging::setSubsystemMqttLevel("CommRouter", Logging::WARNING);
|
||||
Logging::setSubsystemMqttLevel("Logger", Logging::NONE);
|
||||
|
||||
LOG_INFO(TAG, "✅ MQTT client initialized");
|
||||
} catch (...) {
|
||||
LOG_ERROR("❌ MQTT initialization failed, but WebSocket is still available");
|
||||
LOG_ERROR(TAG, "❌ MQTT initialization failed, but WebSocket is still available");
|
||||
}
|
||||
|
||||
// Wire up SD logging channel (requires FileManager to be set first via setFileManagerReference)
|
||||
// SD callback is registered lazily in setFileManagerReference once the pointer is available
|
||||
|
||||
// 🔥 CRITICAL FIX: Connect ClientManager to CommandHandler
|
||||
_commandHandler.setClientManagerReference(&_clientManager);
|
||||
LOG_INFO("ClientManager reference set for CommandHandler");
|
||||
LOG_INFO(TAG, "ClientManager reference set for CommandHandler");
|
||||
|
||||
// 🔥 Set CommunicationRouter reference for MQTT control commands
|
||||
_commandHandler.setCommunicationRouterReference(this);
|
||||
LOG_INFO("CommunicationRouter reference set for CommandHandler");
|
||||
LOG_INFO(TAG, "CommunicationRouter reference set for CommandHandler");
|
||||
|
||||
// Setup command handler response callback
|
||||
_commandHandler.setResponseCallback([this](const String& response, const CommandHandler::MessageContext& context) {
|
||||
@@ -97,30 +112,30 @@ void CommunicationRouter::begin() {
|
||||
});
|
||||
|
||||
// Initialize HTTP Request Handler
|
||||
LOG_INFO("Setting up HTTP REST API...");
|
||||
LOG_INFO(TAG, "Setting up HTTP REST API...");
|
||||
_httpHandler.begin();
|
||||
_httpHandler.setCommandHandlerReference(&_commandHandler);
|
||||
LOG_INFO("✅ HTTP REST API initialized");
|
||||
LOG_INFO(TAG, "✅ HTTP REST API initialized");
|
||||
|
||||
// Initialize Settings Web Server
|
||||
LOG_INFO("Setting up Settings Web Server...");
|
||||
LOG_INFO(TAG, "Setting up Settings Web Server...");
|
||||
_settingsServer.begin();
|
||||
LOG_INFO("✅ Settings Web Server initialized at /settings");
|
||||
LOG_INFO(TAG, "✅ Settings Web Server initialized at /settings");
|
||||
|
||||
// Initialize UART Command Handler
|
||||
LOG_INFO("Setting up UART Command Handler...");
|
||||
LOG_INFO(TAG, "Setting up UART Command Handler...");
|
||||
_uartHandler.begin();
|
||||
_uartHandler.setCallback([this](JsonDocument& message) {
|
||||
onUartMessage(message);
|
||||
});
|
||||
LOG_INFO("✅ UART Command Handler initialized (TX: GPIO12, RX: GPIO13)");
|
||||
LOG_INFO(TAG, "✅ UART Command Handler initialized (TX: GPIO12, RX: GPIO13)");
|
||||
|
||||
LOG_INFO("Communication Router initialized with modular architecture");
|
||||
LOG_INFO(" • MQTT: AsyncMqttClient");
|
||||
LOG_INFO(" • WebSocket: Multi-client support");
|
||||
LOG_INFO(" • HTTP REST API: /api endpoints");
|
||||
LOG_INFO(" • UART: External device control");
|
||||
LOG_INFO(" • Settings Page: /settings");
|
||||
LOG_INFO(TAG, "Communication Router initialized with modular architecture");
|
||||
LOG_INFO(TAG, " • MQTT: AsyncMqttClient");
|
||||
LOG_INFO(TAG, " • WebSocket: Multi-client support");
|
||||
LOG_INFO(TAG, " • HTTP REST API: /api endpoints");
|
||||
LOG_INFO(TAG, " • UART: External device control");
|
||||
LOG_INFO(TAG, " • Settings Page: /settings");
|
||||
}
|
||||
|
||||
void CommunicationRouter::loop() {
|
||||
@@ -136,6 +151,14 @@ void CommunicationRouter::setPlayerReference(Player* player) {
|
||||
void CommunicationRouter::setFileManagerReference(FileManager* fm) {
|
||||
_fileManager = fm;
|
||||
_commandHandler.setFileManagerReference(fm);
|
||||
|
||||
// Register SD log channel now that FileManager is available
|
||||
if (fm != nullptr) {
|
||||
Logging::setSdWriteCallback([fm](const String& line) {
|
||||
fm->appendLine("/logs/vesper.log", line);
|
||||
});
|
||||
LOG_INFO(TAG, "SD log channel registered -> /logs/vesper.log");
|
||||
}
|
||||
}
|
||||
|
||||
void CommunicationRouter::setTimeKeeperReference(Timekeeper* tk) {
|
||||
@@ -155,11 +178,11 @@ void CommunicationRouter::setTelemetryReference(Telemetry* telemetry) {
|
||||
void CommunicationRouter::setupUdpDiscovery() {
|
||||
uint16_t discoveryPort = _configManager.getNetworkConfig().discoveryPort;
|
||||
if (_udp.listen(discoveryPort)) {
|
||||
LOG_INFO("UDP discovery listening on port %u", discoveryPort);
|
||||
LOG_INFO(TAG, "UDP discovery listening on port %u", discoveryPort);
|
||||
|
||||
_udp.onPacket([this](AsyncUDPPacket packet) {
|
||||
String msg = String((const char*)packet.data(), packet.length());
|
||||
LOG_DEBUG("UDP from %s:%u -> %s",
|
||||
LOG_DEBUG(TAG, "UDP from %s:%u -> %s",
|
||||
packet.remoteIP().toString().c_str(),
|
||||
packet.remotePort(),
|
||||
msg.c_str());
|
||||
@@ -200,7 +223,7 @@ void CommunicationRouter::setupUdpDiscovery() {
|
||||
packet.remoteIP(), packet.remotePort());
|
||||
});
|
||||
} else {
|
||||
LOG_ERROR("Failed to start UDP discovery.");
|
||||
LOG_ERROR(TAG, "Failed to start UDP discovery.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,22 +242,24 @@ size_t CommunicationRouter::getWebSocketClientCount() const {
|
||||
bool CommunicationRouter::isHealthy() const {
|
||||
// Check if required references are set
|
||||
if (!_player || !_fileManager || !_timeKeeper) {
|
||||
LOG_DEBUG("CommunicationRouter: Unhealthy - Missing references");
|
||||
LOG_WARNING(TAG, "Unhealthy - missing subsystem references (player=%d fileManager=%d timeKeeper=%d)",
|
||||
_player != nullptr, _fileManager != nullptr, _timeKeeper != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check network connectivity first — no point checking connections without a network
|
||||
if (!_networking.isConnected()) {
|
||||
LOG_WARNING(TAG, "Unhealthy - no network connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if at least one protocol is connected
|
||||
if (!isMqttConnected() && !hasActiveWebSocketClients()) {
|
||||
LOG_DEBUG("CommunicationRouter: Unhealthy - No active connections");
|
||||
LOG_WARNING(TAG, "Unhealthy - no active connections (MQTT=%d, WebSocket=%d)",
|
||||
isMqttConnected(), hasActiveWebSocketClients());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check network connectivity
|
||||
if (!_networking.isConnected()) {
|
||||
LOG_DEBUG("CommunicationRouter: Unhealthy - No network connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -270,9 +295,9 @@ void CommunicationRouter::broadcastToAllWebSocketClients(const JsonDocument& mes
|
||||
void CommunicationRouter::publishToMqtt(const String& data) {
|
||||
if (_mqttClient.isConnected()) {
|
||||
_mqttClient.publish("data", data, 0, false);
|
||||
LOG_DEBUG("Published to MQTT: %s", data.c_str());
|
||||
LOG_DEBUG(TAG, "Published to MQTT: %s", data.c_str());
|
||||
} else {
|
||||
LOG_ERROR("MQTT Not Connected! Message Failed: %s", data.c_str());
|
||||
LOG_ERROR(TAG, "MQTT Not Connected! Message Failed: %s", data.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,29 +319,29 @@ void CommunicationRouter::sendBellOverloadNotification(const std::vector<uint8_t
|
||||
overloadMsg["payload"]["severity"] = severity;
|
||||
broadcastStatus(overloadMsg);
|
||||
|
||||
LOG_WARNING("Bell overload notification sent: %d bells, severity: %s",
|
||||
LOG_WARNING(TAG, "Bell overload notification sent: %d bells, severity: %s",
|
||||
bellNumbers.size(), severity.c_str());
|
||||
}
|
||||
|
||||
void CommunicationRouter::onNetworkConnected() {
|
||||
LOG_DEBUG("Network connected - notifying MQTT client");
|
||||
LOG_DEBUG(TAG, "Network connected - notifying MQTT client");
|
||||
_mqttClient.onNetworkConnected();
|
||||
}
|
||||
|
||||
void CommunicationRouter::onNetworkDisconnected() {
|
||||
LOG_DEBUG("Network disconnected - notifying MQTT client");
|
||||
LOG_DEBUG(TAG, "Network disconnected - notifying MQTT client");
|
||||
_mqttClient.onNetworkDisconnected();
|
||||
}
|
||||
|
||||
void CommunicationRouter::onMqttMessage(const String& topic, const String& payload) {
|
||||
LOG_DEBUG("MQTT message received: %s", payload.c_str());
|
||||
LOG_DEBUG(TAG, "MQTT message received: %s", payload.c_str());
|
||||
|
||||
// Parse JSON
|
||||
StaticJsonDocument<2048> doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR("Failed to parse MQTT JSON: %s", error.c_str());
|
||||
LOG_ERROR(TAG, "Failed to parse MQTT JSON: %s", error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -330,7 +355,7 @@ void CommunicationRouter::onMqttMessage(const String& topic, const String& paylo
|
||||
void CommunicationRouter::onWebSocketMessage(uint32_t clientId, const JsonDocument& message) {
|
||||
// Extract command for logging
|
||||
String cmd = message["cmd"] | "unknown";
|
||||
LOG_INFO("📨 WebSocket message from client #%u: cmd=%s", clientId, cmd.c_str());
|
||||
LOG_INFO(TAG, "📨 WebSocket message from client #%u: cmd=%s", clientId, cmd.c_str());
|
||||
|
||||
// Create message context for WebSocket with client ID
|
||||
CommandHandler::MessageContext context(CommandHandler::MessageSource::WEBSOCKET, clientId);
|
||||
@@ -339,7 +364,7 @@ void CommunicationRouter::onWebSocketMessage(uint32_t clientId, const JsonDocume
|
||||
JsonDocument& mutableDoc = const_cast<JsonDocument&>(message);
|
||||
_commandHandler.processCommand(mutableDoc, context);
|
||||
|
||||
LOG_DEBUG("WebSocket message from client #%u processed", clientId);
|
||||
LOG_DEBUG(TAG, "WebSocket message from client #%u processed", clientId);
|
||||
}
|
||||
|
||||
void CommunicationRouter::onUartMessage(JsonDocument& message) {
|
||||
@@ -360,12 +385,12 @@ void CommunicationRouter::onUartMessage(JsonDocument& message) {
|
||||
|
||||
if (!allowed) {
|
||||
// Silently ignore - do NOT send error response to avoid feedback loop
|
||||
LOG_DEBUG("UART: Ignoring non-whitelisted command (cmd=%s, action=%s)",
|
||||
LOG_DEBUG(TAG, "UART: Ignoring non-whitelisted command (cmd=%s, action=%s)",
|
||||
cmd.c_str(), action.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("🔌 UART command received: cmd=%s, action=%s", cmd.c_str(), action.c_str());
|
||||
LOG_INFO(TAG, "🔌 UART command received: cmd=%s, action=%s", cmd.c_str(), action.c_str());
|
||||
|
||||
// Create message context for UART
|
||||
CommandHandler::MessageContext context(CommandHandler::MessageSource::UART);
|
||||
@@ -373,20 +398,20 @@ void CommunicationRouter::onUartMessage(JsonDocument& message) {
|
||||
// Forward to command handler
|
||||
_commandHandler.processCommand(message, context);
|
||||
|
||||
LOG_DEBUG("UART message processed");
|
||||
LOG_DEBUG(TAG, "UART message processed");
|
||||
}
|
||||
|
||||
void CommunicationRouter::sendResponse(const String& response, const CommandHandler::MessageContext& context) {
|
||||
if (context.source == CommandHandler::MessageSource::MQTT) {
|
||||
LOG_DEBUG("↗️ Sending response via MQTT: %s", response.c_str());
|
||||
LOG_DEBUG(TAG, "↗️ Sending response via MQTT: %s", response.c_str());
|
||||
publishToMqtt(response);
|
||||
} else if (context.source == CommandHandler::MessageSource::WEBSOCKET) {
|
||||
LOG_DEBUG("↗️ Sending response to WebSocket client #%u: %s", context.clientId, response.c_str());
|
||||
LOG_DEBUG(TAG, "↗️ Sending response to WebSocket client #%u: %s", context.clientId, response.c_str());
|
||||
_wsServer.sendToClient(context.clientId, response);
|
||||
} else if (context.source == CommandHandler::MessageSource::UART) {
|
||||
LOG_DEBUG("↗️ Sending response via UART: %s", response.c_str());
|
||||
LOG_DEBUG(TAG, "↗️ Sending response via UART: %s", response.c_str());
|
||||
_uartHandler.send(response);
|
||||
} else {
|
||||
LOG_ERROR("❌ Unknown message source for response routing!");
|
||||
LOG_ERROR(TAG, "❌ Unknown message source for response routing!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user