feat: logging service
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include "spore/services/ClusterService.h"
|
||||
#include "spore/services/TaskService.h"
|
||||
#include "spore/services/StaticFileService.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
Spore::Spore() : ctx(), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
||||
@@ -23,12 +24,12 @@ Spore::~Spore() {
|
||||
|
||||
void Spore::setup() {
|
||||
if (initialized) {
|
||||
Serial.println("[Spore] Already initialized, skipping setup");
|
||||
LOG_INFO(ctx, "Spore", "Already initialized, skipping setup");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("[Spore] Starting Spore framework...");
|
||||
LOG_INFO(ctx, "Spore", "Starting Spore framework...");
|
||||
|
||||
// Initialize core components
|
||||
initializeCore();
|
||||
@@ -37,21 +38,21 @@ void Spore::setup() {
|
||||
registerCoreServices();
|
||||
|
||||
initialized = true;
|
||||
Serial.println("[Spore] Framework setup complete - call begin() to start API server");
|
||||
LOG_INFO(ctx, "Spore", "Framework setup complete - call begin() to start API server");
|
||||
}
|
||||
|
||||
void Spore::begin() {
|
||||
if (!initialized) {
|
||||
Serial.println("[Spore] Framework not initialized, call setup() first");
|
||||
LOG_ERROR(ctx, "Spore", "Framework not initialized, call setup() first");
|
||||
return;
|
||||
}
|
||||
|
||||
if (apiServerStarted) {
|
||||
Serial.println("[Spore] API server already started");
|
||||
LOG_WARN(ctx, "Spore", "API server already started");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("[Spore] Starting API server...");
|
||||
LOG_INFO(ctx, "Spore", "Starting API server...");
|
||||
|
||||
// Start API server
|
||||
startApiServer();
|
||||
@@ -59,12 +60,12 @@ void Spore::begin() {
|
||||
// Print initial task status
|
||||
taskManager.printTaskStatus();
|
||||
|
||||
Serial.println("[Spore] Framework ready!");
|
||||
LOG_INFO(ctx, "Spore", "Framework ready!");
|
||||
}
|
||||
|
||||
void Spore::loop() {
|
||||
if (!initialized) {
|
||||
Serial.println("[Spore] Framework not initialized, call setup() first");
|
||||
LOG_ERROR(ctx, "Spore", "Framework not initialized, call setup() first");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ void Spore::loop() {
|
||||
|
||||
void Spore::addService(std::shared_ptr<Service> service) {
|
||||
if (!service) {
|
||||
Serial.println("[Spore] Warning: Attempted to add null service");
|
||||
LOG_WARN(ctx, "Spore", "Attempted to add null service");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,15 +84,15 @@ void Spore::addService(std::shared_ptr<Service> service) {
|
||||
if (apiServerStarted) {
|
||||
// If API server is already started, register the service immediately
|
||||
apiServer.addService(*service);
|
||||
Serial.printf("[Spore] Added service '%s' to running API server\n", service->getName());
|
||||
LOG_INFO(ctx, "Spore", "Added service '" + String(service->getName()) + "' to running API server");
|
||||
} else {
|
||||
Serial.printf("[Spore] Registered service '%s' (will be added to API server when begin() is called)\n", service->getName());
|
||||
LOG_INFO(ctx, "Spore", "Registered service '" + String(service->getName()) + "' (will be added to API server when begin() is called)");
|
||||
}
|
||||
}
|
||||
|
||||
void Spore::addService(Service* service) {
|
||||
if (!service) {
|
||||
Serial.println("[Spore] Warning: Attempted to add null service");
|
||||
LOG_WARN(ctx, "Spore", "Attempted to add null service");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ void Spore::addService(Service* service) {
|
||||
|
||||
|
||||
void Spore::initializeCore() {
|
||||
Serial.println("[Spore] Initializing core components...");
|
||||
LOG_INFO(ctx, "Spore", "Initializing core components...");
|
||||
|
||||
// Setup WiFi first
|
||||
network.setupWiFi();
|
||||
@@ -109,11 +110,14 @@ void Spore::initializeCore() {
|
||||
// Initialize task manager
|
||||
taskManager.initialize();
|
||||
|
||||
Serial.println("[Spore] Core components initialized");
|
||||
LOG_INFO(ctx, "Spore", "Core components initialized");
|
||||
}
|
||||
|
||||
void Spore::registerCoreServices() {
|
||||
Serial.println("[Spore] Registering core services...");
|
||||
LOG_INFO(ctx, "Spore", "Registering core services...");
|
||||
|
||||
// Create logging service first (other services may use it)
|
||||
auto loggingService = std::make_shared<LoggingService>(ctx, apiServer);
|
||||
|
||||
// Create core services
|
||||
auto nodeService = std::make_shared<NodeService>(ctx, apiServer);
|
||||
@@ -123,28 +127,29 @@ void Spore::registerCoreServices() {
|
||||
auto staticFileService = std::make_shared<StaticFileService>(ctx, apiServer);
|
||||
|
||||
// Add to services list
|
||||
services.push_back(loggingService);
|
||||
services.push_back(nodeService);
|
||||
services.push_back(networkService);
|
||||
services.push_back(clusterService);
|
||||
services.push_back(taskService);
|
||||
services.push_back(staticFileService);
|
||||
|
||||
Serial.println("[Spore] Core services registered");
|
||||
LOG_INFO(ctx, "Spore", "Core services registered");
|
||||
}
|
||||
|
||||
void Spore::startApiServer() {
|
||||
if (apiServerStarted) {
|
||||
Serial.println("[Spore] API server already started");
|
||||
LOG_WARN(ctx, "Spore", "API server already started");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("[Spore] Starting API server...");
|
||||
LOG_INFO(ctx, "Spore", "Starting API server...");
|
||||
|
||||
// Register all services with API server
|
||||
for (auto& service : services) {
|
||||
if (service) {
|
||||
apiServer.addService(*service);
|
||||
Serial.printf("[Spore] Added service '%s' to API server\n", service->getName());
|
||||
LOG_INFO(ctx, "Spore", "Added service '" + String(service->getName()) + "' to API server");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,5 +157,5 @@ void Spore::startApiServer() {
|
||||
apiServer.begin();
|
||||
apiServerStarted = true;
|
||||
|
||||
Serial.println("[Spore] API server started");
|
||||
LOG_INFO(ctx, "Spore", "API server started");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "spore/core/ApiServer.h"
|
||||
#include "spore/Service.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
#include <algorithm>
|
||||
|
||||
const char* ApiServer::methodToStr(int method) {
|
||||
@@ -77,14 +78,14 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
|
||||
|
||||
void ApiServer::addService(Service& service) {
|
||||
services.push_back(service);
|
||||
Serial.printf("[API] Added service: %s\n", service.getName());
|
||||
LOG_INFO(ctx, "API", "Added service: " + String(service.getName()));
|
||||
}
|
||||
|
||||
void ApiServer::begin() {
|
||||
// Register all service endpoints
|
||||
for (auto& service : services) {
|
||||
service.get().registerEndpoints(*this);
|
||||
Serial.printf("[API] Registered endpoints for service: %s\n", service.get().getName());
|
||||
LOG_INFO(ctx, "API", "Registered endpoints for service: " + String(service.get().getName()));
|
||||
}
|
||||
|
||||
server.begin();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "spore/core/ClusterManager.h"
|
||||
#include "spore/internal/Globals.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
|
||||
ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx), taskManager(taskMgr) {
|
||||
// Register callback for node_discovered event
|
||||
@@ -18,11 +19,11 @@ void ClusterManager::registerTasks() {
|
||||
taskManager.registerTask("print_members", ctx.config.print_interval_ms, [this]() { printMemberList(); });
|
||||
taskManager.registerTask("heartbeat", ctx.config.heartbeat_interval_ms, [this]() { heartbeatTaskCallback(); });
|
||||
taskManager.registerTask("update_members_info", ctx.config.member_info_update_interval_ms, [this]() { updateAllMembersInfoTaskCallback(); });
|
||||
Serial.println("[ClusterManager] Registered all cluster tasks");
|
||||
LOG_INFO(ctx, "ClusterManager", "Registered all cluster tasks");
|
||||
}
|
||||
|
||||
void ClusterManager::sendDiscovery() {
|
||||
//Serial.println("[Cluster] Sending discovery packet...");
|
||||
//LOG_DEBUG(ctx, "Cluster", "Sending discovery packet...");
|
||||
ctx.udp->beginPacket("255.255.255.255", ctx.config.udp_port);
|
||||
ctx.udp->write(ClusterProtocol::DISCOVERY_MSG);
|
||||
ctx.udp->endPacket();
|
||||
@@ -36,14 +37,14 @@ void ClusterManager::listenForDiscovery() {
|
||||
if (len > 0) {
|
||||
incoming[len] = 0;
|
||||
}
|
||||
//Serial.printf("[UDP] Packet received: %s\n", incoming);
|
||||
//LOG_DEBUG(ctx, "UDP", "Packet received: " + String(incoming));
|
||||
if (strcmp(incoming, ClusterProtocol::DISCOVERY_MSG) == 0) {
|
||||
//Serial.printf("[UDP] Discovery request from: %s\n", ctx.udp->remoteIP().toString().c_str());
|
||||
//LOG_DEBUG(ctx, "UDP", "Discovery request from: " + ctx.udp->remoteIP().toString());
|
||||
ctx.udp->beginPacket(ctx.udp->remoteIP(), ctx.config.udp_port);
|
||||
String response = String(ClusterProtocol::RESPONSE_MSG) + ":" + ctx.hostname;
|
||||
ctx.udp->write(response.c_str());
|
||||
ctx.udp->endPacket();
|
||||
//Serial.printf("[UDP] Sent response with hostname: %s\n", ctx.hostname.c_str());
|
||||
//LOG_DEBUG(ctx, "UDP", "Sent response with hostname: " + ctx.hostname);
|
||||
} else if (strncmp(incoming, ClusterProtocol::RESPONSE_MSG, strlen(ClusterProtocol::RESPONSE_MSG)) == 0) {
|
||||
char* hostPtr = incoming + strlen(ClusterProtocol::RESPONSE_MSG) + 1;
|
||||
String nodeHost = String(hostPtr);
|
||||
@@ -72,16 +73,13 @@ void ClusterManager::addOrUpdateNode(const String& nodeHost, IPAddress nodeIP) {
|
||||
newNode.lastSeen = millis();
|
||||
updateNodeStatus(newNode, newNode.lastSeen, ctx.config.node_inactive_threshold_ms, ctx.config.node_dead_threshold_ms);
|
||||
memberList[nodeHost] = newNode;
|
||||
Serial.printf("[Cluster] Added node: %s @ %s | Status: %s | last update: 0\n",
|
||||
nodeHost.c_str(),
|
||||
newNode.ip.toString().c_str(),
|
||||
statusToStr(newNode.status));
|
||||
LOG_INFO(ctx, "Cluster", "Added node: " + nodeHost + " @ " + newNode.ip.toString() + " | Status: " + statusToStr(newNode.status) + " | last update: 0");
|
||||
//fetchNodeInfo(nodeIP); // Do not fetch here, handled by periodic task
|
||||
}
|
||||
|
||||
void ClusterManager::fetchNodeInfo(const IPAddress& ip) {
|
||||
if(ip == ctx.localIP) {
|
||||
Serial.println("[Cluster] Skipping fetch for local node");
|
||||
LOG_DEBUG(ctx, "Cluster", "Skipping fetch for local node");
|
||||
return;
|
||||
}
|
||||
unsigned long requestStart = millis();
|
||||
@@ -135,13 +133,13 @@ void ClusterManager::fetchNodeInfo(const IPAddress& ip) {
|
||||
node.labels[k] = v;
|
||||
}
|
||||
}
|
||||
Serial.printf("[Cluster] Fetched info for node: %s @ %s\n", node.hostname.c_str(), ip.toString().c_str());
|
||||
LOG_DEBUG(ctx, "Cluster", "Fetched info for node: " + node.hostname + " @ " + ip.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[Cluster] Failed to fetch info for node @ %s, HTTP code: %d\n", ip.toString().c_str(), httpCode);
|
||||
LOG_ERROR(ctx, "Cluster", "Failed to fetch info for node @ " + ip.toString() + ", HTTP code: " + String(httpCode));
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
@@ -185,7 +183,7 @@ void ClusterManager::removeDeadNodes() {
|
||||
for (auto it = memberList.begin(); it != memberList.end(); ) {
|
||||
unsigned long diff = now - it->second.lastSeen;
|
||||
if (it->second.status == NodeInfo::DEAD && diff > ctx.config.node_dead_threshold_ms) {
|
||||
Serial.printf("[Cluster] Removing node: %s\n", it->second.hostname.c_str());
|
||||
LOG_INFO(ctx, "Cluster", "Removing node: " + it->second.hostname);
|
||||
it = memberList.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@@ -196,13 +194,13 @@ void ClusterManager::removeDeadNodes() {
|
||||
void ClusterManager::printMemberList() {
|
||||
auto& memberList = *ctx.memberList;
|
||||
if (memberList.empty()) {
|
||||
Serial.println("[Cluster] Member List: empty");
|
||||
LOG_INFO(ctx, "Cluster", "Member List: empty");
|
||||
return;
|
||||
}
|
||||
Serial.println("[Cluster] Member List:");
|
||||
LOG_INFO(ctx, "Cluster", "Member List:");
|
||||
for (const auto& pair : memberList) {
|
||||
const NodeInfo& node = pair.second;
|
||||
Serial.printf(" %s @ %s | Status: %s | last seen: %lu\n", node.hostname.c_str(), node.ip.toString().c_str(), statusToStr(node.status), millis() - node.lastSeen);
|
||||
LOG_INFO(ctx, "Cluster", " " + node.hostname + " @ " + node.ip.toString() + " | Status: " + statusToStr(node.status) + " | last seen: " + String(millis() - node.lastSeen));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
#include "spore/core/NetworkManager.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
|
||||
// SSID and password are now configured via Config class
|
||||
|
||||
void NetworkManager::scanWifi() {
|
||||
if (!isScanning) {
|
||||
isScanning = true;
|
||||
Serial.println("[WiFi] Starting WiFi scan...");
|
||||
LOG_INFO(ctx, "WiFi", "Starting WiFi scan...");
|
||||
// Start async WiFi scan
|
||||
WiFi.scanNetworksAsync([this](int networksFound) {
|
||||
Serial.printf("[WiFi] Scan completed, found %d networks\n", networksFound);
|
||||
LOG_INFO(ctx, "WiFi", "Scan completed, found " + String(networksFound) + " networks");
|
||||
this->processAccessPoints();
|
||||
this->isScanning = false;
|
||||
}, true);
|
||||
} else {
|
||||
Serial.println("[WiFi] Scan already in progress...");
|
||||
LOG_WARN(ctx, "WiFi", "Scan already in progress...");
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::processAccessPoints() {
|
||||
int numNetworks = WiFi.scanComplete();
|
||||
if (numNetworks <= 0) {
|
||||
Serial.println("[WiFi] No networks found or scan not complete");
|
||||
LOG_WARN(ctx, "WiFi", "No networks found or scan not complete");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,8 +44,7 @@ void NetworkManager::processAccessPoints() {
|
||||
|
||||
accessPoints.push_back(ap);
|
||||
|
||||
Serial.printf("[WiFi] Found network %d: %s, Ch: %d, RSSI: %d\n",
|
||||
i + 1, ap.ssid.c_str(), ap.channel, ap.rssi);
|
||||
LOG_DEBUG(ctx, "WiFi", "Found network " + String(i + 1) + ": " + ap.ssid + ", Ch: " + String(ap.channel) + ", RSSI: " + String(ap.rssi));
|
||||
}
|
||||
|
||||
// Free the memory used by the scan
|
||||
@@ -77,31 +77,26 @@ void NetworkManager::setHostnameFromMac() {
|
||||
void NetworkManager::setupWiFi() {
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ctx.config.wifi_ssid.c_str(), ctx.config.wifi_password.c_str());
|
||||
Serial.println("[WiFi] Connecting to AP...");
|
||||
LOG_INFO(ctx, "WiFi", "Connecting to AP...");
|
||||
unsigned long startAttemptTime = millis();
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < ctx.config.wifi_connect_timeout_ms) {
|
||||
delay(ctx.config.wifi_retry_delay_ms);
|
||||
Serial.print(".");
|
||||
// Progress dots handled by delay, no logging needed
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.println();
|
||||
Serial.print("[WiFi] Connected to AP, IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
LOG_INFO(ctx, "WiFi", "Connected to AP, IP: " + WiFi.localIP().toString());
|
||||
} else {
|
||||
Serial.println();
|
||||
Serial.println("[WiFi] Failed to connect to AP. Creating AP...");
|
||||
LOG_WARN(ctx, "WiFi", "Failed to connect to AP. Creating AP...");
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(ctx.config.wifi_ssid.c_str(), ctx.config.wifi_password.c_str());
|
||||
Serial.print("[WiFi] AP created, IP: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
LOG_INFO(ctx, "WiFi", "AP created, IP: " + WiFi.softAPIP().toString());
|
||||
}
|
||||
setHostnameFromMac();
|
||||
ctx.udp->begin(ctx.config.udp_port);
|
||||
ctx.localIP = WiFi.localIP();
|
||||
ctx.hostname = WiFi.hostname();
|
||||
Serial.print("[WiFi] Hostname set to: ");
|
||||
Serial.println(ctx.hostname);
|
||||
Serial.printf("[WiFi] UDP listening on port %d\n", ctx.config.udp_port);
|
||||
LOG_INFO(ctx, "WiFi", "Hostname set to: " + ctx.hostname);
|
||||
LOG_INFO(ctx, "WiFi", "UDP listening on port " + String(ctx.config.udp_port));
|
||||
|
||||
// Populate self NodeInfo
|
||||
ctx.self.hostname = ctx.hostname;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "spore/core/TaskManager.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
TaskManager::TaskManager(NodeContext& ctx) : ctx(ctx) {}
|
||||
@@ -31,9 +32,9 @@ void TaskManager::enableTask(const std::string& name) {
|
||||
int idx = findTaskIndex(name);
|
||||
if (idx >= 0) {
|
||||
taskDefinitions[idx].enabled = true;
|
||||
Serial.printf("[TaskManager] Enabled task: %s\n", name.c_str());
|
||||
LOG_INFO(ctx, "TaskManager", "Enabled task: " + String(name.c_str()));
|
||||
} else {
|
||||
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
|
||||
LOG_WARN(ctx, "TaskManager", "Task not found: " + String(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +42,9 @@ void TaskManager::disableTask(const std::string& name) {
|
||||
int idx = findTaskIndex(name);
|
||||
if (idx >= 0) {
|
||||
taskDefinitions[idx].enabled = false;
|
||||
Serial.printf("[TaskManager] Disabled task: %s\n", name.c_str());
|
||||
LOG_INFO(ctx, "TaskManager", "Disabled task: " + String(name.c_str()));
|
||||
} else {
|
||||
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
|
||||
LOG_WARN(ctx, "TaskManager", "Task not found: " + String(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +52,9 @@ void TaskManager::setTaskInterval(const std::string& name, unsigned long interva
|
||||
int idx = findTaskIndex(name);
|
||||
if (idx >= 0) {
|
||||
taskDefinitions[idx].interval = interval;
|
||||
Serial.printf("[TaskManager] Set interval for task %s: %lu ms\n", name.c_str(), interval);
|
||||
LOG_INFO(ctx, "TaskManager", "Set interval for task " + String(name.c_str()) + ": " + String(interval) + " ms");
|
||||
} else {
|
||||
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
|
||||
LOG_WARN(ctx, "TaskManager", "Task not found: " + String(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,27 +85,24 @@ void TaskManager::enableAllTasks() {
|
||||
for (auto& taskDef : taskDefinitions) {
|
||||
taskDef.enabled = true;
|
||||
}
|
||||
Serial.println("[TaskManager] Enabled all tasks");
|
||||
LOG_INFO(ctx, "TaskManager", "Enabled all tasks");
|
||||
}
|
||||
|
||||
void TaskManager::disableAllTasks() {
|
||||
for (auto& taskDef : taskDefinitions) {
|
||||
taskDef.enabled = false;
|
||||
}
|
||||
Serial.println("[TaskManager] Disabled all tasks");
|
||||
LOG_INFO(ctx, "TaskManager", "Disabled all tasks");
|
||||
}
|
||||
|
||||
void TaskManager::printTaskStatus() const {
|
||||
Serial.println("\n[TaskManager] Task Status:");
|
||||
Serial.println("==========================");
|
||||
LOG_INFO(ctx, "TaskManager", "\nTask Status:");
|
||||
LOG_INFO(ctx, "TaskManager", "==========================");
|
||||
|
||||
for (const auto& taskDef : taskDefinitions) {
|
||||
Serial.printf(" %s: %s (interval: %lu ms)\n",
|
||||
taskDef.name.c_str(),
|
||||
taskDef.enabled ? "ENABLED" : "DISABLED",
|
||||
taskDef.interval);
|
||||
LOG_INFO(ctx, "TaskManager", " " + String(taskDef.name.c_str()) + ": " + (taskDef.enabled ? "ENABLED" : "DISABLED") + " (interval: " + String(taskDef.interval) + " ms)");
|
||||
}
|
||||
Serial.println("==========================\n");
|
||||
LOG_INFO(ctx, "TaskManager", "==========================\n");
|
||||
}
|
||||
|
||||
void TaskManager::execute() {
|
||||
|
||||
215
src/spore/services/LoggingService.cpp
Normal file
215
src/spore/services/LoggingService.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "spore/services/LoggingService.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
LoggingService::LoggingService(NodeContext& ctx, ApiServer& apiServer)
|
||||
: ctx(ctx), apiServer(apiServer), currentLogLevel(LogLevel::INFO),
|
||||
serialLoggingEnabled(true) {
|
||||
setupEventHandlers();
|
||||
}
|
||||
|
||||
void LoggingService::setupEventHandlers() {
|
||||
// Register event handlers for different log destinations
|
||||
ctx.on("log/serial", [this](void* data) {
|
||||
this->handleSerialLog(data);
|
||||
});
|
||||
|
||||
// Register for all log events
|
||||
ctx.on("log/debug", [this](void* data) {
|
||||
LogData* logData = static_cast<LogData*>(data);
|
||||
if (logData->level >= currentLogLevel) {
|
||||
this->log(logData->level, logData->component, logData->message);
|
||||
}
|
||||
delete logData;
|
||||
});
|
||||
|
||||
ctx.on("log/info", [this](void* data) {
|
||||
LogData* logData = static_cast<LogData*>(data);
|
||||
if (logData->level >= currentLogLevel) {
|
||||
this->log(logData->level, logData->component, logData->message);
|
||||
}
|
||||
delete logData;
|
||||
});
|
||||
|
||||
ctx.on("log/warn", [this](void* data) {
|
||||
LogData* logData = static_cast<LogData*>(data);
|
||||
if (logData->level >= currentLogLevel) {
|
||||
this->log(logData->level, logData->component, logData->message);
|
||||
}
|
||||
delete logData;
|
||||
});
|
||||
|
||||
ctx.on("log/error", [this](void* data) {
|
||||
LogData* logData = static_cast<LogData*>(data);
|
||||
if (logData->level >= currentLogLevel) {
|
||||
this->log(logData->level, logData->component, logData->message);
|
||||
}
|
||||
delete logData;
|
||||
});
|
||||
}
|
||||
|
||||
void LoggingService::registerEndpoints(ApiServer& api) {
|
||||
LOG_INFO(ctx, "LoggingService", "Registering logging API endpoints");
|
||||
|
||||
// Register API endpoints for logging configuration
|
||||
api.addEndpoint("/api/logging/level", HTTP_GET,
|
||||
[this](AsyncWebServerRequest* request) { handleGetLogLevel(request); },
|
||||
std::vector<ParamSpec>{});
|
||||
LOG_DEBUG(ctx, "LoggingService", "Registered GET /api/logging/level");
|
||||
|
||||
api.addEndpoint("/api/logging/level", HTTP_POST,
|
||||
[this](AsyncWebServerRequest* request) { handleSetLogLevel(request); },
|
||||
std::vector<ParamSpec>{
|
||||
ParamSpec{String("level"), true, String("body"), String("string"),
|
||||
std::vector<String>{"DEBUG", "INFO", "WARN", "ERROR"}, String("INFO")}
|
||||
});
|
||||
LOG_DEBUG(ctx, "LoggingService", "Registered POST /api/logging/level with level parameter");
|
||||
|
||||
api.addEndpoint("/api/logging/config", HTTP_GET,
|
||||
[this](AsyncWebServerRequest* request) { handleGetConfig(request); },
|
||||
std::vector<ParamSpec>{});
|
||||
LOG_DEBUG(ctx, "LoggingService", "Registered GET /api/logging/config");
|
||||
|
||||
LOG_INFO(ctx, "LoggingService", "All logging API endpoints registered successfully");
|
||||
}
|
||||
|
||||
void LoggingService::log(LogLevel level, const String& component, const String& message) {
|
||||
if (level < currentLogLevel) {
|
||||
return; // Skip logging if below current level
|
||||
}
|
||||
|
||||
LogData* logData = new LogData(level, component, message);
|
||||
|
||||
// Fire events for different log destinations
|
||||
if (serialLoggingEnabled) {
|
||||
ctx.fire("log/serial", logData);
|
||||
}
|
||||
}
|
||||
|
||||
void LoggingService::debug(const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::DEBUG, component, message);
|
||||
ctx.fire("log/debug", logData);
|
||||
}
|
||||
|
||||
void LoggingService::info(const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::INFO, component, message);
|
||||
ctx.fire("log/info", logData);
|
||||
}
|
||||
|
||||
void LoggingService::warn(const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::WARN, component, message);
|
||||
ctx.fire("log/warn", logData);
|
||||
}
|
||||
|
||||
void LoggingService::error(const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::ERROR, component, message);
|
||||
ctx.fire("log/error", logData);
|
||||
}
|
||||
|
||||
void LoggingService::setLogLevel(LogLevel level) {
|
||||
currentLogLevel = level;
|
||||
info("LoggingService", "Log level set to " + logLevelToString(level));
|
||||
}
|
||||
|
||||
void LoggingService::enableSerialLogging(bool enable) {
|
||||
serialLoggingEnabled = enable;
|
||||
info("LoggingService", "Serial logging " + String(enable ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
|
||||
void LoggingService::handleSerialLog(void* data) {
|
||||
LogData* logData = static_cast<LogData*>(data);
|
||||
String formattedMessage = formatLogMessage(*logData);
|
||||
Serial.println(formattedMessage);
|
||||
}
|
||||
|
||||
|
||||
String LoggingService::formatLogMessage(const LogData& logData) {
|
||||
String timestamp = String(logData.timestamp);
|
||||
String level = logLevelToString(logData.level);
|
||||
return "[" + timestamp + "] [" + level + "] [" + logData.component + "] " + logData.message;
|
||||
}
|
||||
|
||||
String LoggingService::logLevelToString(LogLevel level) {
|
||||
switch (level) {
|
||||
case LogLevel::DEBUG: return "DEBUG";
|
||||
case LogLevel::INFO: return "INFO";
|
||||
case LogLevel::WARN: return "WARN";
|
||||
case LogLevel::ERROR: return "ERROR";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Global logging functions that use the event system directly
|
||||
void logDebug(NodeContext& ctx, const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::DEBUG, component, message);
|
||||
ctx.fire("log/debug", logData);
|
||||
}
|
||||
|
||||
void logInfo(NodeContext& ctx, const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::INFO, component, message);
|
||||
ctx.fire("log/info", logData);
|
||||
}
|
||||
|
||||
void logWarn(NodeContext& ctx, const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::WARN, component, message);
|
||||
ctx.fire("log/warn", logData);
|
||||
}
|
||||
|
||||
void logError(NodeContext& ctx, const String& component, const String& message) {
|
||||
LogData* logData = new LogData(LogLevel::ERROR, component, message);
|
||||
ctx.fire("log/error", logData);
|
||||
}
|
||||
|
||||
// API endpoint handlers
|
||||
void LoggingService::handleGetLogLevel(AsyncWebServerRequest* request) {
|
||||
LOG_DEBUG(ctx, "LoggingService", "handleGetLogLevel called");
|
||||
String response = "{\"level\":\"" + logLevelToString(currentLogLevel) + "\"}";
|
||||
LOG_DEBUG(ctx, "LoggingService", "Sending response: " + response);
|
||||
request->send(200, "application/json", response);
|
||||
}
|
||||
|
||||
void LoggingService::handleSetLogLevel(AsyncWebServerRequest* request) {
|
||||
LOG_DEBUG(ctx, "LoggingService", "handleSetLogLevel called");
|
||||
LOG_DEBUG(ctx, "LoggingService", "Request method: " + String(request->method()));
|
||||
LOG_DEBUG(ctx, "LoggingService", "Request URL: " + request->url());
|
||||
LOG_DEBUG(ctx, "LoggingService", "Content type: " + request->contentType());
|
||||
|
||||
if (request->hasParam("level", true)) {
|
||||
String levelStr = request->getParam("level", true)->value();
|
||||
LOG_DEBUG(ctx, "LoggingService", "Level parameter found: '" + levelStr + "'");
|
||||
|
||||
if (levelStr == "DEBUG") {
|
||||
LOG_DEBUG(ctx, "LoggingService", "Setting log level to DEBUG");
|
||||
setLogLevel(LogLevel::DEBUG);
|
||||
} else if (levelStr == "INFO") {
|
||||
LOG_DEBUG(ctx, "LoggingService", "Setting log level to INFO");
|
||||
setLogLevel(LogLevel::INFO);
|
||||
} else if (levelStr == "WARN") {
|
||||
LOG_DEBUG(ctx, "LoggingService", "Setting log level to WARN");
|
||||
setLogLevel(LogLevel::WARN);
|
||||
} else if (levelStr == "ERROR") {
|
||||
LOG_DEBUG(ctx, "LoggingService", "Setting log level to ERROR");
|
||||
setLogLevel(LogLevel::ERROR);
|
||||
} else {
|
||||
LOG_ERROR(ctx, "LoggingService", "Invalid log level received: '" + levelStr + "'");
|
||||
request->send(400, "application/json", "{\"error\":\"Invalid log level. Must be one of: DEBUG, INFO, WARN, ERROR\"}");
|
||||
return;
|
||||
}
|
||||
LOG_INFO(ctx, "LoggingService", "Log level successfully set to " + logLevelToString(currentLogLevel));
|
||||
request->send(200, "application/json", "{\"success\":true, \"level\":\"" + logLevelToString(currentLogLevel) + "\"}");
|
||||
} else {
|
||||
LOG_ERROR(ctx, "LoggingService", "Missing required parameter: level");
|
||||
request->send(400, "application/json", "{\"error\":\"Missing required parameter: level\"}");
|
||||
}
|
||||
}
|
||||
|
||||
void LoggingService::handleGetConfig(AsyncWebServerRequest* request) {
|
||||
LOG_DEBUG(ctx, "LoggingService", "handleGetConfig called");
|
||||
JsonDocument doc;
|
||||
doc["level"] = logLevelToString(currentLogLevel);
|
||||
doc["serialEnabled"] = serialLoggingEnabled;
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
LOG_DEBUG(ctx, "LoggingService", "Sending config response: " + response);
|
||||
request->send(200, "application/json", response);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "spore/services/NodeService.h"
|
||||
#include "spore/core/ApiServer.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
|
||||
NodeService::NodeService(NodeContext& ctx, ApiServer& apiServer) : ctx(ctx), apiServer(apiServer) {}
|
||||
|
||||
@@ -65,8 +66,8 @@ void NodeService::handleUpdateRequest(AsyncWebServerRequest* request) {
|
||||
success ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
request->onDisconnect([]() {
|
||||
Serial.println("[API] Restart device");
|
||||
request->onDisconnect([this]() {
|
||||
LOG_INFO(ctx, "API", "Restart device");
|
||||
delay(10);
|
||||
ESP.restart();
|
||||
});
|
||||
@@ -75,11 +76,10 @@ void NodeService::handleUpdateRequest(AsyncWebServerRequest* request) {
|
||||
void NodeService::handleUpdateUpload(AsyncWebServerRequest* request, const String& filename,
|
||||
size_t index, uint8_t* data, size_t len, bool final) {
|
||||
if (!index) {
|
||||
Serial.print("[OTA] Update Start ");
|
||||
Serial.println(filename);
|
||||
LOG_INFO(ctx, "OTA", "Update Start " + String(filename));
|
||||
Update.runAsync(true);
|
||||
if(!Update.begin(request->contentLength(), U_FLASH)) {
|
||||
Serial.println("[OTA] Update failed: not enough space");
|
||||
LOG_ERROR(ctx, "OTA", "Update failed: not enough space");
|
||||
Update.printError(Serial);
|
||||
AsyncWebServerResponse* response = request->beginResponse(500, "application/json",
|
||||
"{\"status\": \"FAIL\"}");
|
||||
@@ -96,14 +96,12 @@ void NodeService::handleUpdateUpload(AsyncWebServerRequest* request, const Strin
|
||||
if (final) {
|
||||
if (Update.end(true)) {
|
||||
if(Update.isFinished()) {
|
||||
Serial.print("[OTA] Update Success with ");
|
||||
Serial.print(index + len);
|
||||
Serial.println("B");
|
||||
LOG_INFO(ctx, "OTA", "Update Success with " + String(index + len) + "B");
|
||||
} else {
|
||||
Serial.println("[OTA] Update not finished");
|
||||
LOG_WARN(ctx, "OTA", "Update not finished");
|
||||
}
|
||||
} else {
|
||||
Serial.print("[OTA] Update failed: ");
|
||||
LOG_ERROR(ctx, "OTA", "Update failed: " + String(Update.getError()));
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
@@ -114,8 +112,8 @@ void NodeService::handleRestartRequest(AsyncWebServerRequest* request) {
|
||||
"{\"status\": \"restarting\"}");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
request->onDisconnect([]() {
|
||||
Serial.println("[API] Restart device");
|
||||
request->onDisconnect([this]() {
|
||||
LOG_INFO(ctx, "API", "Restart device");
|
||||
delay(10);
|
||||
ESP.restart();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "spore/services/StaticFileService.h"
|
||||
#include "spore/services/LoggingService.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
const String StaticFileService::name = "StaticFileService";
|
||||
@@ -10,10 +11,10 @@ StaticFileService::StaticFileService(NodeContext& ctx, ApiServer& apiServer)
|
||||
void StaticFileService::registerEndpoints(ApiServer& api) {
|
||||
// Initialize LittleFS
|
||||
if (!LittleFS.begin()) {
|
||||
Serial.println("[StaticFileService] LittleFS Mount Failed");
|
||||
LOG_ERROR(ctx, "StaticFileService", "LittleFS Mount Failed");
|
||||
return;
|
||||
}
|
||||
Serial.println("[StaticFileService] LittleFS mounted successfully");
|
||||
LOG_INFO(ctx, "StaticFileService", "LittleFS mounted successfully");
|
||||
|
||||
// Root endpoint - serve index.html
|
||||
api.addEndpoint("/", HTTP_GET,
|
||||
|
||||
Reference in New Issue
Block a user