Skip to content

feat(codecs): support encoding native Vector metrics in OTLP serializer#25738

Open
petere-datadog wants to merge 2 commits into
masterfrom
peter.ehik/OPA-5715/implement-otlp-metric-encoder
Open

feat(codecs): support encoding native Vector metrics in OTLP serializer#25738
petere-datadog wants to merge 2 commits into
masterfrom
peter.ehik/OPA-5715/implement-otlp-metric-encoder

Conversation

@petere-datadog

Copy link
Copy Markdown
Contributor

Summary

Vector configuration

How did you test this PR?

Change Type

  • Bug fix
  • New feature
  • Dependencies
  • Non-functional (chore, refactoring, docs)
  • Performance

Is this a breaking change?

  • Yes
  • No

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.
  • No. A maintainer will apply the no-changelog label to this PR.

References

Notes

  • Please read our Vector contributor resources.
  • Do not hesitate to use @vectordotdev/vector to reach out to us regarding this PR.
  • Some CI checks run only after we manually approve them.
    • We recommend adding a pre-push hook, please see this template.
    • Alternatively, we recommend running the following locally before pushing to the remote branch:
      • make fmt
      • make check-clippy (if there are failures it's possible some of them can be fixed with make clippy-fix)
      • make test
  • After a review is requested, please avoid force pushes to help us review incrementally.
    • Feel free to push as many commits as you want. They will be squashed into one before merging.
    • For example, you can run git merge origin master and git push.
  • If this PR introduces changes Vector dependencies (modifies Cargo.lock), please
    run make build-licenses to regenerate the license inventory and commit the changes (if any). More details on the dd-rust-license-tool.

Adds metric -> OTLP conversion to the OTLP codec's serializer, covering
Counter, Gauge, AggregatedHistogram, and AggregatedSummary values via
direct prost encoding. Unsupported metric types (Set, Distribution,
Sketch) return an error naming the variant. The metric -> OTLP -> metric
round trip is deterministic; the reverse direction remains lossy by
design (e.g. ExponentialHistogram flattens to AggregatedHistogram).
@petere-datadog petere-datadog requested a review from a team as a code owner July 1, 2026 21:49
@datadog-vectordotdev

datadog-vectordotdev Bot commented Jul 1, 2026

Copy link
Copy Markdown

Pipelines  Tests

⚠️ Warnings

🚦 3 Pipeline jobs failed

Test Suite | Check Markdown   View in Datadog   GitHub Actions

Test Suite | Check code format   View in Datadog   GitHub Actions

Test Suite | Test Suite   View in Datadog   GitHub Actions

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 1e9b90e | Docs | Give us feedback!

@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: 1e9b90ea1a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +540 to +543
let bucket_counts: Vec<u64> = buckets.iter().map(|bucket| bucket.count).collect();
let explicit_bounds: Vec<f64> = buckets
.iter()
.take(buckets.len().saturating_sub(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 Preserve finite histogram bounds when encoding

For native AggregatedHistograms that do not already carry an explicit +Inf bucket, this drops the last real upper bound and never adds the implicit overflow bucket. That is the normal shape for Prometheus-derived Vector histograms—the parser removes the +Inf bucket (src/sources/prometheus/parser.rs:134-138) and the Prometheus exporter reconstructs it from count (src/sinks/prometheus/collector.rs:133-159). Encoding such a metric as OTLP turns a bucket like le=2 into the implicit +Inf bucket, and if there were observations above the last finite bound the OTLP count no longer matches the sum of bucket_counts; keep all finite bounds and append count - sum(bucket_counts) only when there is no infinity bucket.

Useful? React with 👍 / 👎.

let (resource, scope, attributes) = split_metric_tags(tags);

let kind = metric.kind();
let name = metric.name().to_string();

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 Preserve metric namespaces in OTLP names

When a native Vector metric has a namespace, this emits only Metric::name(), but the namespace is part of the metric series and is included by the existing metric sinks (for example Prometheus uses encode_namespace(metric.namespace().or(default_namespace), '_', metric.name()) in src/sinks/prometheus/collector.rs:41). Exporting namespaced metrics through the new OTLP serializer therefore renames/collides series such as vector.requests and app.requests into the same OTLP metric name requests; include the namespace somewhere in the encoded OTLP identity instead of discarding it.

Useful? React with 👍 / 👎.

match tag_set {
TagValueSet::Empty => None,
TagValueSet::Single(tag) => Some(tag.clone()),
TagValueSet::Set(set) => set.iter().last().cloned(),

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 Preserve all multi-valued metric tags

When a metric tag contains multiple values, this keeps only the last one, so native metrics whose tags were created through Vector's multi-value tag support lose label data on OTLP export. This is reachable from VRL (metric.set_multi_value_tag in lib/vector-core/src/event/vrl_target.rs:268) and existing metric encoders such as Datadog preserve every value via iter_all() (src/sinks/datadog/metrics/encoder.rs:724-730); if OTLP cannot represent these as repeated keys, encode an array value or return an error rather than silently dropping values.

Useful? React with 👍 / 👎.

Comment on lines +608 to +612
let timestamp_ns = metric
.timestamp()
.and_then(|ts| ts.timestamp_nanos_opt())
.unwrap_or_else(|| Utc::now().timestamp_nanos_opt().unwrap_or(0))
as u64;

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 Avoid wrapping pre-epoch metric timestamps

For native metrics with a timestamp before 1970-01-01T00:00:00Z, timestamp_nanos_opt() returns a negative i64 and this cast wraps it into a huge u64, so the OTLP point is emitted with a far-future timestamp instead of the original time or an error. Vector metrics can carry user-provided timestamps, so pre-epoch or otherwise invalid OTLP timestamps should be rejected or clamped before converting to the unsigned OTLP field.

Useful? React with 👍 / 👎.

MetricValue::Counter { value } => Ok(Data::Sum(Sum {
data_points: vec![NumberDataPoint {
attributes: attrs,
start_time_unix_nano: 0,

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 Set OTLP start time from metric intervals

For incremental counters and histograms that carry interval_ms, this hard-codes the OTLP start time to zero, so a 10-second delta from sources such as the Datadog agent (src/sources/datadog_agent/metrics.rs:370-371) is exported as covering from the Unix epoch to the sample timestamp. Vector sinks already treat interval_ms as meaningful rate/window metadata (src/sinks/new_relic/model.rs:78-85), and the OTLP proto comments say StartTimeUnixNano supports correct rate calculation; compute timestamp_ns - interval_ms when the interval is present instead of discarding it.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant