mirror of
https://github.com/unpoller/unpoller.git
synced 2026-09-29 19:11:17 +02:00
Merge pull request #1100 from unpoller/feat/protect-air-quality
feat(protect): export air quality readings when present
This commit is contained in:
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/prometheus/common v0.71.0
|
github.com/prometheus/common v0.71.0
|
||||||
github.com/spf13/pflag v1.0.10
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.12.1
|
github.com/stretchr/testify v1.12.1
|
||||||
github.com/unpoller/unifi/v6 v6.1.3
|
github.com/unpoller/unifi/v6 v6.2.0
|
||||||
go.opentelemetry.io/otel v1.46.0
|
go.opentelemetry.io/otel v1.46.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0
|
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.46.0
|
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.46.0
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||||
github.com/unpoller/unifi/v6 v6.1.3 h1:npoNlGCSc6dMe/P9ZL0fe7fLw4vb7nJSTVqKLmwDjLY=
|
github.com/unpoller/unifi/v6 v6.2.0 h1:5P3C1KkbVRhX11/aQIcgKex/aotroUEXtXLQLLfanv4=
|
||||||
github.com/unpoller/unifi/v6 v6.1.3/go.mod h1:fJ1lU/mpKrQ8Jv3b2yq399ATlrUuiOZAMbKWcw/1GaU=
|
github.com/unpoller/unifi/v6 v6.2.0/go.mod h1:fJ1lU/mpKrQ8Jv3b2yq399ATlrUuiOZAMbKWcw/1GaU=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
|
|||||||
+37
-10
@@ -83,18 +83,45 @@ func (u *DatadogUnifi) batchProtectSensor(r report, d *unifi.ProtectDevices, s *
|
|||||||
_ = r.reportGauge(metricName("battery_low"), boolToFloat64(s.BatteryStatus.IsLow.Val), tags)
|
_ = r.reportGauge(metricName("battery_low"), boolToFloat64(s.BatteryStatus.IsLow.Val), tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Stats != nil {
|
if temp, ok := s.TemperatureReading(); ok {
|
||||||
if s.Stats.Temperature != nil {
|
_ = r.reportGauge(metricName("temperature"), temp, tags)
|
||||||
_ = r.reportGauge(metricName("temperature"), s.Stats.Temperature.Value.Val, tags)
|
}
|
||||||
|
|
||||||
|
if humidity, ok := s.HumidityReading(); ok {
|
||||||
|
_ = r.reportGauge(metricName("humidity"), humidity, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if light, ok := s.LightReading(); ok {
|
||||||
|
_ = r.reportGauge(metricName("light"), light, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.AirQuality == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aq := s.AirQuality
|
||||||
|
channels := []struct {
|
||||||
|
name string
|
||||||
|
reading *unifi.ProtectSensorStatValue
|
||||||
|
}{
|
||||||
|
{"aqi", aq.AQI},
|
||||||
|
{"co2_ppm", aq.CO2},
|
||||||
|
{"tvoc", aq.TVOC},
|
||||||
|
{"voc", aq.VOC},
|
||||||
|
{"vape", aq.Vape},
|
||||||
|
{"pm1_0", aq.PM1p0},
|
||||||
|
{"pm2_5", aq.PM2p5},
|
||||||
|
{"pm4_0", aq.PM4p0},
|
||||||
|
{"pm10", aq.PM10p0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range channels {
|
||||||
|
value, ok := ch.reading.Reading()
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Stats.Humidity != nil {
|
_ = r.reportGauge(metricName(ch.name), value, tags)
|
||||||
_ = r.reportGauge(metricName("humidity"), s.Stats.Humidity.Value.Val, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Stats.Light != nil {
|
|
||||||
_ = r.reportGauge(metricName("light"), s.Stats.Light.Value.Val, tags)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,17 +48,42 @@ func (u *InfluxUnifi) batchProtectDevices(r report, d *unifi.ProtectDevices) {
|
|||||||
fields["battery_low"] = s.BatteryStatus.IsLow.Val
|
fields["battery_low"] = s.BatteryStatus.IsLow.Val
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Stats != nil {
|
if temp, ok := s.TemperatureReading(); ok {
|
||||||
if s.Stats.Temperature != nil {
|
fields["temperature"] = temp
|
||||||
fields["temperature"] = s.Stats.Temperature.Value.Val
|
}
|
||||||
|
|
||||||
|
if humidity, ok := s.HumidityReading(); ok {
|
||||||
|
fields["humidity"] = humidity
|
||||||
|
}
|
||||||
|
|
||||||
|
if light, ok := s.LightReading(); ok {
|
||||||
|
fields["light"] = light
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.AirQuality != nil {
|
||||||
|
aq := s.AirQuality
|
||||||
|
channels := []struct {
|
||||||
|
name string
|
||||||
|
reading *unifi.ProtectSensorStatValue
|
||||||
|
}{
|
||||||
|
{"aqi", aq.AQI},
|
||||||
|
{"co2_ppm", aq.CO2},
|
||||||
|
{"tvoc", aq.TVOC},
|
||||||
|
{"voc", aq.VOC},
|
||||||
|
{"vape", aq.Vape},
|
||||||
|
{"pm1_0", aq.PM1p0},
|
||||||
|
{"pm2_5", aq.PM2p5},
|
||||||
|
{"pm4_0", aq.PM4p0},
|
||||||
|
{"pm10", aq.PM10p0},
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Stats.Humidity != nil {
|
for _, ch := range channels {
|
||||||
fields["humidity"] = s.Stats.Humidity.Value.Val
|
value, ok := ch.reading.Reading()
|
||||||
}
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if s.Stats.Light != nil {
|
fields[ch.name] = value
|
||||||
fields["light"] = s.Stats.Light.Value.Val
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-13
@@ -21,6 +21,15 @@ type protectDevices struct {
|
|||||||
SensorTemperature *prometheus.Desc
|
SensorTemperature *prometheus.Desc
|
||||||
SensorHumidity *prometheus.Desc
|
SensorHumidity *prometheus.Desc
|
||||||
SensorLight *prometheus.Desc
|
SensorLight *prometheus.Desc
|
||||||
|
SensorAQI *prometheus.Desc
|
||||||
|
SensorCO2 *prometheus.Desc
|
||||||
|
SensorTVOC *prometheus.Desc
|
||||||
|
SensorVOC *prometheus.Desc
|
||||||
|
SensorVape *prometheus.Desc
|
||||||
|
SensorPM1 *prometheus.Desc
|
||||||
|
SensorPM2_5 *prometheus.Desc
|
||||||
|
SensorPM4 *prometheus.Desc
|
||||||
|
SensorPM10 *prometheus.Desc
|
||||||
SensorIsOpened *prometheus.Desc
|
SensorIsOpened *prometheus.Desc
|
||||||
SensorIsMotion *prometheus.Desc
|
SensorIsMotion *prometheus.Desc
|
||||||
// Cameras.
|
// Cameras.
|
||||||
@@ -56,6 +65,24 @@ func descProtectDevices(ns string) *protectDevices { // nolint: funlen
|
|||||||
"Protect sensor humidity reading", device, nil),
|
"Protect sensor humidity reading", device, nil),
|
||||||
SensorLight: prometheus.NewDesc(ns+"sensor_light_lux",
|
SensorLight: prometheus.NewDesc(ns+"sensor_light_lux",
|
||||||
"Protect sensor light reading", device, nil),
|
"Protect sensor light reading", device, nil),
|
||||||
|
SensorAQI: prometheus.NewDesc(ns+"sensor_aqi",
|
||||||
|
"Protect air quality index", device, nil),
|
||||||
|
SensorCO2: prometheus.NewDesc(ns+"sensor_co2_ppm",
|
||||||
|
"Protect air quality CO2 reading in ppm", device, nil),
|
||||||
|
SensorTVOC: prometheus.NewDesc(ns+"sensor_tvoc",
|
||||||
|
"Protect air quality total volatile organic compounds", device, nil),
|
||||||
|
SensorVOC: prometheus.NewDesc(ns+"sensor_voc",
|
||||||
|
"Protect air quality volatile organic compounds", device, nil),
|
||||||
|
SensorVape: prometheus.NewDesc(ns+"sensor_vape",
|
||||||
|
"Protect air quality vape detection reading", device, nil),
|
||||||
|
SensorPM1: prometheus.NewDesc(ns+"sensor_pm1_0",
|
||||||
|
"Protect air quality PM1.0 reading", device, nil),
|
||||||
|
SensorPM2_5: prometheus.NewDesc(ns+"sensor_pm2_5",
|
||||||
|
"Protect air quality PM2.5 reading", device, nil),
|
||||||
|
SensorPM4: prometheus.NewDesc(ns+"sensor_pm4_0",
|
||||||
|
"Protect air quality PM4.0 reading", device, nil),
|
||||||
|
SensorPM10: prometheus.NewDesc(ns+"sensor_pm10",
|
||||||
|
"Protect air quality PM10 reading", device, nil),
|
||||||
SensorIsOpened: prometheus.NewDesc(ns+"sensor_is_opened",
|
SensorIsOpened: prometheus.NewDesc(ns+"sensor_is_opened",
|
||||||
"Protect door/window sensor is opened (1) or closed (0)", device, nil),
|
"Protect door/window sensor is opened (1) or closed (0)", device, nil),
|
||||||
SensorIsMotion: prometheus.NewDesc(ns+"sensor_is_motion_detected",
|
SensorIsMotion: prometheus.NewDesc(ns+"sensor_is_motion_detected",
|
||||||
@@ -112,19 +139,7 @@ func (u *promUnifi) exportProtectDevices(r report, d *unifi.ProtectDevices) {
|
|||||||
r.send([]*metric{{u.ProtectDevices.SensorBatteryLow, gauge, isLow, labels}})
|
r.send([]*metric{{u.ProtectDevices.SensorBatteryLow, gauge, isLow, labels}})
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Stats != nil {
|
u.exportProtectSensorReadings(r, s, labels)
|
||||||
if s.Stats.Temperature != nil {
|
|
||||||
r.send([]*metric{{u.ProtectDevices.SensorTemperature, gauge, s.Stats.Temperature.Value.Val, labels}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Stats.Humidity != nil {
|
|
||||||
r.send([]*metric{{u.ProtectDevices.SensorHumidity, gauge, s.Stats.Humidity.Value.Val, labels}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Stats.Light != nil {
|
|
||||||
r.send([]*metric{{u.ProtectDevices.SensorLight, gauge, s.Stats.Light.Value.Val, labels}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isOpened := 0.0
|
isOpened := 0.0
|
||||||
if s.IsOpened.Val {
|
if s.IsOpened.Val {
|
||||||
@@ -222,6 +237,54 @@ func (u *promUnifi) exportProtectDevices(r report, d *unifi.ProtectDevices) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exportProtectSensorReadings emits environmental and air-quality gauges for one sensor.
|
||||||
|
//
|
||||||
|
// A channel whose status is "unknown" is omitted. Temperature and humidity prefer
|
||||||
|
// stats and fall back to airQuality, which is where a UP-AirQuality sensor reports
|
||||||
|
// them. The air-quality series are emitted only when that object is present.
|
||||||
|
func (u *promUnifi) exportProtectSensorReadings(r report, s *unifi.ProtectSensor, labels []string) {
|
||||||
|
if temp, ok := s.TemperatureReading(); ok {
|
||||||
|
r.send([]*metric{{u.ProtectDevices.SensorTemperature, gauge, temp, labels}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if humidity, ok := s.HumidityReading(); ok {
|
||||||
|
r.send([]*metric{{u.ProtectDevices.SensorHumidity, gauge, humidity, labels}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if light, ok := s.LightReading(); ok {
|
||||||
|
r.send([]*metric{{u.ProtectDevices.SensorLight, gauge, light, labels}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.AirQuality == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aq := s.AirQuality
|
||||||
|
channels := []struct {
|
||||||
|
desc *prometheus.Desc
|
||||||
|
reading *unifi.ProtectSensorStatValue
|
||||||
|
}{
|
||||||
|
{u.ProtectDevices.SensorAQI, aq.AQI},
|
||||||
|
{u.ProtectDevices.SensorCO2, aq.CO2},
|
||||||
|
{u.ProtectDevices.SensorTVOC, aq.TVOC},
|
||||||
|
{u.ProtectDevices.SensorVOC, aq.VOC},
|
||||||
|
{u.ProtectDevices.SensorVape, aq.Vape},
|
||||||
|
{u.ProtectDevices.SensorPM1, aq.PM1p0},
|
||||||
|
{u.ProtectDevices.SensorPM2_5, aq.PM2p5},
|
||||||
|
{u.ProtectDevices.SensorPM4, aq.PM4p0},
|
||||||
|
{u.ProtectDevices.SensorPM10, aq.PM10p0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range channels {
|
||||||
|
value, ok := ch.reading.Reading()
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.send([]*metric{{ch.desc, gauge, value, labels}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// exportProtectIdentity emits the presence and state gauges shared by every Protect device kind
|
// exportProtectIdentity emits the presence and state gauges shared by every Protect device kind
|
||||||
// and returns the label set used for that device's other metrics.
|
// and returns the label set used for that device's other metrics.
|
||||||
func (u *promUnifi) exportProtectIdentity(r report, sourceName string, id *unifi.ProtectDeviceIdentity) []string {
|
func (u *promUnifi) exportProtectIdentity(r report, sourceName string, id *unifi.ProtectDeviceIdentity) []string {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package promunifi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@@ -132,3 +133,104 @@ func TestExportProtectDevicesSensorTolerantOfNilBatteryAndStats(t *testing.T) {
|
|||||||
// identity(2) + is_opened/is_motion(2), no battery or stats series.
|
// identity(2) + is_opened/is_motion(2), no battery or stats series.
|
||||||
assert.Len(t, r.sent, 4)
|
assert.Len(t, r.sent, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExportProtectSensorAirQuality(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := &promUnifi{ProtectDevices: descProtectDevices("unifi_")}
|
||||||
|
|
||||||
|
unknownOnly := &fakeReport{}
|
||||||
|
u.exportProtectDevices(unknownOnly, &unifi.ProtectDevices{
|
||||||
|
SourceName: "https://protect.example",
|
||||||
|
Sensors: []*unifi.ProtectSensor{{
|
||||||
|
ProtectDeviceIdentity: unifi.ProtectDeviceIdentity{Name: "AQ", Type: "UP-AirQuality", ModelKey: "sensor", State: "CONNECTED"},
|
||||||
|
Stats: &unifi.ProtectSensorStats{
|
||||||
|
Temperature: &unifi.ProtectSensorStatValue{Status: "unknown"},
|
||||||
|
Humidity: &unifi.ProtectSensorStatValue{Status: "unknown"},
|
||||||
|
Light: &unifi.ProtectSensorStatValue{Status: "unknown"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
|
unknown := readingsByName(t, unknownOnly.sent)
|
||||||
|
assert.NotContains(t, unknown, "unifi_protect_sensor_temperature_celsius")
|
||||||
|
assert.NotContains(t, unknown, "unifi_protect_sensor_humidity_percent")
|
||||||
|
assert.NotContains(t, unknown, "unifi_protect_sensor_light_lux")
|
||||||
|
assert.NotContains(t, unknown, "unifi_protect_sensor_aqi")
|
||||||
|
|
||||||
|
withAir := &fakeReport{}
|
||||||
|
u.exportProtectDevices(withAir, &unifi.ProtectDevices{
|
||||||
|
SourceName: "https://protect.example",
|
||||||
|
Sensors: []*unifi.ProtectSensor{{
|
||||||
|
ProtectDeviceIdentity: unifi.ProtectDeviceIdentity{Name: "AQ", Type: "UP-AirQuality", ModelKey: "sensor", State: "CONNECTED"},
|
||||||
|
Stats: &unifi.ProtectSensorStats{
|
||||||
|
Temperature: &unifi.ProtectSensorStatValue{Status: "unknown"},
|
||||||
|
Humidity: &unifi.ProtectSensorStatValue{Status: "unknown"},
|
||||||
|
},
|
||||||
|
AirQuality: &unifi.ProtectAirQuality{
|
||||||
|
AQI: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 7}, Status: "neutral"},
|
||||||
|
Vape: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 0}, Status: "safe"},
|
||||||
|
CO2: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 452}, Status: "neutral"},
|
||||||
|
PM2p5: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 1.79}, Status: "neutral"},
|
||||||
|
Humidity: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 51}, Status: "neutral"},
|
||||||
|
Temperature: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 25.4}, Status: "neutral"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
|
got := readingsByName(t, withAir.sent)
|
||||||
|
assert.InDelta(t, 25.4, got["unifi_protect_sensor_temperature_celsius"], 0.001)
|
||||||
|
assert.InDelta(t, 51, got["unifi_protect_sensor_humidity_percent"], 0.001)
|
||||||
|
assert.InDelta(t, 7, got["unifi_protect_sensor_aqi"], 0.001)
|
||||||
|
assert.InDelta(t, 0, got["unifi_protect_sensor_vape"], 0.001)
|
||||||
|
assert.InDelta(t, 452, got["unifi_protect_sensor_co2_ppm"], 0.001)
|
||||||
|
assert.InDelta(t, 1.79, got["unifi_protect_sensor_pm2_5"], 0.001)
|
||||||
|
assert.NotContains(t, got, "unifi_protect_sensor_tvoc")
|
||||||
|
|
||||||
|
statsWin := &fakeReport{}
|
||||||
|
u.exportProtectDevices(statsWin, &unifi.ProtectDevices{
|
||||||
|
SourceName: "https://protect.example",
|
||||||
|
Sensors: []*unifi.ProtectSensor{{
|
||||||
|
ProtectDeviceIdentity: unifi.ProtectDeviceIdentity{Name: "Both", Type: "sensor", ModelKey: "sensor", State: "CONNECTED"},
|
||||||
|
Stats: &unifi.ProtectSensorStats{
|
||||||
|
Temperature: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 21.5}, Status: "safe"},
|
||||||
|
},
|
||||||
|
AirQuality: &unifi.ProtectAirQuality{
|
||||||
|
Temperature: &unifi.ProtectSensorStatValue{Value: unifi.FlexFloat{Val: 99}, Status: "neutral"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.InDelta(t, 21.5, readingsByName(t, statsWin.sent)["unifi_protect_sensor_temperature_celsius"], 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readingsByName(t *testing.T, sent []*metric) map[string]float64 {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
out := make(map[string]float64, len(sent))
|
||||||
|
for _, m := range sent {
|
||||||
|
name := prometheusDescName(t, m.Desc)
|
||||||
|
value, ok := m.Value.(float64)
|
||||||
|
require.True(t, ok, "metric %s value is %T", name, m.Value)
|
||||||
|
|
||||||
|
out[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func prometheusDescName(t *testing.T, desc *prometheus.Desc) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
const key = `fqName: "`
|
||||||
|
|
||||||
|
text := desc.String()
|
||||||
|
start := strings.Index(text, key)
|
||||||
|
require.GreaterOrEqual(t, start, 0, "descriptor has no fqName: %s", text)
|
||||||
|
|
||||||
|
rest := text[start+len(key):]
|
||||||
|
end := strings.Index(rest, `"`)
|
||||||
|
require.GreaterOrEqual(t, end, 0, "descriptor fqName is unclosed: %s", text)
|
||||||
|
|
||||||
|
return rest[:end]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user