feat(metrics): add Prometheus text format deserialization#1729
Conversation
- Add `PrometheusDeserializable` trait to `prometheus.rs` alongside the existing `PrometheusSerializable` trait - Add `PrometheusDeserializationError` with fine-grained variants: ParseError, UnsupportedType, UnknownType, ValueMismatch, UnknownValue, LabelConversion, CollectionError - Implement `TryFrom<openmetrics_parser::LabelSet>` for `LabelSet` - Extract private `parse_prometheus_timestamp` helper to eliminate duplicated timestamp-parsing logic - Implement `PrometheusDeserializable for MetricCollection` using `openmetrics-parser` 0.4.4; no silent zero-returns for unknown or mismatched values - Add unit tests for the timestamp helper, label conversion, and full round-trip serialize → deserialize Closes torrust#1582 Co-authored-by: josecelano <josecelano@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds Prometheus exposition text-format deserialization to the packages/metrics crate by introducing a PrometheusDeserializable trait and implementing it for MetricCollection (currently Counter + Gauge families), aiming for symmetry with existing to_prometheus() serialization.
Changes:
- Added
PrometheusDeserializable+PrometheusDeserializationErrorto model Prometheus parsing and error reporting. - Implemented Prometheus text parsing for
MetricCollectionusingopenmetrics-parserand added unit tests (timestamps, label conversion, round-trip, and error paths). - Introduced
TryFrom<openmetrics_parser::LabelSet>forLabelSetand addedopenmetrics-parser = "0.4.4"dependency.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| project-words.txt | Adds new spelling whitelist entries used by the implementation/docs. |
| packages/metrics/src/prometheus.rs | Introduces deserialization trait and structured error type for Prometheus parsing. |
| packages/metrics/src/metric_collection/mod.rs | Implements MetricCollection::from_prometheus, adds timestamp parsing helper + tests. |
| packages/metrics/src/label/set.rs | Adds TryFrom conversion from openmetrics_parser::LabelSet + tests. |
| packages/metrics/Cargo.toml | Adds the openmetrics-parser dependency. |
| docs/issues/1582-add-prometheus-deserialization-metrics/ISSUE.md | Adds design/spec documentation for the feature. |
| Cargo.lock | Locks new transitive dependencies for openmetrics-parser. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #1729 +/- ##
===========================================
+ Coverage 79.66% 79.69% +0.02%
===========================================
Files 354 359 +5
Lines 25597 26411 +814
Branches 25597 26411 +814
===========================================
+ Hits 20392 21047 +655
- Misses 4969 5117 +148
- Partials 236 247 +11 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Tighten nanosecond overflow assertion from approx epsilon 1e-3 to exact equality, catching the 'nanos - 1_000_000_000 → /' mutant. - Add it_should_accept_a_counter_value_that_is_a_whole_number_float to exercise the float-counter match guard (value.is_finite() && >= 0.0 && fract() == 0.0 && < MAX), killing four related guard mutations. - Add it_should_reject_a_float_counter_value_equal_to_first_unrepresentable_u64 to verify the strict-less-than bound (< not <=) for 2^64. - Exclude mutants.out / mutants.out.old from cspell to prevent 'xyzzy' false-positive failures from cargo-mutants artefacts. Result: 35 mutants tested — 24 caught, 11 unviable, 0 survived.
…eline Extract family-iteration logic into exposition_to_metric_collection helper function and refactor from_prometheus to coordinate three distinct stages: - Stage 1 (Normalize): ensure_trailing_newline - Stage 2 (Parse): openmetrics_parser::prometheus::parse_prometheus - Stage 3 (Convert): exposition_to_metric_collection The conversion stage encapsulates the dispatch logic for Counter/Gauge types, improving testability and extensibility. Future metric type support only needs to modify the conversion stage. All 253 tests pass. No behavior changes.
|
ACK 19f98d0 |
Summary
Adds deserialization from Prometheus exposition text format to
MetricCollection, completing the serialization ↔ deserialization symmetry alongside the existing JSON andto_prometheus()paths.Closes #1582
What changed
packages/metrics/src/prometheus.rsPrometheusDeserializabletrait (mirrorsPrometheusSerializable)PrometheusDeserializationErrorenum with fine-grained variants:ParseError— syntax error in the input textUnsupportedType— known but not yet supported type (Histogram, Summary, …)UnknownType— unrecognised metric typeValueMismatch— value type does not match the declared metric typeUnknownValue—PrometheusValue::Unknownsample (no silent zero-returns)LabelConversion— label name/value conversion failureCollectionError— structural error assembling theMetricCollectionpackages/metrics/src/label/set.rsTryFrom<openmetrics_parser::LabelSet<'_>> for LabelSet— replaces ad-hoc inline label conversionpackages/metrics/src/metric_collection/mod.rsparse_prometheus_timestamp(t: f64, fallback) -> DurationSinceUnixEpoch— eliminates the duplicated timestamp-parsing blockimpl PrometheusDeserializable for MetricCollection— parses Counter and Gauge families; usesopenmetrics-parser 0.4.4packages/metrics/Cargo.tomlopenmetrics-parser = "0.4.4"Tests
TryFromforLabelSet: 3 unit tests (empty conversion, round-trip, empty-name error)MetricCollection: round-trip serialize → deserialize, error-path tests for unsupported and malformed inputPrior art
This is a clean re-implementation based on PR #1611 by @naoNao89, incorporating all maintainer feedback that was pending on that PR. @naoNao89 is preserved as the commit author.