Add url.template attribute to otel4s-metrics-backend#2840
Add url.template attribute to otel4s-metrics-backend#2840remimomprive wants to merge 1 commit intosoftwaremill:masterfrom
Conversation
20d3ac5 to
7020e90
Compare
5214224 to
ac192df
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for the experimental url.template semantic attribute in the otel4s-metrics-backend, aligning emitted attributes with the experimental OpenTelemetry HTTP client metrics semconv.
Changes:
- Emit
url.template(experimental) on metrics attributes via a simple URI templating heuristic. - Update semantic test to use
HttpExperimentalMetrics.ClientRequestDuration. - Add
otel4s-semconv-experimentaldependency required for the new attribute.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| observability/otel4s-metrics-backend/src/main/scala/sttp/client4/opentelemetry/otel4s/Otel4sMetricsBackend.scala | Adds url.template attribute emission and URI templating helper. |
| observability/otel4s-metrics-backend/src/test/scala/sttp/client4/opentelemetry/otel4s/Otel4sMetricsBackendTest.scala | Switches duration metric semantic spec to experimental variant. |
| build.sbt | Adds otel4s-semconv-experimental dependency for main compilation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case Uri.QuerySegment.KeyValue(k, v, _, _) => | ||
| s"$k=${if (IdRegex.matches(v)) IdPlaceholder else v}" | ||
| case Uri.QuerySegment.Value(v, _) => | ||
| if (IdRegex.matches(v)) IdPlaceholder else v | ||
| case Uri.QuerySegment.Plain(v, _) => | ||
| IdRegex.replaceAllIn(v, IdPlaceholder) |
There was a problem hiding this comment.
urlTemplate currently includes query parameter values in the emitted url.template (e.g., k=v is kept when v doesn't match IdRegex). Query values commonly contain tokens/PII and can also explode metric cardinality. Consider omitting the query string from url.template entirely, or at minimum templating all query values (e.g., k={value}) so no raw values are exported.
| case Uri.QuerySegment.KeyValue(k, v, _, _) => | |
| s"$k=${if (IdRegex.matches(v)) IdPlaceholder else v}" | |
| case Uri.QuerySegment.Value(v, _) => | |
| if (IdRegex.matches(v)) IdPlaceholder else v | |
| case Uri.QuerySegment.Plain(v, _) => | |
| IdRegex.replaceAllIn(v, IdPlaceholder) | |
| case Uri.QuerySegment.KeyValue(k, _, _, _) => | |
| s"$k={value}" | |
| case Uri.QuerySegment.Value(_, _) => | |
| "{value}" | |
| case Uri.QuerySegment.Plain(_, _) => | |
| "{value}" |
| private def urlTemplate(request: GenericRequest[_, _]): Option[String] = { | ||
| val rawSegments = request.uri.pathSegments.segments | ||
| val templatedSegments = rawSegments.map(s => if (IdRegex.matches(s.v)) IdPlaceholder else s.v) | ||
| val pathChanged = rawSegments.zip(templatedSegments).exists { case (s, t) => s.v != t } | ||
|
|
There was a problem hiding this comment.
The new urlTemplate logic is non-trivial (path+query templating, change detection) but the existing semantic test only asserts required attributes and may not fail if url.template is missing/incorrect. Add a focused test case that sends a request with an ID-like path/query (e.g., /users/123?token=...) and asserts the recorded metric attributes include url.template with the expected placeholders.
|
As I understand, the PR includes a heuristic to determine what's the template. Can you describe the heuristic? Why do you think it would work in a generic setting? |
It replaces all the magic numbers and UUIDs from both the path parameters and query parameters with
It works for the examples from the spec (https://opentelemetry.io/docs/specs/semconv/registry/attributes/url/#url-template). If that's too specific, we can add a default implementation in |
|
Thanks I see :) I think that the URLs in the wild might be very varied. Maybe we can have this as an optional feature, activated by the config? And in the config, we could have a function |
|
Sure! I'll rework that to apply the comments |
Implementing the experimental attribute
url.templatefor the metrichttp.client.request.durationSee https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpclientrequestduration
Before submitting pull request:
sbt compilesbt compileDocssbt testsbt scalafmt