mirror of
https://github.com/lumapu/ahoy.git
synced 2025-06-10 22:51:36 +02:00
chunked response for prometheus metrics
This commit is contained in:
parent
22ae6d2e22
commit
9610e71a2c
2 changed files with 137 additions and 67 deletions
47
doc/prometheus_ep_description.md
Normal file
47
doc/prometheus_ep_description.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Prometheus Endpoint
|
||||||
|
Metrics available for AhoyDTU device, inverters and channels.
|
||||||
|
|
||||||
|
Prometheus metrics provided at `/metrics`.
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
| Label name | Description |
|
||||||
|
|:-----------|:--------------------------------------|
|
||||||
|
| version | current installed version of AhoyDTU |
|
||||||
|
| image | currently not used |
|
||||||
|
| devicename | Device name from setup |
|
||||||
|
| name | Inverter name from setup |
|
||||||
|
| serial | Serial number of inverter |
|
||||||
|
| enabled | Communication enable for inverter |
|
||||||
|
| inverter | Inverter name from setup |
|
||||||
|
| channel | Channel name from setup |
|
||||||
|
|
||||||
|
|
||||||
|
## Exported Metrics
|
||||||
|
| Metric name | Type | Description | Labels |
|
||||||
|
|----------------------------------------|---------|--------------------------------------------------------|--------------|
|
||||||
|
| `ahoy_solar_info` | Gauge | Information about the AhoyDTU device | version, image, devicename |
|
||||||
|
| `ahoy_solar_inverter_info` | Gauge | Information about the configured inverter(s) | name, serial, enabled |
|
||||||
|
| `ahoy_solar_U_AC_volt` | Gauge | AC voltage of inverter [V] | inverter |
|
||||||
|
| `ahoy_solar_I_AC_ampere` | Gauge | AC current of inverter [A] | inverter |
|
||||||
|
| `ahoy_solar_P_AC_watt` | Gauge | AC power of inverter [W] | inverter |
|
||||||
|
| `ahoy_solar_Q_AC_var` | Gauge | AC reactive power[var] | inverter |
|
||||||
|
| `ahoy_solar_F_AC_hertz` | Gauge | AC frequency [Hz] | inverter |
|
||||||
|
| `ahoy_solar_PF_AC` | Gauge | AC Power factor | inverter |
|
||||||
|
| `ahoy_solar_Temp_celsius` | Gauge | Temperature of inverter | inverter |
|
||||||
|
| `ahoy_solar_ALARM_MES_ID` | Gauge | Last alarm message id of inverter | inverter |
|
||||||
|
| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter |
|
||||||
|
| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter |
|
||||||
|
| `ahoy_solar_P_DC_watt` | Gauge | DC power of inverter [W] | inverter |
|
||||||
|
| `ahoy_solar_Efficiency_ratio` | Gauge | ration AC Power over DC Power [%] | inverter |
|
||||||
|
| `ahoy_solar_U_DC_volt` | Gauge | DC voltage of channel [V] | inverter, channel |
|
||||||
|
| `ahoy_solar_I_DC_ampere` | Gauge | DC current of channel [A] | inverter, channel |
|
||||||
|
| `ahoy_solar_P_DC_watt` | Gauge | DC power of channel [P] | inverter, channel |
|
||||||
|
| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter, channel |
|
||||||
|
| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter, channel |
|
||||||
|
| `ahoy_solar_Irradiation_ratio` | Gauge | ratio DC Power over set maximum power per channel [%] | inverter, channel |
|
||||||
|
| `ahoy_solar_radio_rx_success` | Gauge | NRF24 statistic | |
|
||||||
|
| `ahoy_solar_radio_rx_fail` | Gauge | NRF24 statistic | |
|
||||||
|
| `ahoy_solar_radio_rx_fail_answer` | Gauge | NRF24 statistic | |
|
||||||
|
| `ahoy_solar_radio_frame_cnt` | Gauge | NRF24 statistic | |
|
||||||
|
| `ahoy_solar_radio_tx_cnt` | Gauge | NRF24 statistic | |
|
||||||
|
|
157
src/web/web.h
157
src/web/web.h
|
@ -733,70 +733,93 @@ class Web {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_PROMETHEUS_EP
|
#ifdef ENABLE_PROMETHEUS_EP
|
||||||
|
enum {
|
||||||
|
metricsStateStart, metricsStateInverter, metricStateChannel,metricsStateEnd
|
||||||
|
} metricsStep;
|
||||||
|
int metricsInverterId,metricsChannelId;
|
||||||
|
|
||||||
void showMetrics(AsyncWebServerRequest *request) {
|
void showMetrics(AsyncWebServerRequest *request) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("web::showMetrics"));
|
DPRINTLN(DBG_VERBOSE, F("web::showMetrics"));
|
||||||
String metrics;
|
|
||||||
char infoline[90];
|
|
||||||
|
|
||||||
// System info
|
metricsStep = metricsStateStart;
|
||||||
snprintf(infoline, sizeof(infoline), "ahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1", mApp->getVersion(), mConfig->sys.deviceName);
|
AsyncWebServerResponse *response = request->beginChunkedResponse(F("text/plain"),
|
||||||
metrics += "# TYPE ahoy_solar_info gauge\n" + String(infoline) + "\n";
|
[this](uint8_t *buffer, size_t maxLen, size_t filledLength) -> size_t
|
||||||
Inverter<> *iv;
|
{
|
||||||
record_t<> *rec;
|
Inverter<> *iv;
|
||||||
char type[60], topic[80], val[25];
|
record_t<> *rec;
|
||||||
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
statistics_t *stat;
|
||||||
iv = mSys->getInverterByPos(id);
|
String metrics;
|
||||||
if(NULL == iv)
|
char type[60], topic[100], val[25];
|
||||||
continue;
|
size_t len = 0;
|
||||||
// Inverter info
|
|
||||||
snprintf(infoline, sizeof(infoline), "ahoy_solar_inverter_info{name=\"%s\",serial=\"%12llx\",enabled=\"%d\"} 1",
|
|
||||||
iv->config->name, iv->config->serial.u64,iv->config->enabled);
|
|
||||||
metrics += "# TYPE ahoy_solar_inverter_info gauge\n" + String(infoline) + "\n";
|
|
||||||
|
|
||||||
// AC
|
switch (metricsStep) {
|
||||||
rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
case metricsStateStart: // System Info & NRF Statistics : fit to one packet
|
||||||
for(uint8_t i = 0; i < rec->length; i++) {
|
snprintf(topic,sizeof(topic),"# TYPE ahoy_solar_info gauge\nahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1\n",
|
||||||
uint8_t channel = rec->assign[i].ch;
|
mApp->getVersion(), mConfig->sys.deviceName);
|
||||||
if(channel == 0) {
|
metrics = topic;
|
||||||
String promUnit, promType;
|
// NRF Statistics
|
||||||
std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(i, rec));
|
stat = mApp->getStatistics();
|
||||||
snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s_%s %s", iv->getFieldName(i, rec), promUnit.c_str(), promType.c_str());
|
metrics += radioStatistic(F("rx_success"), stat->rxSuccess);
|
||||||
snprintf(topic, sizeof(topic), "ahoy_solar_%s_%s{inverter=\"%s\"}", iv->getFieldName(i, rec), promUnit.c_str(), iv->config->name);
|
metrics += radioStatistic(F("rx_fail"), stat->rxFail);
|
||||||
snprintf(val, sizeof(val), "%.3f", iv->getValue(i, rec));
|
metrics += radioStatistic(F("rx_fail_answer"), stat->rxFailNoAnser);
|
||||||
metrics += String(type) + "\n" + String(topic) + " " + String(val) + "\n";
|
metrics += radioStatistic(F("frame_cnt"), stat->frmCnt);
|
||||||
}
|
metrics += radioStatistic(F("tx_cnt"), mSys->Radio.mSendCnt);
|
||||||
}
|
|
||||||
// channels DC
|
len = snprintf((char *)buffer,maxLen,"%s",metrics.c_str());
|
||||||
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
// Start Inverter loop
|
||||||
uint8_t pos;
|
metricsInverterId = 0;
|
||||||
for (uint8_t k = 0; k < 6; k++) {
|
metricsStep = metricsStateInverter;
|
||||||
switch(k) {
|
break;
|
||||||
default: pos = (iv->getPosByChFld(j, FLD_UDC, rec)); break;
|
|
||||||
case 1: pos = (iv->getPosByChFld(j, FLD_IDC, rec)); break;
|
case metricsStateInverter: // Inverter loop
|
||||||
case 2: pos = (iv->getPosByChFld(j, FLD_PDC, rec)); break;
|
if (metricsInverterId < mSys->getNumInverters()) {
|
||||||
case 3: pos = (iv->getPosByChFld(j, FLD_YD, rec)); break;
|
iv = mSys->getInverterByPos(metricsInverterId);
|
||||||
case 4: pos = (iv->getPosByChFld(j, FLD_YT, rec)); break;
|
if(NULL != iv) {
|
||||||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR, rec)); break;
|
// Inverter info
|
||||||
|
len = snprintf((char *)buffer, maxLen, "ahoy_solar_inverter_info{name=\"%s\",serial=\"%12llx\",enabled=\"%d\"} 1\n",
|
||||||
|
iv->config->name, iv->config->serial.u64,iv->config->enabled);
|
||||||
|
// Start Channel loop for this inverter
|
||||||
|
metricsChannelId = 0;
|
||||||
|
metricsStep = metricStateChannel;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metricsStep = metricsStateEnd;
|
||||||
}
|
}
|
||||||
String promUnit, promType;
|
break;
|
||||||
std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(pos, rec));
|
|
||||||
snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s_%s %s", iv->getFieldName(pos, rec), promUnit.c_str(), promType.c_str());
|
case metricStateChannel: // Channel loop
|
||||||
snprintf(topic, sizeof(topic), "ahoy_solar_%s_%s{inverter=\"%s\",channel=\"%s\"}", iv->getFieldName(pos, rec), promUnit.c_str(), iv->config->name, iv->config->chName[j-1]);
|
iv = mSys->getInverterByPos(metricsInverterId);
|
||||||
snprintf(val, sizeof(val), "%.3f", iv->getValue(pos, rec));
|
rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
metrics += String(type) + "\n" + String(topic) + " " + String(val) + "\n";
|
if (metricsChannelId < rec->length) {
|
||||||
}
|
uint8_t channel = rec->assign[metricsChannelId].ch;
|
||||||
|
String promUnit, promType;
|
||||||
|
std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(metricsChannelId, rec));
|
||||||
|
snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s%s %s", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), promType.c_str());
|
||||||
|
if (0 == channel) {
|
||||||
|
snprintf(topic, sizeof(topic), "ahoy_solar_%s%s{inverter=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name);
|
||||||
|
} else {
|
||||||
|
snprintf(topic, sizeof(topic), "ahoy_solar_%s%s{inverter=\"%s\",channel=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name,iv->config->chName[channel-1]);
|
||||||
|
}
|
||||||
|
snprintf(val, sizeof(val), "%.3f", iv->getValue(metricsChannelId, rec));
|
||||||
|
len = snprintf((char*)buffer,maxLen,"%s\n%s %s\n",type,topic,val);
|
||||||
|
|
||||||
|
metricsChannelId++;
|
||||||
|
} else {
|
||||||
|
len = snprintf((char*)buffer,maxLen,"#\n"); // At least one char to send otherwise the transmission ends.
|
||||||
|
|
||||||
|
// All channels processed --> try next inverter
|
||||||
|
metricsInverterId++;
|
||||||
|
metricsStep = metricsStateInverter;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case metricsStateEnd:
|
||||||
|
default: // end of transmission
|
||||||
|
len = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
return len;
|
||||||
|
});
|
||||||
// NRF Statistics
|
|
||||||
statistics_t *stat = mApp->getStatistics();
|
|
||||||
metrics += radioStatistic(F("rx_success"), stat->rxSuccess);
|
|
||||||
metrics += radioStatistic(F("rx_fail"), stat->rxFail);
|
|
||||||
metrics += radioStatistic(F("rx_fail_answer"), stat->rxFailNoAnser);
|
|
||||||
metrics += radioStatistic(F("frame_cnt"), stat->frmCnt);
|
|
||||||
metrics += radioStatistic(F("tx_cnt"), mSys->Radio.mSendCnt);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse(200, F("text/plain"), metrics);
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,18 +832,18 @@ class Web {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<String, String> convertToPromUnits(String shortUnit) {
|
std::pair<String, String> convertToPromUnits(String shortUnit) {
|
||||||
if(shortUnit == "A") return {"ampere", "gauge"};
|
if(shortUnit == "A") return {"_ampere", "gauge"};
|
||||||
if(shortUnit == "V") return {"volt", "gauge"};
|
if(shortUnit == "V") return {"_volt", "gauge"};
|
||||||
if(shortUnit == "%") return {"ratio", "gauge"};
|
if(shortUnit == "%") return {"_ratio", "gauge"};
|
||||||
if(shortUnit == "W") return {"watt", "gauge"};
|
if(shortUnit == "W") return {"_watt", "gauge"};
|
||||||
if(shortUnit == "Wh") return {"watt_daily", "counter"};
|
if(shortUnit == "Wh") return {"_wattHours", "counter"};
|
||||||
if(shortUnit == "kWh") return {"watt_total", "counter"};
|
if(shortUnit == "kWh") return {"_kilowattHours", "counter"};
|
||||||
if(shortUnit == "°C") return {"celsius", "gauge"};
|
if(shortUnit == "°C") return {"_celsius", "gauge"};
|
||||||
|
if(shortUnit == "var") return {"_var", "gauge"};
|
||||||
|
if(shortUnit == "Hz") return {"_hertz", "gauge"};
|
||||||
return {"", "gauge"};
|
return {"", "gauge"};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AsyncWebServer mWeb;
|
AsyncWebServer mWeb;
|
||||||
AsyncEventSource mEvts;
|
AsyncEventSource mEvts;
|
||||||
bool mProtected;
|
bool mProtected;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue