Repository navigation
Clean up span operations #22446
Copy link
Copy link
Closed
Description
Description
Currently, many of our instrumentations send span ops that are not defined in conventions. This has two reasons:
- The span op is simply still missing in conventions and needs to be added
- SDKs sent suboptimal, maybe, too fragmented ops. For example, ops that include the framework or library name (e.g.
middleware.expressorfunction.sveltekit). Ops should be a bit more general than that.
I (okay, mostly my clanker, followed by a manual review) audited our instrumentations and these are the places we should make changes to:
Add to conventions
| Effort | Priority | Category | Current op(s) | Replacement or conventions action | Registry status | Rationale / other findings | Preserve detail in | Production locations |
|---|---|---|---|---|---|---|---|---|
| XS | High | Routing | router |
Add generic router to conventions |
Missing | This is the common framework-neutral replacement target for frontend and backend router spans. Router work is distinct from both navigation roots and generic middleware. | ui.framework; navigation.phase; code.namespace or framework; router.name; route attributes |
packages/angular/src/tracing.ts packages/sveltekit/src/client/svelte4BrowserTracing.ts packages/sveltekit/src/client/svelte5BrowserTracing.ts packages/ember/addon/utils/instrumentEmberAppInstanceForPerformance.ts packages/core/src/integrations/express/patch-layer.ts packages/server-utils/src/integrations/tracing-channel/express/instrumentation.ts packages/node/src/integrations/tracing/koa/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/koa.ts packages/node/src/integrations/tracing/hapi/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/hapi-utils.ts |
| XS | High | UI | ui.interaction.click|hover|drag|press |
Keep ui.interaction.* and add all four leaves to conventions |
Missing | Event Timing and INP interaction spans remain distinct from ui.action roots. Register click, hover, drag, and press as the finite interaction taxonomy. |
Keep DOM event type and INP metric identity as attributes | packages/browser-utils/src/metrics/browserMetrics.ts packages/browser-utils/src/metrics/inp.ts packages/browser-utils/src/metrics/webVitalSpans.ts packages/browser/src/tracing/browserTracingIntegration.ts |
| XS | High | UI | ui.long-task / ui.long-animation-frame |
Keep both operations and add them to conventions | Missing; ui.task is not equivalent |
Long Task and Long Animation Frame are distinct browser performance APIs. The generic ui.task operation is not a semantic replacement. |
browser.performance.entry_type=long-task|long-animation-frame |
packages/browser-utils/src/metrics/browserMetrics.ts |
| XS | Medium | Browser | browser.${timingPhase} |
Keep the browser timing-phase ops and add them to conventions | Only browser is currently registered |
Current finite values include browser.unloadEvent, redirect, domContentLoadedEvent, loadEvent, connect, TLS/SSL, cache, DNS, request, and response. These phases remain useful as distinct operations. |
Keep browser.performance.phase as complementary detail |
packages/browser-utils/src/metrics/browserMetrics.ts |
| XS | Medium | Browser | measure |
Keep measure and add it to conventions |
Missing | PerformanceMeasure is a distinct, fixed, useful category. |
Keep the PerformanceEntry name in the span name |
packages/browser-utils/src/metrics/browserMetrics.ts |
| XS | Medium | Browser | resource.${initiatorType} |
Keep initiator-type leaves, add them to conventions, and retain resource.other as fallback |
Partially registered | Register the browser-standard initiator types, including currently undocumented values such as worker, icon, frame, object, ping, or track. Values outside the recognized browser set must still map to resource.other. |
resource.initiator_type |
packages/browser-utils/src/metrics/browserMetrics.ts |
| XS | Medium | HTTP | navigation.redirect |
Keep navigation.redirect and add it to conventions |
Missing | Redirect timing is a stable navigation category worth retaining as its own operation. | navigation.type=redirect |
packages/browser/src/tracing/browserTracingIntegration.ts |
| XS | Medium | Storage | object.get|head|put|delete|list|upload_part|multipart_upload.abort|create|complete |
Keep object.* and add an object-storage category to conventions |
Missing | Unlike framework suffixes, these are a finite, provider-neutral API taxonomy for a domain not represented by current conventions. If a smaller taxonomy is preferred, normalize to object.read/write/delete before registration. |
cloud provider and bucket/object identity remain attributes | packages/cloudflare/src/instrumentations/worker/instrumentR2.ts |
| XS | Medium | UI | ui.mount / ui.update / ui.unmount |
Add ui.mount, ui.update, and ui.unmount to conventions; ui.render already exists |
Generic lifecycle leaves are incomplete | These are framework-neutral replacement targets for equivalent React, Vue, and Svelte lifecycle work. | ui.framework; ui.component_name; ui.lifecycle_phase |
packages/react/src/profiler.tsx packages/vue/src/tracing.ts packages/svelte/src/performance.ts |
| XS | Medium | UI | ui.webvital.lcp / ui.webvital.cls |
Keep and add both to conventions | Missing | These are fixed, cross-framework browser concepts. They are better convention candidates than framework-specific lifecycle leaves. | web_vital.name and measurement value remain authoritative |
packages/browser-utils/src/metrics/lcp.ts packages/browser-utils/src/metrics/cls.ts packages/browser-utils/src/metrics/webVitalSpans.ts |
| XL | High | Gen AI | gen_ai.embeddings / gen_ai.generate_content / gen_ai.rerank |
embeddings / generate_content / rerank |
Missing | These are fixed, provider-neutral, semantically distinct model operations. Add their unprefixed forms to conventions, then migrate SDK emission consistently. | gen_ai.operation.name and gen_ai.operation.type |
packages/core/src/tracing/langchain/embeddings.ts packages/core/src/tracing/openai/index.ts packages/core/src/tracing/google-genai/index.ts packages/core/src/tracing/workers-ai/index.ts packages/core/src/tracing/vercel-ai/index.ts packages/server-utils/src/integrations/tracing-channel/openai.ts packages/server-utils/src/integrations/tracing-channel/google-genai.ts packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts |
Replace with another op
Note: The additive "gap" rows — adding a
sentry.opto spans that currently emit none, which is not breaking — were split out into #23138 (rate-limit, DO WebSocket, DataLoaderprime/clear/clearAll, GraphQL, generic-pool, Effect). The row numbering below therefore skips 21–23.
| Row # | Effort | Priority | Category | Current op(s) | Replacement or conventions action | Registry status | Rationale / other findings | Preserve detail in | Production locations |
|---|---|---|---|---|---|---|---|---|---|
| 1 | S | High | FaaS | http.request (Firebase Functions) |
function.gcp |
http.request missing |
The wrapper instruments all Firebase function trigger types, not only HTTP. A fixed http.request op is semantically wrong. |
faas.trigger already exists; keep faas.provider=firebase |
packages/node/src/integrations/tracing/firebase/otel/patches/functions.ts |
| 2 | S | High | HTTP | http.client.${serviceIdentifier} |
http.client |
Dynamic suffix missing | Service identity belongs in attributes; endpoint-derived suffixes fragment grouping and can be high-cardinality. | rpc.service or server.address / cloud.service.name |
packages/google-cloud-serverless/src/integrations/google-cloud-http.ts |
| 3 | S | High | HTTP | http.client.prefetch / http.server.prefetch |
http.client / http.server |
Missing; base ops exist | OTel inference fabricates unregistered suffixes. Preserve direction and move prefetch to data. | sentry.http.prefetch=true |
packages/opentelemetry/src/utils/parseSpanDescription.ts |
| 4 | S | High | Messaging | faas.queue plus sentry.op=queue.process on one span |
queue.process only |
queue.process exact; faas.queue missing |
Cloudflare passes conflicting values. The sentry.op attribute wins, so faas.queue is effectively dead and should be removed. |
— | packages/cloudflare/src/instrumentations/worker/instrumentQueue.ts |
| 5 | S | High | RPC | grpc.${serviceIdentifier} |
grpc |
Dynamic suffix missing | Service identity can be high-cardinality and already has standard RPC attributes. | rpc.service; rpc.method |
packages/google-cloud-serverless/src/integrations/google-cloud-grpc.ts |
| 6 | S | High | UI | ui.angular.init / ui.angular.${methodKey} |
ui.mount for init; function for TraceMethod |
Parent ui.angular exists |
The method-key suffix is user-controlled and unbounded. Component initialization is a mount; arbitrary decorated methods are functions. | ui.framework=angular; code.function.name |
packages/angular/src/tracing.ts |
| 7 | S | High | UI | ui.ember.route.before_model|model|after_model|setup_controller |
function |
Missing | These are route-hook functions. The hook name is already available and should not be encoded in sentry.op. |
code.function.name; ember.route.hook |
packages/ember/addon/index.ts |
| 8 | S | High | UI | ui.ember.runloop.${queue} |
ui.task |
Missing; ui.task exists |
Queue values are phase details and Ember can be configured with custom queues. | ui.framework=ember; ember.runloop.queue |
packages/ember/addon/utils/instrumentEmberGlobals.ts |
| 9 | S | Medium | Browser | paint |
browser.paint |
browser.paint exists |
Rename the raw paint operation to the registered browser-specific operation. |
Keep the PerformanceEntry name in the span name |
packages/browser-utils/src/metrics/browserMetrics.ts |
| 10 | S | Medium | FaaS | function.aws.lambda |
function.aws |
function.aws exists |
Lambda is already implied by provider/runtime attributes; use the registered provider-level operation. | faas.name; cloud.provider=aws; cloud.platform=aws_lambda |
packages/aws-serverless/src/requestSpanOptions.ts |
| 11 | S | Medium | HTTP | http.client.stream |
http.client |
Missing; parent exists | Streaming changes response behavior but not the HTTP operation category. | http.response.body.streaming=true |
packages/browser/src/integrations/fetchStreamPerformance.ts |
| 12 | S | Medium | UI | ui.ember.component.render / component.definition / init |
ui.render / function / ui.mount |
Only parent ui.ember exists |
Map render and initialization to cross-framework lifecycle ops. Component definition lookup is generic function work. | ui.framework=ember; ui.component_name; code.function.name |
packages/ember/addon/utils/instrumentEmberGlobals.ts |
| 13 | M | High | Cache | cache.has_item|get_item|get_item_raw|get_items|get_keys|set_item|set_item_raw|set_items|remove_item|clear |
cache.get / cache.put / cache.remove |
Only target ops exist | This is the exact over-granularity the cache conventions avoid. Map has/get/list to get; set variants to put; remove/clear to remove. | db.operation.name already preserves the unstorage method |
packages/nuxt/src/runtime/utils/instrumentStorage.ts packages/nitro/src/runtime/hooks/captureStorageEvents.ts |
| 14 | M | High | Database | db.redis / db.redis.connect |
db.query for commands; db for connect |
Missing; db and db.query exist |
The datastore belongs in db.system.name. Library/system-specific op leaves are redundant. |
db.system.name=redis; db.operation.name |
packages/server-utils/src/redis/redis-dc-subscriber.ts packages/server-utils/src/integrations/tracing-channel/redis.ts |
| 15 | M | High | FaaS | faas.cron / faas.email / function.step.do |
function |
Missing | Cloudflare trigger/workflow phase belongs in attributes. Do not add a new op per trigger. | faas.trigger=timer|email; code.function.name; workflow.step.name |
packages/cloudflare/src/instrumentations/worker/instrumentScheduled.ts packages/cloudflare/src/instrumentations/worker/instrumentEmail.ts packages/cloudflare/src/workflows.ts |
| 16 | M | High | FaaS | faas.trigger.toString() inferred as op |
Infer function.<provider>, http.server, or queue.process from semantic evidence |
Arbitrary values | OTel inference currently promotes an attribute value directly into sentry.op. Custom trigger strings can create unbounded ops. |
Retain faas.trigger as data; never use it verbatim as op |
packages/opentelemetry/src/utils/parseSpanDescription.ts |
| 17 | M | High | Gen AI | rpc on AWS Bedrock inference spans |
chat or generate_content |
rpc exists but is semantically wrong here |
Bedrock adds GenAI attributes but inherits the generic AWS SDK rpc op. Select the GenAI operation from the Bedrock request type. |
— | packages/server-utils/src/integrations/tracing-channel/aws-sdk/index.ts packages/server-utils/src/integrations/tracing-channel/aws-sdk/services/bedrock-runtime.ts |
| 18 | M | High | Middleware | app_creation.nestjs / request_context.nestjs / event.nestjs / plugin.hapi |
function |
Missing | These represent executable functions or setup work. Keep lifecycle/event/plugin identity in attributes. | code.function.name; nestjs.type; hapi.type; event.name |
packages/nestjs/src/integrations/wrap-route.ts packages/nestjs/src/integrations/helpers.ts packages/node/src/integrations/tracing/hapi/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/hapi-utils.ts |
| 19 | M | High | RPC | rpc.server / mcp.server / mcp.notification.client_to_server|server_to_client |
rpc |
Only rpc exists |
MCP is JSON-RPC and tRPC is RPC. Direction, method, notification kind, and protocol belong in attributes rather than op suffixes. | rpc.system=mcp|trpc; rpc.method; jsonrpc.*; messaging direction |
packages/core/src/trpc.ts packages/core/src/integrations/mcp-server/spans.ts packages/core/src/integrations/mcp-server/attributes.ts |
| 20 | M | Medium | FaaS | function.gcp.http / event / cloud_event |
function.gcp |
function.gcp exists |
Trigger type belongs in faas.trigger. The existing suffixes split one provider category. |
faas.trigger |
packages/google-cloud-serverless/src/gcpfunction/http.ts packages/google-cloud-serverless/src/gcpfunction/events.ts packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts |
| 24 | L | High | Gen AI | gen_ai.create_agent / gen_ai.models / gen_ai.unknown / ai.run |
function for create/models/unknown fallback; map ai.run to a known AI op when evidence exists |
Missing | Agent compilation and model metadata calls are not inference operations. Unknown strings should not become conventions. Vercel AI's fallback should use an explicit mapping or a generic function op. |
gen_ai.operation.name; code.function.name; retain provider SDK method |
packages/core/src/tracing/langgraph/index.ts packages/core/src/tracing/anthropic-ai/index.ts packages/core/src/tracing/openai/index.ts packages/core/src/tracing/vercel-ai/index.ts |
| 25 | L | High | Messaging | message / message.produce / message.consume |
queue.publish / queue.receive / queue.process |
message* missing; queue.* exists |
Kafka, AMQP, and Deno use a parallel non-convention taxonomy. Use messaging.operation.type to distinguish publish, receive, and process. Deno must not infer all ops from SpanKind alone. |
messaging.system; messaging.operation.type |
packages/server-utils/src/integrations/tracing-channel/kafkajs/spans.ts packages/server-utils/src/integrations/tracing-channel/amqplib.ts packages/opentelemetry/src/utils/parseSpanDescription.ts packages/deno/src/opentelemetry/tracer.ts |
| 26 | L | High | Middleware | request_handler.<library> / handler.nestjs / hono.request |
function; use http.server only for a true inbound/internal HTTP boundary |
Missing | Terminal framework handlers are functions, not new op categories. Hono app.request() is an in-process HTTP dispatch and should use http.server with HTTP attributes. |
code.function.name; framework handler type; HTTP attributes for Hono dispatch |
packages/core/src/integrations/express/patch-layer.ts packages/server-utils/src/integrations/tracing-channel/express/instrumentation.ts packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts packages/node/src/integrations/tracing/hono/index.ts packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/fastify/instrumentation.ts packages/elysia/src/withElysia.ts packages/nestjs/src/integrations/wrap-route.ts packages/hono/src/shared/patchAppRequest.ts |
| 27 | L | High | Middleware | router.express / router.koa / router.hapi |
router (conventions addition tracked in the first table) |
router is currently missing |
Backend router layers should share one framework-neutral operation. They are semantically distinct from generic middleware. | code.namespace or framework; router.name; route attributes |
packages/core/src/integrations/express/patch-layer.ts packages/server-utils/src/integrations/tracing-channel/express/instrumentation.ts packages/node/src/integrations/tracing/koa/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/koa.ts packages/node/src/integrations/tracing/hapi/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/hapi-utils.ts |
| 28 | L | Medium | UI | ui.angular.routing / ui.sveltekit.routing / ui.ember.transition |
router (conventions addition tracked in the first table) |
router must be added to conventions |
All three measure router execution. Collapse framework identity while keeping router work distinct from the broader navigation root. |
ui.framework; navigation.phase |
packages/angular/src/tracing.ts packages/sveltekit/src/client/svelte4BrowserTracing.ts packages/sveltekit/src/client/svelte5BrowserTracing.ts packages/ember/addon/utils/instrumentEmberAppInstanceForPerformance.ts |
| 29 | L | Medium | UI | ui.react.* / ui.vue.* / ui.svelte.* lifecycle ops |
ui.mount / ui.render / ui.update / ui.unmount (missing generic leaves tracked in the first table) |
React leaves exist; generic leaves are incomplete | Preferred cleanup: normalize equivalent lifecycle work across frameworks. Current values include React mount/render/update; Vue render/activate/create/unmount/destroy/mount/update; Svelte init/update. Keeping React-specific leaves while collapsing other frameworks would preserve an arbitrary asymmetry. | ui.framework; ui.component_name; ui.lifecycle_phase |
packages/react/src/profiler.tsx packages/vue/src/tracing.ts packages/svelte/src/performance.ts |
| 30 | XL | High | Framework functions | function.<framework>(.<phase>) and loader.remix / action.remix |
function |
function exists; function.nextjs/remix are legacy leaves |
Collapse all framework and phase suffixes. This also resolves Remix's three competing loader/action taxonomies. Includes Next.js, Remix, React Router client/server, SvelteKit client/server, SolidStart, TanStack Start, and Cloudflare workflow steps. | code.function.name; code.namespace; framework-specific phase/type attributes |
packages/nextjs/src/common/utils/tracingUtils.ts packages/nextjs/src/common/withServerActionInstrumentation.ts packages/remix/src/server/instrumentServer.ts packages/remix/src/server/integrations/tracing-channel.ts packages/remix/src/server/integrations/opentelemetry.ts packages/react-router/src/server/createServerInstrumentation.ts packages/react-router/src/server/wrapServerLoader.ts packages/react-router/src/server/wrapServerAction.ts packages/react-router/src/server/instrumentation/reactRouter.ts packages/react-router/src/server/instrumentation/util.ts packages/react-router/src/client/createClientInstrumentation.ts packages/react-router/src/client/hydratedRouter.ts packages/sveltekit/src/client/load.ts packages/sveltekit/src/server-common/load.ts packages/sveltekit/src/server-common/serverRoute.ts packages/sveltekit/src/server-common/integrations/svelteKitSpans.ts packages/solidstart/src/server/withServerActionInstrumentation.ts packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts packages/cloudflare/src/workflows.ts |
| 31 | XL | High | Gen AI | gen_ai.chat / gen_ai.execute_tool / gen_ai.invoke_agent |
chat / execute_tool / invoke_agent |
Registry values are unprefixed | The SDK and source-of-truth registry disagree. Migrate to the exact convention values, or explicitly change the registry—but do not leave both. Before migration, replace SDK internals which identify AI spans via op.startsWith('gen_ai.') with gen_ai.operation.type/name. |
gen_ai.operation.name and gen_ai.operation.type |
packages/core/src/tracing/langchain/index.ts packages/core/src/tracing/langgraph/index.ts packages/core/src/tracing/langgraph/utils.ts packages/core/src/tracing/openai/index.ts packages/core/src/tracing/anthropic-ai/index.ts packages/core/src/tracing/google-genai/index.ts packages/core/src/tracing/workers-ai/index.ts packages/core/src/tracing/vercel-ai/index.ts packages/server-utils/src/integrations/tracing-channel/openai.ts packages/server-utils/src/integrations/tracing-channel/anthropic.ts packages/server-utils/src/integrations/tracing-channel/google-genai.ts packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts |
| 32 | XL | High | Middleware | middleware.<library> / hook.fastify / http.server.middleware |
middleware |
middleware exists |
Collapse framework identity for middleware and request-pipeline hooks. Router layers are handled separately as router. |
code.namespace or framework; middleware.type; middleware.name |
packages/core/src/integrations/express/patch-layer.ts packages/server-utils/src/integrations/tracing-channel/express/instrumentation.ts packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts packages/node/src/integrations/tracing/koa/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/koa.ts packages/node/src/integrations/tracing/hapi/vendored/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/hapi-utils.ts packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts packages/server-utils/src/integrations/tracing-channel/fastify/instrumentation.ts packages/node/src/integrations/tracing/hono/index.ts packages/hono/src/shared/wrapMiddlewareSpan.ts packages/elysia/src/withElysia.ts packages/nestjs/src/integrations/helpers.ts packages/nuxt/src/runtime/hooks/wrapMiddlewareHandler.ts packages/nitro/src/runtime/hooks/captureTracingEvents.ts packages/tanstackstart-react/src/server/utils.ts packages/nextjs/src/common/wrapMiddlewareWithSentry.ts packages/nextjs/src/common/enhanceMiddlewareRootSpan.ts packages/nextjs/src/server/enhanceHandleRequestRootSpan.ts packages/nextjs/src/edge/index.ts |
Reactions are currently unavailable
Activity
linear-code commentedon Jul 21, 2026
JS-3105
feat(google-cloud-serverless)!: Use `grpc` span op for GCP gRPC calls (…
fix(cloudflare): Remove conflicting `faas.queue` op on queue consumer…
feat(angular)!: Use `ui.mount` and `function` span ops for tracing de…
feat(google-cloud-serverless)!: Use `http.client` span op for GCP HTT…
feat(aws-serverless)!: Use `function.aws` span op for Lambda invocati…
feat(cloudflare)!: Use `function` span op for cron, email and workflo…
feat(node)!: Use `db.query` and `db` span ops for Redis (#22690)
feat(nuxt, nitro)!: Use convention cache ops for storage spans (#22689)
feat!: Use `middleware` span op for web-server middleware (#22852)
7 remaining items
feat(angular,ember,sveltekit)!: Use `router` span op for frontend rou…
feat!: Use `handler` span op for terminal request handlers (#22871)
feat(nestjs)!: Use `function` span op for setup & lifecycle handlers (#…
feat(server-utils)!: Clean up gen ai ops
feat(server-utils,core)!: Use `router` span op for backend router lay…
feat(opentelemetry)!: Drop `.prefetch` suffix from inferred HTTP span…
feat(server-utils)!: Clean up gen ai ops
feat(server-utils)!: Clean up gen ai ops (#23076)
feat(opentelemetry)!: Drop `.prefetch` suffix from inferred HTTP span…
feat(frameworks)!: Use `function` op for framework functions (#23047)
chore: Update migration guide with span op breaking changes (#23137)
github-actions commentedon Sep 23, 2026
A PR closing this issue has just been released 🚀
This issue was referenced by PR #23137, which was included in the 11.0.0 release.