From 759f8b72088b1efa00b1d35c0c05c1091cfed7c6 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Thu, 23 Feb 2023 21:10:00 +0100 Subject: [PATCH] Log errors if too less memory for webapi is available --- include/WebApi.h | 2 + src/WebApi.cpp | 7 +++ src/WebApi_prometheus.cpp | 113 ++++++++++++++++++++------------------ src/WebApi_ws_live.cpp | 44 +++++++++------ 4 files changed, 98 insertions(+), 68 deletions(-) diff --git a/include/WebApi.h b/include/WebApi.h index 94275aa..ed1f386 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -31,6 +31,8 @@ public: static bool checkCredentials(AsyncWebServerRequest* request); static bool checkCredentialsReadonly(AsyncWebServerRequest* request); + static void sendTooManyRequests(AsyncWebServerRequest* request); + private: AsyncWebServer _server; AsyncEventSource _events; diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 349fb58..cd53409 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -90,4 +90,11 @@ bool WebApiClass::checkCredentialsReadonly(AsyncWebServerRequest* request) } } +void WebApiClass::sendTooManyRequests(AsyncWebServerRequest* request) +{ + auto response = request->beginResponse(429, "text/plain", "Too Many Requests"); + response->addHeader("Retry-After", "60"); + request->send(response); +} + WebApiClass WebApi; \ No newline at end of file diff --git a/src/WebApi_prometheus.cpp b/src/WebApi_prometheus.cpp index b200abf..a805ee8 100644 --- a/src/WebApi_prometheus.cpp +++ b/src/WebApi_prometheus.cpp @@ -5,7 +5,9 @@ */ #include "WebApi_prometheus.h" #include "Configuration.h" +#include "MessageOutput.h" #include "NetworkSettings.h" +#include "WebApi.h" #include void WebApiPrometheusClass::init(AsyncWebServer* server) @@ -23,71 +25,78 @@ void WebApiPrometheusClass::loop() void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request) { - auto stream = request->beginResponseStream("text/plain; charset=utf-8", 40960); + try { + auto stream = request->beginResponseStream("text/plain; charset=utf-8", 40960); - stream->print(F("# HELP opendtu_build Build info\n")); - stream->print(F("# TYPE opendtu_build gauge\n")); - stream->printf("opendtu_build{name=\"%s\",id=\"%s\",version=\"%d.%d.%d\"} 1\n", - NetworkSettings.getHostname().c_str(), AUTO_GIT_HASH, CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff); + stream->print(F("# HELP opendtu_build Build info\n")); + stream->print(F("# TYPE opendtu_build gauge\n")); + stream->printf("opendtu_build{name=\"%s\",id=\"%s\",version=\"%d.%d.%d\"} 1\n", + NetworkSettings.getHostname().c_str(), AUTO_GIT_HASH, CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff); - stream->print(F("# HELP opendtu_platform Platform info\n")); - stream->print(F("# TYPE opendtu_platform gauge\n")); - stream->printf("opendtu_platform{arch=\"%s\",mac=\"%s\"} 1\n", ESP.getChipModel(), NetworkSettings.macAddress().c_str()); + stream->print(F("# HELP opendtu_platform Platform info\n")); + stream->print(F("# TYPE opendtu_platform gauge\n")); + stream->printf("opendtu_platform{arch=\"%s\",mac=\"%s\"} 1\n", ESP.getChipModel(), NetworkSettings.macAddress().c_str()); - stream->print(F("# HELP opendtu_uptime Uptime in seconds\n")); - stream->print(F("# TYPE opendtu_uptime counter\n")); - stream->printf("opendtu_uptime %lld\n", esp_timer_get_time() / 1000000); + stream->print(F("# HELP opendtu_uptime Uptime in seconds\n")); + stream->print(F("# TYPE opendtu_uptime counter\n")); + stream->printf("opendtu_uptime %lld\n", esp_timer_get_time() / 1000000); - stream->print(F("# HELP opendtu_heap_size System memory size\n")); - stream->print(F("# TYPE opendtu_heap_size gauge\n")); - stream->printf("opendtu_heap_size %zu\n", ESP.getHeapSize()); + stream->print(F("# HELP opendtu_heap_size System memory size\n")); + stream->print(F("# TYPE opendtu_heap_size gauge\n")); + stream->printf("opendtu_heap_size %zu\n", ESP.getHeapSize()); - stream->print(F("# HELP opendtu_free_heap_size System free memory\n")); - stream->print(F("# TYPE opendtu_free_heap_size gauge\n")); - stream->printf("opendtu_free_heap_size %zu\n", ESP.getFreeHeap()); + stream->print(F("# HELP opendtu_free_heap_size System free memory\n")); + stream->print(F("# TYPE opendtu_free_heap_size gauge\n")); + stream->printf("opendtu_free_heap_size %zu\n", ESP.getFreeHeap()); - stream->print(F("# HELP wifi_rssi WiFi RSSI\n")); - stream->print(F("# TYPE wifi_rssi gauge\n")); - stream->printf("wifi_rssi %d\n", WiFi.RSSI()); + stream->print(F("# HELP wifi_rssi WiFi RSSI\n")); + stream->print(F("# TYPE wifi_rssi gauge\n")); + stream->printf("wifi_rssi %d\n", WiFi.RSSI()); - for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { - auto inv = Hoymiles.getInverterByPos(i); + for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { + auto inv = Hoymiles.getInverterByPos(i); - String serial = inv->serialString(); - const char* name = inv->name(); - if (i == 0) { - stream->print(F("# HELP opendtu_last_update last update from inverter in s\n")); - stream->print(F("# TYPE opendtu_last_update gauge\n")); - } - stream->printf("opendtu_last_update{serial=\"%s\",unit=\"%d\",name=\"%s\"} %d\n", - serial.c_str(), i, name, inv->Statistics()->getLastUpdate() / 1000); + String serial = inv->serialString(); + const char* name = inv->name(); + if (i == 0) { + stream->print(F("# HELP opendtu_last_update last update from inverter in s\n")); + stream->print(F("# TYPE opendtu_last_update gauge\n")); + } + stream->printf("opendtu_last_update{serial=\"%s\",unit=\"%d\",name=\"%s\"} %d\n", + serial.c_str(), i, name, inv->Statistics()->getLastUpdate() / 1000); - // Loop all channels - for (auto& t : inv->Statistics()->getChannelTypes()) { - for (auto& c : inv->Statistics()->getChannelsByType(t)) { - addField(stream, serial, i, inv, t, c, FLD_PAC); - addField(stream, serial, i, inv, t, c, FLD_UAC); - addField(stream, serial, i, inv, t, c, FLD_IAC); - if (t == TYPE_AC) { - addField(stream, serial, i, inv, t, c, FLD_PDC, "PowerDC"); - } else { - addField(stream, serial, i, inv, t, c, FLD_PDC); + // Loop all channels + for (auto& t : inv->Statistics()->getChannelTypes()) { + for (auto& c : inv->Statistics()->getChannelsByType(t)) { + addField(stream, serial, i, inv, t, c, FLD_PAC); + addField(stream, serial, i, inv, t, c, FLD_UAC); + addField(stream, serial, i, inv, t, c, FLD_IAC); + if (t == TYPE_AC) { + addField(stream, serial, i, inv, t, c, FLD_PDC, "PowerDC"); + } else { + addField(stream, serial, i, inv, t, c, FLD_PDC); + } + addField(stream, serial, i, inv, t, c, FLD_UDC); + addField(stream, serial, i, inv, t, c, FLD_IDC); + addField(stream, serial, i, inv, t, c, FLD_YD); + addField(stream, serial, i, inv, t, c, FLD_YT); + addField(stream, serial, i, inv, t, c, FLD_F); + addField(stream, serial, i, inv, t, c, FLD_T); + addField(stream, serial, i, inv, t, c, FLD_PF); + addField(stream, serial, i, inv, t, c, FLD_PRA); + addField(stream, serial, i, inv, t, c, FLD_EFF); + addField(stream, serial, i, inv, t, c, FLD_IRR); } - addField(stream, serial, i, inv, t, c, FLD_UDC); - addField(stream, serial, i, inv, t, c, FLD_IDC); - addField(stream, serial, i, inv, t, c, FLD_YD); - addField(stream, serial, i, inv, t, c, FLD_YT); - addField(stream, serial, i, inv, t, c, FLD_F); - addField(stream, serial, i, inv, t, c, FLD_T); - addField(stream, serial, i, inv, t, c, FLD_PF); - addField(stream, serial, i, inv, t, c, FLD_PRA); - addField(stream, serial, i, inv, t, c, FLD_EFF); - addField(stream, serial, i, inv, t, c, FLD_IRR); } } + stream->addHeader(F("Cache-Control"), F("no-cache")); + request->send(stream); + + } catch (std::bad_alloc& bad_alloc) { + MessageOutput.printf("Call to /api/prometheus/metrics temporarely out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + + WebApi.sendTooManyRequests(request); } - stream->addHeader(F("Cache-Control"), F("no-cache")); - request->send(stream); } void WebApiPrometheusClass::addField(AsyncResponseStream* stream, String& serial, uint8_t idx, std::shared_ptr inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, const char* channelName) diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index fedc9ff..1a69d46 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -60,21 +60,26 @@ void WebApiWsLiveClass::loop() // Update on every inverter change or at least after 10 seconds if (millis() - _lastWsPublish > (10 * 1000) || (maxTimeStamp != _newestInverterTimestamp)) { - DynamicJsonDocument root(40960); - JsonVariant var = root; - generateJsonResponse(var); + try { + DynamicJsonDocument root(40960); + JsonVariant var = root; + generateJsonResponse(var); - String buffer; - if (buffer) { - serializeJson(root, buffer); + String buffer; + if (buffer) { + serializeJson(root, buffer); - if (Configuration.get().Security_AllowReadonly) { - _ws.setAuthentication("", ""); - } else { - _ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security_Password); + if (Configuration.get().Security_AllowReadonly) { + _ws.setAuthentication("", ""); + } else { + _ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security_Password); + } + + _ws.textAll(buffer); } - _ws.textAll(buffer); + } catch (std::bad_alloc& bad_alloc) { + MessageOutput.printf("Call to /api/livedata/status temporarely out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); } _lastWsPublish = millis(); @@ -220,11 +225,18 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request) return; } - AsyncJsonResponse* response = new AsyncJsonResponse(false, 40960U); - JsonVariant root = response->getRoot(); + try { + AsyncJsonResponse* response = new AsyncJsonResponse(false, 40960U); + JsonVariant root = response->getRoot(); - generateJsonResponse(root); + generateJsonResponse(root); - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); + + } catch (std::bad_alloc& bad_alloc) { + MessageOutput.printf("Call to /api/livedata/status temporarely out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + + WebApi.sendTooManyRequests(request); + } } \ No newline at end of file