Skip to content

Align OpenTelemetry HTTP metrics with semantic conventions#5188

Merged
adamw merged 2 commits intomasterfrom
align-opentelemetry-http-metrics-conventions
Apr 22, 2026
Merged

Align OpenTelemetry HTTP metrics with semantic conventions#5188
adamw merged 2 commits intomasterfrom
align-opentelemetry-http-metrics-conventions

Conversation

@adamw
Copy link
Copy Markdown
Member

@adamw adamw commented Apr 21, 2026

Summary

Brings the metrics registered by OpenTelemetryMetrics in line with the OpenTelemetry HTTP server semantic conventions:

  • http.server.request.duration: unit changed from ms to s; values now recorded in seconds (computed from nanoseconds to preserve sub-ms precision); added the spec-recommended ExplicitBucketBoundariesAdvice ([0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]).
  • http.server.active_requests: unit changed from 1 to {request}.
  • http.server.request.total: unit changed from 1 to {request}. Docstring now notes this metric is not defined by the semantic conventions — the count is implicitly provided by the duration histogram. Kept because addRequestsTotal is part of the public API.
  • Updated docstrings to reference real emitted metric names and attribute keys (http.response.status_code, http.route) instead of the previous placeholders.

No attribute keys changed; existing tests don't assert on units or exact duration values, so no test changes were needed.

Test plan

  • opentelemetryMetrics/test (Scala 2.13)
  • opentelemetryMetrics3/test (Scala 3)
  • Verify exported metrics in a sample app include http.server.request.duration in seconds with the recommended buckets

🤖 Generated with Claude Code

Per https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#http-server:
- `http.server.request.duration`: unit `s` (was `ms`); record seconds;
  add recommended ExplicitBucketBoundariesAdvice
- `http.server.active_requests`: unit `{request}` (was `1`)
- `http.server.request.total`: unit `{request}` (was `1`); note in
  docstring that this metric is not part of the semantic conventions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 21, 2026 15:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Aligns Tapir’s OpenTelemetry HTTP server metrics (OpenTelemetryMetrics) with the OpenTelemetry HTTP server semantic conventions (names/units/buckets), so exported metrics match the spec and common dashboards/collectors.

Changes:

  • Renames documented metric names to http.server.* and updates docstrings to reference the actual emitted metric/attribute names.
  • Updates metric units for active requests / request totals to {request}.
  • Updates request duration histogram to record seconds (from nanoseconds) and adds explicit bucket boundary advice per the HTTP semconv recommendations.

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

copy(metrics = metrics :+ requestTotal(meter, labels))

/** Registers a `request_duration_seconds{path, method, status, phase}` histogram (assuming default labels). */
/** Registers a `http.server.request.duration` histogram (assuming default labels). */
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

The addRequestsDuration Scaladoc no longer mentions the unit, but the implementation now records values in seconds (setUnit("s") and nanos-to-seconds conversion). Please document the unit here as well to avoid consumers assuming milliseconds (especially since older versions used ms).

Suggested change
/** Registers a `http.server.request.duration` histogram (assuming default labels). */
/** Registers a `http.server.request.duration` histogram (assuming default labels), recording durations in seconds (`s`). */

Copilot uses AI. Check for mistakes.
Comment on lines +174 to +176
.setExplicitBucketBoundariesAdvice(
java.util.Arrays.asList[java.lang.Double](0.005d, 0.01d, 0.025d, 0.05d, 0.075d, 0.1d, 0.25d, 0.5d, 0.75d, 1d, 2.5d, 5d, 7.5d, 10d)
)
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

The explicit bucket boundaries list is hard-coded inline, which hurts readability and makes it harder to reuse/update in the future. Consider extracting the boundaries into a named val/private constant (e.g., httpServerRequestDurationBucketsSeconds) and referencing it here.

Copilot uses AI. Check for mistakes.
Comment on lines 179 to 182
m.eval {
val requestStart = Instant.now()
def duration = Duration.between(requestStart, Instant.now()).toMillis.toDouble
def duration = Duration.between(requestStart, Instant.now()).toNanos.toDouble / 1e9
EndpointMetric()
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

Instant.now()/Duration.between uses the wall clock, so NTP/time adjustments can produce negative or skewed durations that end up recorded in the histogram. For elapsed-time measurement, prefer a monotonic source (e.g., capture System.nanoTime() at request start and compute (System.nanoTime() - startNanos) / 1e9d).

Copilot uses AI. Check for mistakes.
- Scaladoc on `addRequestsDuration` now notes durations are in seconds
- Extract explicit bucket boundaries into
  `HttpServerRequestDurationBucketsSeconds` constant
- Measure duration with `System.nanoTime` (monotonic) instead of
  `Instant.now` to avoid wall-clock skew producing negative/bad values

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@adamw adamw merged commit 4e1cdec into master Apr 22, 2026
37 of 38 checks passed
@adamw adamw deleted the align-opentelemetry-http-metrics-conventions branch April 22, 2026 06:26
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.

2 participants