mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Encode prometheus metric names per the prom spec (#26639)
Referencing issue #26418. Prometheus metric names can only contain chars a-zA-Z0-9, : and _ (https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). HA currently generates invalid prometheus names, e.g. if the unit for a sensor is a non-ASCII character containing ° or μ. To resolve, we need to sanitize the name before creating, replacing non-valid characters with a valid representation. In this case, I've used "u{unicode-hex-code}". Also updated the test case to make sure that the ° case is handled.
This commit is contained in:
parent
5e15675593
commit
770eeaf82f
@ -1,5 +1,6 @@
|
|||||||
"""Support for Prometheus metrics export."""
|
"""Support for Prometheus metrics export."""
|
||||||
import logging
|
import logging
|
||||||
|
import string
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -159,10 +160,23 @@ class PrometheusMetrics:
|
|||||||
try:
|
try:
|
||||||
return self._metrics[metric]
|
return self._metrics[metric]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
full_metric_name = f"{self.metrics_prefix}{metric}"
|
full_metric_name = self._sanitize_metric_name(
|
||||||
|
f"{self.metrics_prefix}{metric}"
|
||||||
|
)
|
||||||
self._metrics[metric] = factory(full_metric_name, documentation, labels)
|
self._metrics[metric] = factory(full_metric_name, documentation, labels)
|
||||||
return self._metrics[metric]
|
return self._metrics[metric]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sanitize_metric_name(metric: str) -> str:
|
||||||
|
return "".join(
|
||||||
|
[
|
||||||
|
c
|
||||||
|
if c in string.ascii_letters or c.isdigit() or c == "_" or c == ":"
|
||||||
|
else f"u{hex(ord(c))}"
|
||||||
|
for c in metric
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def state_as_number(state):
|
def state_as_number(state):
|
||||||
"""Return a state casted to a float."""
|
"""Return a state casted to a float."""
|
||||||
|
@ -41,6 +41,11 @@ async def prometheus_client(loop, hass, hass_client):
|
|||||||
sensor3.entity_id = "sensor.electricity_price"
|
sensor3.entity_id = "sensor.electricity_price"
|
||||||
await sensor3.async_update_ha_state()
|
await sensor3.async_update_ha_state()
|
||||||
|
|
||||||
|
sensor4 = DemoSensor("Wind Direction", 25, None, "°", None)
|
||||||
|
sensor4.hass = hass
|
||||||
|
sensor4.entity_id = "sensor.wind_direction"
|
||||||
|
await sensor4.async_update_ha_state()
|
||||||
|
|
||||||
return await hass_client()
|
return await hass_client()
|
||||||
|
|
||||||
|
|
||||||
@ -103,3 +108,9 @@ def test_view(prometheus_client): # pylint: disable=redefined-outer-name
|
|||||||
'entity="sensor.electricity_price",'
|
'entity="sensor.electricity_price",'
|
||||||
'friendly_name="Electricity price"} 0.123' in body
|
'friendly_name="Electricity price"} 0.123' in body
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
'sensor_unit_u0xb0{domain="sensor",'
|
||||||
|
'entity="sensor.wind_direction",'
|
||||||
|
'friendly_name="Wind Direction"} 25.0' in body
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user