Skip to content

Add url.template attribute to otel4s-metrics-backend#2840

Open
remimomprive wants to merge 1 commit intosoftwaremill:masterfrom
remimomprive:feat/add-url-template
Open

Add url.template attribute to otel4s-metrics-backend#2840
remimomprive wants to merge 1 commit intosoftwaremill:masterfrom
remimomprive:feat/add-url-template

Conversation

@remimomprive
Copy link

@remimomprive remimomprive commented Mar 25, 2026

Implementing the experimental attribute url.template for the metric http.client.request.duration
See https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpclientrequestduration

Before submitting pull request:

  • Check if the project compiles by running sbt compile
  • Verify docs compilation by running sbt compileDocs
  • Check if tests pass by running sbt test
  • Format code by running sbt scalafmt

@remimomprive remimomprive force-pushed the feat/add-url-template branch 2 times, most recently from 20d3ac5 to 7020e90 Compare March 25, 2026 22:02
@remimomprive remimomprive force-pushed the feat/add-url-template branch 2 times, most recently from 5214224 to ac192df Compare March 25, 2026 22:34
@adamw adamw requested a review from Copilot March 26, 2026 08:49
Copy link

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

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-experimental dependency 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.

Comment on lines +231 to +236
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)
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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}"

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +224
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 }

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
@adamw
Copy link
Member

adamw commented Mar 26, 2026

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?

@remimomprive
Copy link
Author

remimomprive commented Mar 26, 2026

As I understand, the PR includes a heuristic to determine what's the template. Can you describe the heuristic?

It replaces all the magic numbers and UUIDs from both the path parameters and query parameters with {id}

Why do you think it would work in a generic setting?

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 Otel4sMetricsConfig and let the user chose the implementation

@adamw
Copy link
Member

adamw commented Mar 26, 2026

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 Uri => Option[String] or sth like this, which would return the template. One out-of-the-box implementation of the function could be what you have in code now, just not activated by default. Some docs around this would be great to have as well.

@remimomprive
Copy link
Author

Sure! I'll rework that to apply the comments

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.

3 participants