Skip to content

feat(otel): add getRoute callback so adapters can resolve http.route#1915

Open
spokodev wants to merge 1 commit into
honojs:mainfrom
spokodev:fix/otel-preserve-downstream-http-route
Open

feat(otel): add getRoute callback so adapters can resolve http.route#1915
spokodev wants to merge 1 commit into
honojs:mainfrom
spokodev:fix/otel-preserve-downstream-http-route

Conversation

@spokodev

Copy link
Copy Markdown

Closes #1914.

@hono/otel overwrote the http.route attribute on the active span at finalize time with the matched Hono pattern (e.g. /rpc/*), even when a downstream adapter resolved a more specific operation name (e.g. /rpc/user/getProfile) during request handling. The same Hono pattern was reported as the http.route attribute of the http.server.request.duration metric, so per-operation latency collapsed into the pattern bucket.

This PR adds a getRoute(c) config callback. When it returns a non-empty string the value is used at finalize for both the span attribute and the metric attribute. When it returns undefined, an empty string, or throws, the middleware falls back to routePath(c), so the existing behavior is preserved when the callback is not provided.

Typical adapter usage:

app.use(
  httpInstrumentationMiddleware({
    tracerProvider,
    getRoute: (c) => c.get('rpc.route'),
  })
)

Verification:

  • 5 new tests in packages/otel/src/index.test.ts:
    • span http.route reflects the getRoute return value
    • metric http.route reflects the getRoute return value
    • getRoute returning undefined falls back to routePath(c)
    • getRoute returning an empty string falls back to routePath(c)
    • getRoute throwing falls back to routePath(c)
  • RED baseline confirmed: reverting index.ts + types.ts to main makes the two positive tests fail; applying the fix turns them green
  • yarn test --run for @hono/otel: 27 / 27 (22 existing + 5 new)
  • yarn typecheck clean
  • yarn turbo run lint --filter=@hono/otel clean
  • yarn format reports All matched files use Prettier code style!
  • Changeset added at .changeset/otel-get-route-callback.md (@hono/otel: minor)

Out of scope:

  • The initial attributes passed to tracer.startActiveSpan still use routePath(c), because getRoute may rely on context state that downstream middleware writes after the span is started. The finalize step updates the attribute to the resolved value so the exported span and the metric agree.

The middleware overwrote the http.route attribute at finalize time
with the Hono route pattern, even when a downstream adapter (such as
an RPC layer) had already set a more specific value on the active
span. The same pattern was also written to the http.server.request.duration
metric, so per-operation latency collapsed into the pattern bucket
(see issue honojs#1914).

Add an optional getRoute(c) config callback. When it returns a non-empty
string the value is used for both the span attribute and the metric
attribute at finalize. When it returns undefined, an empty string, or
throws, the middleware falls back to routePath(c). This is fully
backward compatible.

Closes honojs#1914
@changeset-bot

changeset-bot Bot commented May 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc351b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hono/otel Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@spokodev spokodev marked this pull request as ready for review June 18, 2026 16:37
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.

@hono/otel overwrites http.route set by downstream middleware

1 participant