Add generic unavailable and last_updated metrics for prometheus (#37456)

* Add generic unavailable and last_updated metrics for prometheus

* Updated with feedback from the code review
This commit is contained in:
Eric Severance 2020-07-12 12:27:33 -07:00 committed by GitHub
parent c6ab2c5d0a
commit 16a947aa5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 14 deletions

View File

@ -25,6 +25,7 @@ from homeassistant.const import (
CONTENT_TYPE_TEXT_PLAIN,
EVENT_STATE_CHANGED,
STATE_ON,
STATE_UNAVAILABLE,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
UNIT_PERCENTAGE,
@ -153,13 +154,28 @@ class PrometheusMetrics:
handler = f"_handle_{domain}"
if hasattr(self, handler):
if hasattr(self, handler) and state.state != STATE_UNAVAILABLE:
getattr(self, handler)(state)
metric = self._metric(
labels = self._labels(state)
state_change = self._metric(
"state_change", self.prometheus_cli.Counter, "The number of state changes"
)
metric.labels(**self._labels(state)).inc()
state_change.labels(**labels).inc()
entity_available = self._metric(
"entity_available",
self.prometheus_cli.Gauge,
"Entity is available (not in the unavailable state)",
)
entity_available.labels(**labels).set(float(state.state != STATE_UNAVAILABLE))
last_updated_time_seconds = self._metric(
"last_updated_time_seconds",
self.prometheus_cli.Gauge,
"The last_updated timestamp",
)
last_updated_time_seconds.labels(**labels).set(state.last_updated.timestamp())
def _handle_attributes(self, state):
for key, value in state.attributes.items():

View File

@ -1,9 +1,9 @@
"""The tests for the Prometheus exporter."""
from dataclasses import dataclass
import datetime
import pytest
from homeassistant import setup
from homeassistant.components import climate, humidifier, sensor
from homeassistant.components.demo.sensor import DemoSensor
import homeassistant.components.prometheus as prometheus
@ -16,6 +16,7 @@ from homeassistant.const import (
)
from homeassistant.core import split_entity_id
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
import tests.async_mock as mock
@ -30,21 +31,18 @@ class FilterTest:
should_pass: bool
@pytest.fixture
async def prometheus_client(loop, hass, hass_client):
async def prometheus_client(hass, hass_client):
"""Initialize an hass_client with Prometheus component."""
await async_setup_component(hass, prometheus.DOMAIN, {prometheus.DOMAIN: {}})
await setup.async_setup_component(
hass, sensor.DOMAIN, {"sensor": [{"platform": "demo"}]}
)
await async_setup_component(hass, sensor.DOMAIN, {"sensor": [{"platform": "demo"}]})
await setup.async_setup_component(
await async_setup_component(
hass, climate.DOMAIN, {"climate": [{"platform": "demo"}]}
)
await hass.async_block_till_done()
await setup.async_setup_component(
await async_setup_component(
hass, humidifier.DOMAIN, {"humidifier": [{"platform": "demo"}]}
)
@ -60,6 +58,10 @@ async def prometheus_client(loop, hass, hass_client):
)
sensor2.hass = hass
sensor2.entity_id = "sensor.radio_energy"
with mock.patch(
"homeassistant.util.dt.utcnow",
return_value=datetime.datetime(1970, 1, 2, tzinfo=dt_util.UTC),
):
await sensor2.async_update_ha_state()
sensor3 = DemoSensor(
@ -89,9 +91,10 @@ async def prometheus_client(loop, hass, hass_client):
return await hass_client()
async def test_view(prometheus_client): # pylint: disable=redefined-outer-name
async def test_view(hass, hass_client):
"""Test prometheus metrics view."""
resp = await prometheus_client.get(prometheus.API_ENDPOINT)
client = await prometheus_client(hass, hass_client)
resp = await client.get(prometheus.API_ENDPOINT)
assert resp.status == 200
assert resp.headers["content-type"] == "text/plain"
@ -167,6 +170,18 @@ async def test_view(prometheus_client): # pylint: disable=redefined-outer-name
'friendly_name="Radio Energy"} 14.0' in body
)
assert (
'entity_available{domain="sensor",'
'entity="sensor.radio_energy",'
'friendly_name="Radio Energy"} 1.0' in body
)
assert (
'last_updated_time_seconds{domain="sensor",'
'entity="sensor.radio_energy",'
'friendly_name="Radio Energy"} 86400.0' in body
)
assert (
'sensor_unit_sek_per_kwh{domain="sensor",'
'entity="sensor.electricity_price",'