Lines
100.00%
45 / 45
Functions and Methods
100.00%
1 / 1
Classes and Traits
100.00%
1 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| AppMetricSource | 100.00% | 45 / 45 | 100.00% | 1 / 1 | 9 | 100.00% | 1 / 1 | |||
| collectMetrics | 100.00% | 45 / 45 | 100.00% | 1 / 1 | 9 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace Stewart\Runtime\Metrics\Source; | |
| 6 | ||
| 7 | use Stewart\Runtime\Control\Protocol\Status\RuntimeSnapshot; | |
| 8 | use Stewart\Runtime\Lifecycle\AppState; | |
| 9 | use Stewart\Runtime\Metrics\Exposition\Histogram; | |
| 10 | use Stewart\Runtime\Metrics\Exposition\MetricFamily; | |
| 11 | use Stewart\Runtime\Metrics\Exposition\MetricLabels; | |
| 12 | use Stewart\Runtime\Metrics\Exposition\MetricType; | |
| 13 | use Stewart\Runtime\Metrics\Exposition\PrometheusNumber; | |
| 14 | use Stewart\Runtime\Metrics\RuntimeMetricSource; | |
| 15 | use Stewart\Runtime\Model\ResourceScope; | |
| 16 | ||
| 17 | final readonly class AppMetricSource implements RuntimeMetricSource | |
| 18 | { | |
| 19 | /** @return list<MetricFamily> */ | |
| 20 | public function collectMetrics(RuntimeSnapshot $snapshot): array | |
| 21 | { | |
| 22 | $info = new MetricFamily('stewart_app_info', 'App class per app; always 1.', MetricType::Gauge); | |
| 23 | $states = new MetricFamily('stewart_app_state', 'App lifecycle state; 1 for the current one.', MetricType::Gauge); | |
| 24 | $paused = new MetricFamily('stewart_app_paused', 'Whether the app is paused; 1 or 0.', MetricType::Gauge); | |
| 25 | $subscriptions = new MetricFamily('stewart_app_subscriptions', 'Subscriptions the app holds.', MetricType::Gauge); | |
| 26 | $schedules = new MetricFamily('stewart_app_schedules', 'Schedules the app holds.', MetricType::Gauge); | |
| 27 | $exposedEntities = new MetricFamily('stewart_app_exposed_entities', 'Home Assistant entities the app exposes, unavailable ones included.', MetricType::Gauge); | |
| 28 | $delivered = new MetricFamily('stewart_app_events_delivered_total', 'Events delivered to the app handlers.', MetricType::Counter); | |
| 29 | $subscriptionDropped = new MetricFamily('stewart_app_subscription_dropped_total', 'Events dropped because a subscription queue was full.', MetricType::Counter); | |
| 30 | $scheduleRuns = new MetricFamily('stewart_app_schedule_runs_total', 'Scheduled handler runs.', MetricType::Counter); | |
| 31 | $publishes = new MetricFamily('stewart_app_publishes_total', 'Messages the app published.', MetricType::Counter); | |
| 32 | $failures = new MetricFamily('stewart_app_failures_total', 'Handler failures of the app.', MetricType::Counter); | |
| 33 | $suppressed = new MetricFamily('stewart_app_suppressed_total', 'Work skipped while the app was paused.', MetricType::Counter); | |
| 34 | $lastFailure = new MetricFamily('stewart_app_last_failure_timestamp_seconds', 'Unix time of the last app failure.', MetricType::Gauge); | |
| 35 | $serviceCalls = new MetricFamily('stewart_app_service_calls_total', 'Home Assistant calls of the app by outcome.', MetricType::Counter); | |
| 36 | $exposedCommands = new MetricFamily('stewart_app_exposed_commands_total', 'Home Assistant commands on the app\'s exposed entities by outcome.', MetricType::Counter); | |
| 37 | $serviceCallDuration = new MetricFamily('stewart_app_service_call_duration_seconds', 'Home Assistant call latency of the app by outcome.', MetricType::Histogram); | |
| 38 | ||
| 39 | foreach ($snapshot->apps as $app) { | |
| 40 | $labels = MetricLabels::fromLabel('app', $app->id)->withLabel('worker', $app->workerId === null ? '' : (string) $app->workerId); | |
| 41 | ||
| 42 | if (ResourceScope::tryFromWireValue($app->id)?->isShared() !== true) { | |
| 43 | $info->recordSample($labels->withLabel('class', $app->class), 1); | |
| 44 | ||
| 45 | foreach (AppState::cases() as $state) { | |
| 46 | $states->recordSample($labels->withLabel('state', $state->value), $state === $app->state); | |
| 47 | } | |
| 48 | ||
| 49 | $paused->recordSample($labels, $app->pause !== null); | |
| 50 | } | |
| 51 | ||
| 52 | $subscriptions->recordSample($labels, $app->subscriptions); | |
| 53 | $schedules->recordSample($labels, $app->schedules); | |
| 54 | $exposedEntities->recordSample($labels, $app->exposedEntities); | |
| 55 | $delivered->recordSample($labels, $app->counters->delivered); | |
| 56 | $subscriptionDropped->recordSample($labels, $app->counters->subscriptionDropped); | |
| 57 | $scheduleRuns->recordSample($labels, $app->counters->scheduleRuns); | |
| 58 | $publishes->recordSample($labels, $app->counters->publishes); | |
| 59 | $failures->recordSample($labels, $app->counters->failures); | |
| 60 | $suppressed->recordSample($labels, $app->counters->suppressed); | |
| 61 | ||
| 62 | if ($app->lastFailure !== null) { | |
| 63 | $lastFailure->recordSample($labels, PrometheusNumber::convertToEpochSeconds($app->lastFailure->at)); | |
| 64 | } | |
| 65 | ||
| 66 | foreach ($app->exposedCommands as $commands) { | |
| 67 | $exposedCommands->recordSample($labels->withLabel('outcome', $commands->outcome->value), $commands->count); | |
| 68 | } | |
| 69 | ||
| 70 | foreach ($app->serviceCalls as $calls) { | |
| 71 | $outcomeLabels = $labels->withLabel('outcome', $calls->outcome->value); | |
| 72 | $serviceCalls->recordSample($outcomeLabels, $calls->count); | |
| 73 | ||
| 74 | if ($calls->latency !== null) { | |
| 75 | $serviceCallDuration->recordHistogram($outcomeLabels, Histogram::fromLatencyHistogram($calls->latency)); | |
| 76 | } | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | return [ | |
| 81 | $info, $states, $paused, $subscriptions, $schedules, $exposedEntities, $delivered, $subscriptionDropped, $scheduleRuns, $publishes, | |
| 82 | $failures, $suppressed, $lastFailure, $serviceCalls, $serviceCallDuration, $exposedCommands, | |
| 83 | ]; | |
| 84 | } | |
| 85 | } |