Skip to content

Add OpenMetrics HTTP blackhole body#1895

Open
scottopell wants to merge 3 commits into
mainfrom
sopell/openmetrics
Open

Add OpenMetrics HTTP blackhole body#1895
scottopell wants to merge 3 commits into
mainfrom
sopell/openmetrics

Conversation

@scottopell

Copy link
Copy Markdown
Contributor

Summary

Adds a reusable lading_payload OpenMetrics module and wires it into the HTTP blackhole as an openmetrics body variant. This lets scrape-based tests serve generated Prometheus/OpenMetrics text bodies without embedding large static YAML strings.

Validation

Local checks run:

  • cargo fmt --all
  • ci/fmt
  • cargo check -p lading-payload
  • cargo check -p lading --no-default-features --tests
  • cargo test -p lading-payload
  • cargo test -p lading --no-default-features blackhole::http::tests -- --nocapture

Full ci/validate could not run in the workspace because local system dependencies are missing: shellcheck, and all-features checks need FUSE libs that CI installs.

Notes

Example config added in examples/lading-openmetrics.yaml.

@datadog-official

datadog-official Bot commented May 23, 2026

Copy link
Copy Markdown

Pipelines

Fix all issues with BitsAI

⚠️ Warnings

🚦 1 Pipeline job failed

Container | Build amd64   View in Datadog   GitHub Actions

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 3bfa033 | Docs | Datadog PR Page | Give us feedback!

@blt blt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good. @scottopell give me a ping when it's open for review and I'll have a look.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a reusable lading_payload::openmetrics generator and wires it into the HTTP blackhole so it can serve a generated Prometheus/OpenMetrics-style text exposition body via a new openmetrics body_variant, avoiding embedding large static response strings in configs.

Changes:

  • Added lading_payload::openmetrics module that deterministically precomputes a scrape response body plus tests (unit + proptest).
  • Extended HTTP blackhole config with body_variant.openmetrics and precomputes the body during Http::new.
  • Added an example config and documented the new variant in the changelog.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lading/src/blackhole/http.rs Adds openmetrics body variant, deserialization support, and a config test for the new variant.
lading_payload/src/openmetrics.rs Implements deterministic, precomputed text exposition generator with validation and tests.
lading_payload/src/lib.rs Exposes the new openmetrics module and re-exports OpenMetrics.
examples/lading-openmetrics.yaml Provides an example HTTP blackhole configuration serving generated scrape text.
CHANGELOG.md Notes the new HTTP blackhole openmetrics body variant.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lading_payload/src/openmetrics.rs Outdated
Comment on lines +1 to +5
//! `OpenMetrics` text exposition payload.
//!
//! This generator builds a deterministic Prometheus text exposition body for
//! scrape-oriented tests. The body is fully precomputed before serving so that
//! lading does no payload generation work on request hot paths.
Comment on lines +332 to +343
fn validate_metric_prefix(prefix: &str) -> Result<(), String> {
if prefix.is_empty() {
return Err("metric_name_prefix cannot be empty".to_string());
}
if !prefix
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == ':')
{
return Err("metric_name_prefix contains invalid characters".to_string());
}
Ok(())
}
@scottopell scottopell force-pushed the sopell/openmetrics branch from faed763 to 3bfa033 Compare June 25, 2026 18:46
@scottopell scottopell marked this pull request as ready for review June 25, 2026 20:44
@scottopell scottopell requested a review from a team as a code owner June 25, 2026 20:44

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bfa0338d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"counter",
"Synthetic monotonic request counter.",
)?;
for index in 0..config.counters.count {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep generated counter series unique

With the default config this loop emits 240 counter samples, but the label tuple generated below repeats every 60 indices (for example, index 0 and 60 both produce the same service/region/method/status/route labels) with different values. OpenMetrics defines a metric by a unique LabelSet, and Prometheus/OpenMetrics scrapers reject duplicate samples in one scrape, so the default/example openmetrics response is invalid until these labels are made unique or the count is capped to the generated cardinality.

Useful? React with 👍 / 👎.

Comment on lines +308 to +309
write_build_info(config, &mut body)?;
sample_count += 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Append the required OpenMetrics EOF marker

When users serve this generated body as OpenMetrics, for example with Content-Type: application/openmetrics-text, generation stops after build_info and never appends # EOF. The OpenMetrics text format requires expositions to end with EOF, so strict OpenMetrics scrapers/parsers will reject these responses even though the variant is advertised as OpenMetrics; append the EOF line after the final metric.

Useful? React with 👍 / 👎.

Comment on lines +435 to +436
"requests_total",
"counter",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use an unsuffixed counter family name

For OpenMetrics responses, declaring the family as requests_total makes the emitted samples invalid because a counter's Total sample name must be the family name plus the _total suffix; strict parsers will expect lading_openmetrics_requests_total_total for this TYPE line, not the lading_openmetrics_requests_total samples emitted below. Declare the family as requests (while keeping the sample name suffixed) or emit the required suffixed sample name when serving OpenMetrics text.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants