Skip to content

tracing: name HTTP spans as "{method} {route}" per OTel semconv#8632

Open
ChrisJr404 wants to merge 1 commit into
open-policy-agent:mainfrom
ChrisJr404:feat-otel-http-span-names
Open

tracing: name HTTP spans as "{method} {route}" per OTel semconv#8632
ChrisJr404 wants to merge 1 commit into
open-policy-agent:mainfrom
ChrisJr404:feat-otel-http-span-names

Conversation

@ChrisJr404
Copy link
Copy Markdown

Closes #8612.

OPA's tracing emits HTTP spans named after the Prometheus handler label on the server side (e.g. v1/data, health) and HTTP {METHOD} on the client side. The OpenTelemetry HTTP semantic conventions prescribe span names of the form {method} {route} (server) and {method} {target} (client), which makes traces in tools like Jaeger far easier to scan.

Change

In v1/features/tracing/tracing.go, install default WithSpanNameFormatter options on both the otelhttp Handler and Transport:

  • Server spans: "{METHOD} {operation}", where operation is the route label OPA already passes to NewHandler. So POST v1/data, GET v1/data, DELETE v1/policies, GET health, etc.
  • Client spans: "{METHOD} {url.path}", falling back to "{METHOD}" when no path is available, replacing otelhttp's legacy "HTTP {METHOD}".

The defaults are prepended to the converted otelhttp options. Because otelhttp applies options in order with last-writer-wins semantics on SpanNameFormatter, anyone passing their own WithSpanNameFormatter through tracing.Options still wins. A dedicated unit test covers that override path.

Notes on naming

The reporter's example used POST /v1/data/:path (the registered Go 1.22 mux pattern with the {path...} capture). I used the existing operation label (v1/data) rather than the full mux pattern for two reasons:

  1. The label is already low-cardinality and stable, designed for exactly this kind of metrics/trace grouping. {path...} would collapse the same way at the visualization layer but adds noise to the span name.
  2. The label is what NewHandler already receives, so the formatter is route-stable without any plumbing changes through the server.

If the maintainers would prefer the full registered pattern (e.g. POST /v1/data/{path...}), I'm happy to wire instrumentHandler to pass the pattern alongside the Prom label and switch the formatter to use it.

Tests

  • New unit tests in v1/features/tracing/tracing_test.go cover:
    • serverSpanName table tests (method, empty operation, empty method, nil request).
    • clientSpanName table tests (with/without path, empty method, nil request).
    • End-to-end via otelhttp.NewHandler + httptest.Server: span name is POST v1/data.
    • End-to-end via otelhttp.NewTransport: client span name is POST /v1/logs.
    • User-supplied WithSpanNameFormatter overrides the default.
  • Updated assertions in v1/test/e2e/distributedtracing/distributedtracing_test.go to match the new server (POST v0/data, GET v1/data, POST authz, POST v1/data) and client (POST /logs) names.

Local runs that pass on this branch:

go test ./v1/features/tracing/...
go test ./v1/test/e2e/distributedtracing/...
go test ./v1/server/...
go test ./v1/runtime/...
go vet ./v1/features/tracing/... ./v1/test/e2e/distributedtracing/... ./v1/server/... ./v1/tracing/...

Compat

Anyone parsing span names from Jaeger/Tempo/etc. will see a string change (v1/data to POST v1/data, HTTP POST to POST /v1/logs). That seems acceptable: the existing names didn't conform to the spec, the OTel attributes (http.request.method, url.path, etc.) on the span are unchanged, and the new names are what the OTel docs say tracing UIs should be grouping on.

Signed-off-by: ChrisJr404 chris@hacknow.com

OPA emits HTTP spans named after the Prometheus handler label (e.g.
"v1/data") on the server side and "HTTP {METHOD}" on the client side.
The OpenTelemetry HTTP semantic conventions specify span names of the
form "{method} {route}" / "{method} {target}", which makes traces easier
to read in tools like Jaeger.

Install default span-name formatters on both the otelhttp Handler and
Transport produced by features/tracing:

  - server: "{METHOD} {operation}" where operation is the route label
    OPA already supplies to NewHandler (e.g. "POST v1/data", "GET health").
  - client: "{METHOD} {url.path}" with a "{METHOD}" fallback when no
    path is available, replacing otelhttp's legacy "HTTP {METHOD}".

The defaults are prepended to the converted otelhttp options so that any
user-supplied WithSpanNameFormatter passed via tracing.Options still
takes precedence (otelhttp applies options in order, last write wins).

Existing assertions in the distributed-tracing e2e test are updated to
match the new names. New unit tests in v1/features/tracing cover the
formatter logic directly and end-to-end through otelhttp, including the
user-override case.

Closes open-policy-agent#8612

Signed-off-by: ChrisJr404 <chris@hacknow.com>
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.

OPA HTTP span names omit HTTP method

1 participant