@danieljvdm/dev-kit 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/package.json +1 -1
- package/skill-sources.jsonc +6 -29
- package/skill-sources.lock.json +23 -109
- package/skills/animate/LICENSE +21 -0
- package/skills/animate/NOTICE +10 -0
- package/skills/animate/SKILL.md +34 -0
- package/skills/animate/references/gestures.md +27 -0
- package/skills/animate/references/glossary.md +136 -0
- package/skills/animate/references/implementation.md +22 -0
- package/skills/animate/references/opportunities.md +18 -0
- package/skills/animate/references/principles.md +48 -0
- package/skills/animate/references/review.md +19 -0
- package/skills/cloudflare-workers/LICENSE +202 -0
- package/skills/cloudflare-workers/NOTICE +9 -0
- package/skills/cloudflare-workers/SKILL.md +26 -0
- package/skills/cloudflare-workers/references/durable-objects.md +68 -0
- package/skills/cloudflare-workers/references/sandbox.md +48 -0
- package/skills/cloudflare-workers/references/workers.md +63 -0
- package/skills/cloudflare-workers/references/wrangler.md +56 -0
- package/skills/design-ui/LICENSE +21 -0
- package/skills/design-ui/NOTICE +8 -0
- package/skills/design-ui/SKILL.md +29 -0
- package/skills/design-ui/references/appearance.md +22 -0
- package/skills/design-ui/references/components.md +33 -0
- package/skills/design-ui/references/exploration.md +50 -0
- package/skills/design-ui/references/libraries.md +39 -0
- package/skills/design-ui/references/themes.md +23 -0
- package/skills/dev-kit/SKILL.md +19 -38
- package/skills/dev-kit/references/cloudflare-worker-api.md +1 -1
- package/skills/dev-kit/references/skills.md +2 -2
- package/skills/effect-development/SKILL.md +27 -0
- package/skills/effect-development/agents/openai.yaml +4 -0
- package/skills/effect-development/references/api/index.md +32 -0
- package/skills/effect-development/references/api/schema-codecs.md +15 -0
- package/skills/{build-effect-apis/references → effect-development/references/api}/verification.md +10 -5
- package/skills/{effect-architecture-audit/SKILL.md → effect-development/references/architecture/index.md} +1 -6
- package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-client.md +8 -6
- package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-testing.md +7 -2
- package/skills/effect-development/references/atom/index.md +40 -0
- package/skills/{build-effect-clis/references → effect-development/references/cli}/entrypoints-and-testing.md +6 -2
- package/skills/effect-development/references/cli/index.md +39 -0
- package/skills/effect-development/references/setup.md +15 -0
- package/skills/open-pull-request/SKILL.md +11 -115
- package/skills/open-pull-request/agents/openai.yaml +3 -3
- package/skills/open-pull-request/references/evidence.md +22 -0
- package/skills/open-pull-request/references/explanation.md +21 -0
- package/skills/open-pull-request/references/publication.md +21 -0
- package/skills/sentry/LICENSE +202 -0
- package/skills/sentry/LICENSE-UPSTREAM +21 -0
- package/skills/sentry/NOTICE +14 -0
- package/skills/sentry/SKILL.md +33 -0
- package/skills/sentry/references/alert-payloads.md +105 -0
- package/skills/sentry/references/alerts.md +17 -0
- package/skills/sentry/references/artifacts.md +25 -0
- package/skills/sentry/references/auth-token.md +65 -0
- package/skills/sentry/references/concepts/ai-monitoring.md +131 -0
- package/skills/sentry/references/concepts/choosing-a-signal.md +78 -0
- package/skills/sentry/references/concepts/crons.md +50 -0
- package/skills/sentry/references/concepts/data-scrubbing.md +43 -0
- package/skills/sentry/references/concepts/errors.md +40 -0
- package/skills/sentry/references/concepts/logging.md +85 -0
- package/skills/sentry/references/concepts/metrics.md +49 -0
- package/skills/sentry/references/concepts/monitors.md +78 -0
- package/skills/sentry/references/concepts/profiling.md +38 -0
- package/skills/sentry/references/concepts/reduce-volume.md +49 -0
- package/skills/sentry/references/concepts/releases.md +43 -0
- package/skills/sentry/references/concepts/session-replay.md +35 -0
- package/skills/sentry/references/concepts/tracing.md +54 -0
- package/skills/sentry/references/concepts/user-feedback.md +36 -0
- package/skills/sentry/references/debug-artifacts/android.md +68 -0
- package/skills/sentry/references/debug-artifacts/apple.md +96 -0
- package/skills/sentry/references/debug-artifacts/dotnet.md +70 -0
- package/skills/sentry/references/debug-artifacts/flutter.md +65 -0
- package/skills/sentry/references/debug-artifacts/index.md +75 -0
- package/skills/sentry/references/debug-artifacts/javascript.md +94 -0
- package/skills/sentry/references/debug-artifacts/matching.md +73 -0
- package/skills/sentry/references/debug-artifacts/react-native.md +75 -0
- package/skills/sentry/references/first-error-setup.md +25 -0
- package/skills/sentry/references/instrumentation.md +30 -0
- package/skills/sentry/references/issues.md +32 -0
- package/skills/sentry/references/new-project.md +17 -0
- package/skills/sentry/references/otel-exporter.md +30 -0
- package/skills/sentry/references/release-workflow.md +31 -0
- package/skills/sentry/references/releases/ci-pipeline.md +214 -0
- package/skills/sentry/references/releases/index.md +93 -0
- package/skills/sentry/references/releases/suspect-commits.md +155 -0
- package/skills/sentry/references/releases/tagging.md +131 -0
- package/skills/sentry/references/releases/troubleshooting.md +67 -0
- package/skills/sentry/references/sdks/android/crons.md +305 -0
- package/skills/sentry/references/sdks/android/error-monitoring.md +800 -0
- package/skills/sentry/references/sdks/android/index.md +748 -0
- package/skills/sentry/references/sdks/android/integrations.md +48 -0
- package/skills/sentry/references/sdks/android/logging.md +374 -0
- package/skills/sentry/references/sdks/android/metrics.md +288 -0
- package/skills/sentry/references/sdks/android/profiling.md +397 -0
- package/skills/sentry/references/sdks/android/session-replay.md +406 -0
- package/skills/sentry/references/sdks/android/tracing.md +861 -0
- package/skills/sentry/references/sdks/browser/error-monitoring.md +861 -0
- package/skills/sentry/references/sdks/browser/index.md +714 -0
- package/skills/sentry/references/sdks/browser/logging.md +315 -0
- package/skills/sentry/references/sdks/browser/profiling.md +236 -0
- package/skills/sentry/references/sdks/browser/session-replay.md +586 -0
- package/skills/sentry/references/sdks/browser/tracing.md +561 -0
- package/skills/sentry/references/sdks/browser/user-feedback.md +354 -0
- package/skills/sentry/references/sdks/cloudflare/ai-monitoring.md +661 -0
- package/skills/sentry/references/sdks/cloudflare/crons.md +231 -0
- package/skills/sentry/references/sdks/cloudflare/durable-objects.md +381 -0
- package/skills/sentry/references/sdks/cloudflare/error-monitoring.md +329 -0
- package/skills/sentry/references/sdks/cloudflare/index.md +698 -0
- package/skills/sentry/references/sdks/cloudflare/logging.md +169 -0
- package/skills/sentry/references/sdks/cloudflare/nodejs-compat.md +134 -0
- package/skills/sentry/references/sdks/cloudflare/tracing.md +475 -0
- package/skills/sentry/references/sdks/cocoa/error-monitoring.md +394 -0
- package/skills/sentry/references/sdks/cocoa/index.md +497 -0
- package/skills/sentry/references/sdks/cocoa/logging.md +209 -0
- package/skills/sentry/references/sdks/cocoa/metrics.md +136 -0
- package/skills/sentry/references/sdks/cocoa/profiling.md +223 -0
- package/skills/sentry/references/sdks/cocoa/session-replay.md +257 -0
- package/skills/sentry/references/sdks/cocoa/tracing.md +524 -0
- package/skills/sentry/references/sdks/cocoa/user-feedback.md +310 -0
- package/skills/sentry/references/sdks/dotnet/crons.md +465 -0
- package/skills/sentry/references/sdks/dotnet/error-monitoring.md +1100 -0
- package/skills/sentry/references/sdks/dotnet/index.md +614 -0
- package/skills/sentry/references/sdks/dotnet/logging.md +556 -0
- package/skills/sentry/references/sdks/dotnet/metrics.md +229 -0
- package/skills/sentry/references/sdks/dotnet/profiling.md +296 -0
- package/skills/sentry/references/sdks/dotnet/tracing.md +897 -0
- package/skills/sentry/references/sdks/elixir/crons.md +371 -0
- package/skills/sentry/references/sdks/elixir/error-monitoring.md +335 -0
- package/skills/sentry/references/sdks/elixir/index.md +383 -0
- package/skills/sentry/references/sdks/elixir/logging.md +239 -0
- package/skills/sentry/references/sdks/elixir/tracing.md +233 -0
- package/skills/sentry/references/sdks/flutter/ecosystem-integrations.md +70 -0
- package/skills/sentry/references/sdks/flutter/error-monitoring.md +312 -0
- package/skills/sentry/references/sdks/flutter/index.md +728 -0
- package/skills/sentry/references/sdks/flutter/logging.md +387 -0
- package/skills/sentry/references/sdks/flutter/metrics.md +310 -0
- package/skills/sentry/references/sdks/flutter/profiling.md +214 -0
- package/skills/sentry/references/sdks/flutter/session-replay.md +308 -0
- package/skills/sentry/references/sdks/flutter/tracing.md +502 -0
- package/skills/sentry/references/sdks/go/crons.md +270 -0
- package/skills/sentry/references/sdks/go/error-monitoring.md +361 -0
- package/skills/sentry/references/sdks/go/index.md +324 -0
- package/skills/sentry/references/sdks/go/logging.md +354 -0
- package/skills/sentry/references/sdks/go/metrics.md +273 -0
- package/skills/sentry/references/sdks/go/profiling.md +46 -0
- package/skills/sentry/references/sdks/go/tracing.md +366 -0
- package/skills/sentry/references/sdks/index.md +41 -0
- package/skills/sentry/references/sdks/nestjs/ai-monitoring.md +410 -0
- package/skills/sentry/references/sdks/nestjs/crons.md +268 -0
- package/skills/sentry/references/sdks/nestjs/error-monitoring.md +729 -0
- package/skills/sentry/references/sdks/nestjs/index.md +699 -0
- package/skills/sentry/references/sdks/nestjs/logging.md +174 -0
- package/skills/sentry/references/sdks/nestjs/metrics.md +243 -0
- package/skills/sentry/references/sdks/nestjs/profiling.md +127 -0
- package/skills/sentry/references/sdks/nestjs/tracing.md +762 -0
- package/skills/sentry/references/sdks/nextjs/ai-monitoring.md +494 -0
- package/skills/sentry/references/sdks/nextjs/crons.md +383 -0
- package/skills/sentry/references/sdks/nextjs/error-monitoring.md +974 -0
- package/skills/sentry/references/sdks/nextjs/index.md +491 -0
- package/skills/sentry/references/sdks/nextjs/logging.md +381 -0
- package/skills/sentry/references/sdks/nextjs/metrics.md +411 -0
- package/skills/sentry/references/sdks/nextjs/profiling.md +403 -0
- package/skills/sentry/references/sdks/nextjs/session-replay.md +462 -0
- package/skills/sentry/references/sdks/nextjs/tracing.md +628 -0
- package/skills/sentry/references/sdks/node/ai-monitoring.md +303 -0
- package/skills/sentry/references/sdks/node/crons.md +432 -0
- package/skills/sentry/references/sdks/node/error-monitoring.md +1125 -0
- package/skills/sentry/references/sdks/node/index.md +942 -0
- package/skills/sentry/references/sdks/node/logging.md +380 -0
- package/skills/sentry/references/sdks/node/metrics.md +278 -0
- package/skills/sentry/references/sdks/node/profiling.md +373 -0
- package/skills/sentry/references/sdks/node/tracing.md +878 -0
- package/skills/sentry/references/sdks/php/ai-monitoring.md +310 -0
- package/skills/sentry/references/sdks/php/crons.md +265 -0
- package/skills/sentry/references/sdks/php/error-monitoring.md +626 -0
- package/skills/sentry/references/sdks/php/index.md +362 -0
- package/skills/sentry/references/sdks/php/laravel.md +542 -0
- package/skills/sentry/references/sdks/php/logging.md +242 -0
- package/skills/sentry/references/sdks/php/metrics.md +193 -0
- package/skills/sentry/references/sdks/php/profiling.md +115 -0
- package/skills/sentry/references/sdks/php/symfony.md +552 -0
- package/skills/sentry/references/sdks/php/tracing.md +516 -0
- package/skills/sentry/references/sdks/python/ai-monitoring.md +351 -0
- package/skills/sentry/references/sdks/python/crons.md +218 -0
- package/skills/sentry/references/sdks/python/error-monitoring.md +292 -0
- package/skills/sentry/references/sdks/python/index.md +361 -0
- package/skills/sentry/references/sdks/python/logging.md +234 -0
- package/skills/sentry/references/sdks/python/metrics.md +154 -0
- package/skills/sentry/references/sdks/python/profiling.md +120 -0
- package/skills/sentry/references/sdks/python/tracing.md +364 -0
- package/skills/sentry/references/sdks/react/error-monitoring.md +1607 -0
- package/skills/sentry/references/sdks/react/index.md +492 -0
- package/skills/sentry/references/sdks/react/logging.md +364 -0
- package/skills/sentry/references/sdks/react/profiling.md +322 -0
- package/skills/sentry/references/sdks/react/react-features.md +1272 -0
- package/skills/sentry/references/sdks/react/session-replay.md +1193 -0
- package/skills/sentry/references/sdks/react/tracing.md +1415 -0
- package/skills/sentry/references/sdks/react-native/error-monitoring.md +1481 -0
- package/skills/sentry/references/sdks/react-native/expo-config-plugin.md +45 -0
- package/skills/sentry/references/sdks/react-native/index.md +1030 -0
- package/skills/sentry/references/sdks/react-native/logging.md +444 -0
- package/skills/sentry/references/sdks/react-native/profiling.md +386 -0
- package/skills/sentry/references/sdks/react-native/session-replay.md +704 -0
- package/skills/sentry/references/sdks/react-native/tracing.md +1086 -0
- package/skills/sentry/references/sdks/react-native/user-feedback.md +815 -0
- package/skills/sentry/references/sdks/react-router-framework/error-monitoring.md +127 -0
- package/skills/sentry/references/sdks/react-router-framework/index.md +411 -0
- package/skills/sentry/references/sdks/react-router-framework/logging.md +66 -0
- package/skills/sentry/references/sdks/react-router-framework/profiling.md +57 -0
- package/skills/sentry/references/sdks/react-router-framework/react-router-framework-features.md +110 -0
- package/skills/sentry/references/sdks/react-router-framework/session-replay.md +67 -0
- package/skills/sentry/references/sdks/react-router-framework/tracing.md +96 -0
- package/skills/sentry/references/sdks/react-router-framework/user-feedback.md +71 -0
- package/skills/sentry/references/sdks/ruby/crons.md +184 -0
- package/skills/sentry/references/sdks/ruby/error-monitoring.md +248 -0
- package/skills/sentry/references/sdks/ruby/index.md +297 -0
- package/skills/sentry/references/sdks/ruby/logging.md +168 -0
- package/skills/sentry/references/sdks/ruby/metrics.md +196 -0
- package/skills/sentry/references/sdks/ruby/migration.md +321 -0
- package/skills/sentry/references/sdks/ruby/profiling.md +107 -0
- package/skills/sentry/references/sdks/ruby/tracing.md +388 -0
- package/skills/sentry/references/sdks/svelte/error-monitoring.md +456 -0
- package/skills/sentry/references/sdks/svelte/index.md +467 -0
- package/skills/sentry/references/sdks/svelte/logging.md +344 -0
- package/skills/sentry/references/sdks/svelte/session-replay.md +369 -0
- package/skills/sentry/references/sdks/svelte/tracing.md +406 -0
- package/skills/sentry/references/sdks/tanstack-start/error-monitoring.md +149 -0
- package/skills/sentry/references/sdks/tanstack-start/index.md +381 -0
- package/skills/sentry/references/sdks/tanstack-start/logging.md +74 -0
- package/skills/sentry/references/sdks/tanstack-start/session-replay.md +76 -0
- package/skills/sentry/references/sdks/tanstack-start/tanstackstart-features.md +128 -0
- package/skills/sentry/references/sdks/tanstack-start/tracing.md +123 -0
- package/skills/sentry/references/sdks/tanstack-start/user-feedback.md +81 -0
- package/skills/sentry/references/search-query-language.md +226 -0
- package/skills/sentry/references/semantics/angular.md +7 -0
- package/skills/sentry/references/semantics/app.md +28 -0
- package/skills/sentry/references/semantics/art.md +17 -0
- package/skills/sentry/references/semantics/aws.md +43 -0
- package/skills/sentry/references/semantics/browser.md +34 -0
- package/skills/sentry/references/semantics/cache.md +12 -0
- package/skills/sentry/references/semantics/client.md +8 -0
- package/skills/sentry/references/semantics/cloud.md +12 -0
- package/skills/sentry/references/semantics/cloudflare.md +23 -0
- package/skills/sentry/references/semantics/code.md +11 -0
- package/skills/sentry/references/semantics/culture.md +11 -0
- package/skills/sentry/references/semantics/db.md +21 -0
- package/skills/sentry/references/semantics/device.md +43 -0
- package/skills/sentry/references/semantics/error.md +7 -0
- package/skills/sentry/references/semantics/event.md +8 -0
- package/skills/sentry/references/semantics/exception.md +10 -0
- package/skills/sentry/references/semantics/faas.md +19 -0
- package/skills/sentry/references/semantics/file.md +8 -0
- package/skills/sentry/references/semantics/flag.md +7 -0
- package/skills/sentry/references/semantics/gcp.md +16 -0
- package/skills/sentry/references/semantics/gen_ai.md +53 -0
- package/skills/sentry/references/semantics/general.md +11 -0
- package/skills/sentry/references/semantics/graphql.md +9 -0
- package/skills/sentry/references/semantics/grpc.md +19 -0
- package/skills/sentry/references/semantics/http.md +34 -0
- package/skills/sentry/references/semantics/index.md +63 -0
- package/skills/sentry/references/semantics/jsonrpc.md +8 -0
- package/skills/sentry/references/semantics/jvm.md +12 -0
- package/skills/sentry/references/semantics/koa.md +7 -0
- package/skills/sentry/references/semantics/logger.md +7 -0
- package/skills/sentry/references/semantics/mcp.md +37 -0
- package/skills/sentry/references/semantics/mdc.md +7 -0
- package/skills/sentry/references/semantics/messaging.md +23 -0
- package/skills/sentry/references/semantics/middleware.md +7 -0
- package/skills/sentry/references/semantics/navigation.md +9 -0
- package/skills/sentry/references/semantics/nel.md +11 -0
- package/skills/sentry/references/semantics/network.md +17 -0
- package/skills/sentry/references/semantics/os.md +15 -0
- package/skills/sentry/references/semantics/otel.md +10 -0
- package/skills/sentry/references/semantics/params.md +7 -0
- package/skills/sentry/references/semantics/process.md +14 -0
- package/skills/sentry/references/semantics/react.md +7 -0
- package/skills/sentry/references/semantics/remix.md +7 -0
- package/skills/sentry/references/semantics/resource.md +7 -0
- package/skills/sentry/references/semantics/rpc.md +10 -0
- package/skills/sentry/references/semantics/score.md +10 -0
- package/skills/sentry/references/semantics/sentry.md +69 -0
- package/skills/sentry/references/semantics/server.md +8 -0
- package/skills/sentry/references/semantics/service.md +8 -0
- package/skills/sentry/references/semantics/session.md +7 -0
- package/skills/sentry/references/semantics/state.md +7 -0
- package/skills/sentry/references/semantics/thread.md +8 -0
- package/skills/sentry/references/semantics/timber.md +7 -0
- package/skills/sentry/references/semantics/trpc.md +8 -0
- package/skills/sentry/references/semantics/ui.md +18 -0
- package/skills/sentry/references/semantics/url.md +15 -0
- package/skills/sentry/references/semantics/user.md +17 -0
- package/skills/sentry/references/semantics/user_agent.md +7 -0
- package/skills/sentry/references/semantics/vercel.md +42 -0
- package/skills/sentry/references/setup-verification.md +39 -0
- package/skills/sentry/references/snapshots/github-actions-fanout.md +206 -0
- package/skills/sentry/references/snapshots/github-actions-simple.md +102 -0
- package/skills/sentry/references/snapshots/github-actions-swift-snapshot-testing.md +118 -0
- package/skills/sentry/references/snapshots/index.md +19 -0
- package/skills/sentry/references/snapshots/snapshot-previews.md +100 -0
- package/skills/sentry/references/snapshots/snapshots.md +123 -0
- package/skills/sentry/references/snapshots/wizard-setup.md +134 -0
- package/skills/testing/LICENSE +21 -0
- package/skills/testing/NOTICE +7 -0
- package/skills/testing/SKILL.md +13 -19
- package/skills/testing/agents/openai.yaml +1 -2
- package/skills/testing/references/selection.md +23 -0
- package/skills/testing/references/tdd.md +19 -0
- package/skills/testing/references/test-design.md +17 -0
- package/src/catalog.ts +1 -7
- package/skills/build-effect-apis/SKILL.md +0 -81
- package/skills/build-effect-apis/agents/openai.yaml +0 -4
- package/skills/build-effect-clis/SKILL.md +0 -92
- package/skills/build-effect-clis/agents/openai.yaml +0 -4
- package/skills/effect-architecture-audit/agents/openai.yaml +0 -4
- package/skills/effect-atom-state/SKILL.md +0 -97
- package/skills/effect-atom-state/agents/openai.yaml +0 -4
- package/skills/effect-ts/SKILL.md +0 -35
- package/skills/effect-ts/agents/openai.yaml +0 -4
- /package/skills/{build-effect-apis/references → effect-development/references/api}/cloudflare-workers.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/runtime-assembly.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/server-and-middleware.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/shared-contracts.md +0 -0
- /package/skills/{effect-architecture-audit/references → effect-development/references/architecture}/service-and-boundary-audit.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-lifecycle.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-workflows.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/tanstack-start.md +0 -0
- /package/skills/{build-effect-clis/references → effect-development/references/cli}/command-design.md +0 -0
- /package/skills/{build-effect-clis/references → effect-development/references/cli}/processes-and-platform.md +0 -0
|
@@ -0,0 +1,1415 @@
|
|
|
1
|
+
# Tracing — Sentry React SDK
|
|
2
|
+
|
|
3
|
+
> Minimum SDK: `@sentry/react` ≥8.0.0+\
|
|
4
|
+
> `reactRouterV7BrowserTracingIntegration`: requires `@sentry/react` ≥8.0.0\
|
|
5
|
+
> `ignoreSpans`: requires `@sentry/react` ≥10.2.0\
|
|
6
|
+
> `enableAsyncRouteHandlers` + `lazyRouteManifest`: requires `@sentry/react` ≥10.39.0\
|
|
7
|
+
> `enableLongAnimationFrame`: requires `@sentry/react` ≥8.18.0
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## How Automatic Tracing Works
|
|
12
|
+
|
|
13
|
+
| What’s traced | Op | How |
|
|
14
|
+
| -------------------------------------- | ------------------------- | --------------------------------------------------------------- |
|
|
15
|
+
| Initial page load | `pageload` | `browserTracingIntegration()` reads `window.performance` timing |
|
|
16
|
+
| Client-side navigations | `navigation` | History API (pushState / replaceState) |
|
|
17
|
+
| `fetch()` requests | `http.client` | Patched automatically |
|
|
18
|
+
| `XMLHttpRequest` requests | `http.client` | Patched automatically |
|
|
19
|
+
| Long Tasks (main-thread blocks > 50ms) | `ui.long-task` | `PerformanceLongTaskTiming` observer |
|
|
20
|
+
| Long Animation Frames (≥8.18.0) | `ui.long-animation-frame` | `PerformanceLongAnimationFrameTiming` observer |
|
|
21
|
+
| INP interactions | `ui.interaction` | `PerformanceEventTiming` observer, emitted on page hide |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Core Setup
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// src/instrument.ts (imported FIRST in main.tsx / index.tsx)
|
|
29
|
+
import * as Sentry from "@sentry/react";
|
|
30
|
+
|
|
31
|
+
Sentry.init({
|
|
32
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
33
|
+
environment: import.meta.env.MODE,
|
|
34
|
+
|
|
35
|
+
integrations: [Sentry.browserTracingIntegration()],
|
|
36
|
+
|
|
37
|
+
// Tracing sample rates
|
|
38
|
+
tracesSampleRate: 1.0, // 100% in dev; lower to 0.1–0.2 in production
|
|
39
|
+
|
|
40
|
+
// Which outgoing requests get sentry-trace + baggage headers
|
|
41
|
+
tracePropagationTargets: ["localhost", /^https:\/\/api\.yourapp\.com/],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> **To disable tracing entirely:** omit both `tracesSampleRate` and `tracesSampler`.
|
|
46
|
+
> Setting `tracesSampleRate: 0` is **not** the same — the integration still runs, it
|
|
47
|
+
> just doesn’t send data.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## `browserTracingIntegration` — All Options
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
Sentry.browserTracingIntegration({/* option: default */});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Page Load & Navigation
|
|
58
|
+
|
|
59
|
+
| Option | Type | Default | Description |
|
|
60
|
+
| ---------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `instrumentPageLoad` | `boolean` | `true` | Create a `pageload` span on initial load. Disable when you want to name the span yourself via `startBrowserTracingPageLoadSpan`. |
|
|
62
|
+
| `instrumentNavigation` | `boolean` | `true` | Create `navigation` spans on History API changes. |
|
|
63
|
+
|
|
64
|
+
### Span Lifecycle / Timing
|
|
65
|
+
|
|
66
|
+
| Option | Type | Default | Description |
|
|
67
|
+
| -------------------- | ------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `idleTimeout` | `number` (ms) | `1000` | How long to wait after the last child span finishes before closing the root span. The root takes the last child’s end time as its own end time. |
|
|
69
|
+
| `finalTimeout` | `number` (ms) | `30000` | Hard cap on how long a pageload/navigation span can live. Prevents runaway open spans. |
|
|
70
|
+
| `childSpanTimeout` | `number` (ms) | `15000` | If a child span hasn’t finished within this time, the root span finishes anyway. |
|
|
71
|
+
| `markBackgroundSpan` | `boolean` | `true` | When the tab goes to the background, mark the active span as `cancelled` and close it. |
|
|
72
|
+
|
|
73
|
+
### HTTP Request Spans
|
|
74
|
+
|
|
75
|
+
| Option | Type | Default | Description |
|
|
76
|
+
| ---------------------------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
|
|
77
|
+
| `traceFetch` | `boolean` | `true` | Auto-create child spans for `fetch()` calls. |
|
|
78
|
+
| `traceXHR` | `boolean` | `true` | Auto-create child spans for `XMLHttpRequest` calls. |
|
|
79
|
+
| `enableHTTPTimings` | `boolean` | `true` | Enrich HTTP spans with Resource Timing API data: DNS lookup, TLS handshake, connection, TTFB, download time. |
|
|
80
|
+
| `shouldCreateSpanForRequest` | `(url: string) => boolean` | — | Return `false` to skip creating a span for a specific URL. |
|
|
81
|
+
| `onRequestSpanStart` | `(span, requestInfo) => void` | — | Fires when a fetch/XHR span starts. Add custom attributes based on headers or URL. |
|
|
82
|
+
|
|
83
|
+
### Performance Observations
|
|
84
|
+
|
|
85
|
+
| Option | Type | Default | Description |
|
|
86
|
+
| -------------------------- | --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `enableLongTask` | `boolean` | `true` | Capture spans for Long Tasks — main-thread blocks > 50ms. |
|
|
88
|
+
| `enableLongAnimationFrame` | `boolean` | `true` | Capture Long Animation Frames (LoAF). Supersedes Long Tasks for most use cases. SDK ≥8.18.0. |
|
|
89
|
+
| `enableInp` | `boolean` | `true` (SDK 8.x+) | Auto-capture INP events as standalone spans. In SDK 7.x, defaults to `false` and must be opted in. |
|
|
90
|
+
| `interactionsSampleRate` | `number` | `1.0` | Applied **on top of** `tracesSampleRate` for INP spans. `interactionsSampleRate: 0.5` + `tracesSampleRate: 0.1` = **5%** of interactions captured. |
|
|
91
|
+
|
|
92
|
+
### Span Naming
|
|
93
|
+
|
|
94
|
+
| Option | Type | Default | Description |
|
|
95
|
+
| ----------------- | ------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `beforeStartSpan` | `(context: StartSpanOptions) => StartSpanOptions` | — | Called just before every pageload or navigation span is created. Mutate and return `context` to rename the span, change `op`, or add attributes. Primary use: parameterize URLs (`/users/123` → `/users/<id>`). |
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
browserTracingIntegration({
|
|
100
|
+
beforeStartSpan: (context) => ({
|
|
101
|
+
...context,
|
|
102
|
+
name: location.pathname
|
|
103
|
+
.replace(/\/[a-f0-9]{8,}/g, "/<hash>") // strip hashes/UUIDs
|
|
104
|
+
.replace(/\/\d+/g, "/<id>"), // strip numeric IDs
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Trace Linking
|
|
110
|
+
|
|
111
|
+
| Option | Type | Default | Description |
|
|
112
|
+
| ------------------------ | ------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `linkPreviousTrace` | `'in-memory' \| 'session-storage' \| false` | `'in-memory'` | How a new pageload links back to the previous trace. `'session-storage'` persists across hard reloads. `false` disables linking. |
|
|
114
|
+
| `enableReportPageLoaded` | `boolean` | `false` | Enables `Sentry.reportPageLoaded()` for manually signalling page load completion in complex hydration scenarios. |
|
|
115
|
+
|
|
116
|
+
### Span Filtering
|
|
117
|
+
|
|
118
|
+
| Option | Type | Default | Description |
|
|
119
|
+
| --------------------------- | ------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
|
|
120
|
+
| `ignoreResourceSpans` | `string[]` | `[]` | Skip resource spans by `op` prefix. Example: `["resource.css", "resource.script", "resource.img"]`. |
|
|
121
|
+
| `ignorePerformanceApiSpans` | `Array<string \| RegExp>` | `[]` | Skip spans created from `performance.mark()`/`performance.measure()` matching these names. |
|
|
122
|
+
|
|
123
|
+
### Full Example With All Common Options
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import * as Sentry from "@sentry/react";
|
|
127
|
+
|
|
128
|
+
Sentry.init({
|
|
129
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
130
|
+
environment: import.meta.env.MODE,
|
|
131
|
+
|
|
132
|
+
integrations: [
|
|
133
|
+
Sentry.browserTracingIntegration({
|
|
134
|
+
// Lifecycle
|
|
135
|
+
idleTimeout: 1000,
|
|
136
|
+
finalTimeout: 30_000,
|
|
137
|
+
childSpanTimeout: 15_000,
|
|
138
|
+
markBackgroundSpan: true,
|
|
139
|
+
|
|
140
|
+
// HTTP spans
|
|
141
|
+
traceFetch: true,
|
|
142
|
+
traceXHR: true,
|
|
143
|
+
enableHTTPTimings: true,
|
|
144
|
+
shouldCreateSpanForRequest: (url) =>
|
|
145
|
+
!url.includes("/health") && !url.includes("/__webpack_hmr"),
|
|
146
|
+
onRequestSpanStart: (span, { headers }) => {
|
|
147
|
+
const rid = headers?.["x-request-id"];
|
|
148
|
+
if (rid) span.setAttribute("request.id", rid);
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// Performance observations
|
|
152
|
+
enableLongTask: true,
|
|
153
|
+
enableLongAnimationFrame: true, // SDK ≥8.18.0
|
|
154
|
+
enableInp: true,
|
|
155
|
+
interactionsSampleRate: 1.0,
|
|
156
|
+
|
|
157
|
+
// Span naming
|
|
158
|
+
beforeStartSpan: (context) => ({
|
|
159
|
+
...context,
|
|
160
|
+
name: context.name.replace(/\/\d+/g, "/<id>"),
|
|
161
|
+
}),
|
|
162
|
+
|
|
163
|
+
// Filtering
|
|
164
|
+
ignoreResourceSpans: ["resource.css"],
|
|
165
|
+
|
|
166
|
+
// Trace linking
|
|
167
|
+
linkPreviousTrace: "in-memory",
|
|
168
|
+
}),
|
|
169
|
+
],
|
|
170
|
+
|
|
171
|
+
tracesSampleRate: 1.0,
|
|
172
|
+
tracePropagationTargets: ["localhost", /^https:\/\/api\.myapp\.com/],
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## What’s Auto-Instrumented
|
|
179
|
+
|
|
180
|
+
### Page Load (`op: "pageload"`)
|
|
181
|
+
|
|
182
|
+
- Created on initial page render using `window.performance` timing API
|
|
183
|
+
- Contains Web Vitals: **LCP**, **CLS**, **FCP**, **TTFB**
|
|
184
|
+
- HTTP requests made during page load appear as child spans
|
|
185
|
+
- Long Tasks and Long Animation Frames appear as child spans
|
|
186
|
+
|
|
187
|
+
### Navigation (`op: "navigation"`)
|
|
188
|
+
|
|
189
|
+
- Created on every client-side navigation via the History API
|
|
190
|
+
- Does **not** include Web Vitals (those are page-load only)
|
|
191
|
+
- HTTP requests during navigation appear as child spans
|
|
192
|
+
|
|
193
|
+
### HTTP Spans (`op: "http.client"`)
|
|
194
|
+
|
|
195
|
+
- Automatic for both `fetch()` and `XMLHttpRequest`
|
|
196
|
+
- Captures: method, URL, HTTP status code, response size
|
|
197
|
+
- With `enableHTTPTimings`: DNS lookup time, TLS handshake, connection time, TTFB,
|
|
198
|
+
download time
|
|
199
|
+
|
|
200
|
+
### Long Task Spans (`op: "ui.long-task"`)
|
|
201
|
+
|
|
202
|
+
- Created for any main-thread block > 50ms
|
|
203
|
+
- Helps identify JavaScript that blocks interactivity
|
|
204
|
+
|
|
205
|
+
### Long Animation Frame Spans (`op: "ui.long-animation-frame"`)
|
|
206
|
+
|
|
207
|
+
- SDK 8.18.0+; based on the LoAF API
|
|
208
|
+
- Captures render-blocking work including style/layout recalculations
|
|
209
|
+
- More accurate than Long Tasks for measuring rendering bottlenecks
|
|
210
|
+
|
|
211
|
+
### INP / Interaction Spans (`op: "ui.interaction"`)
|
|
212
|
+
|
|
213
|
+
- Standalone spans capturing Interaction to Next Paint
|
|
214
|
+
- Emitted on page hide (tab switch, navigation away)
|
|
215
|
+
- Attributes: `component`, `element`, `interaction_type`
|
|
216
|
+
- On by default in SDK 8.x+; opt-in (`enableInp: true`) in SDK 7.x
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Web Vitals
|
|
221
|
+
|
|
222
|
+
`browserTracingIntegration()` captures Core Web Vitals automatically and surfaces them
|
|
223
|
+
in the **Sentry Web Vitals** product module:
|
|
224
|
+
|
|
225
|
+
| Vital | What it measures | Good | Needs Improvement | Poor |
|
|
226
|
+
| ----------------------------------- | ------------------------------------------------------------------ | ------- | ----------------- | ------- |
|
|
227
|
+
| **LCP** — Largest Contentful Paint | Time for largest viewport element to render | ≤ 2.5s | ≤ 4s | > 4s |
|
|
228
|
+
| **INP** — Interaction to Next Paint | Time from user interaction to next paint (replaced FID March 2024) | ≤ 200ms | ≤ 500ms | > 500ms |
|
|
229
|
+
| **CLS** — Cumulative Layout Shift | Sum of unexpected layout shift scores | ≤ 0.1 | ≤ 0.25 | > 0.25 |
|
|
230
|
+
| **FCP** — First Contentful Paint | Time for first content to render | ≤ 1s | ≤ 3s | > 3s |
|
|
231
|
+
| **TTFB** — Time to First Byte | Time until browser receives first byte | ≤ 100ms | ≤ 200ms | > 200ms |
|
|
232
|
+
| **FID** — First Input Delay | _(Legacy — collected but replaced by INP)_ | ≤ 100ms | ≤ 300ms | > 300ms |
|
|
233
|
+
|
|
234
|
+
> **LCP and CLS timing note:** These keep changing after the pageload span ends.
|
|
235
|
+
> Sentry captures their final values via `visibilitychange` and page hide events.
|
|
236
|
+
> INP is similarly emitted as a standalone span on page hide.
|
|
237
|
+
|
|
238
|
+
**INP in SDK 7.x** (must opt in):
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
browserTracingIntegration({ enableInp: true });
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## React Router Integrations
|
|
247
|
+
|
|
248
|
+
All React Router integrations live in `@sentry/react`. The core mechanism: **replace**
|
|
249
|
+
`browserTracingIntegration()` with the router-specific variant.
|
|
250
|
+
Both cannot be used simultaneously.
|
|
251
|
+
|
|
252
|
+
> This section is for React Router non-framework/data/declarative usage.
|
|
253
|
+
> If the project uses React Router Framework mode with `@sentry/react-router`, use
|
|
254
|
+
> [`react-router-framework`](../react-router-framework/index.md).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### React Router v7 (Library Mode)
|
|
259
|
+
|
|
260
|
+
**Package:** `react-router` (v7)\
|
|
261
|
+
**Import source for hooks:** `"react-router"`
|
|
262
|
+
|
|
263
|
+
#### Method 1 — `createBrowserRouter` (Recommended)
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// src/instrument.ts
|
|
267
|
+
import React from "react";
|
|
268
|
+
import {
|
|
269
|
+
createBrowserRouter,
|
|
270
|
+
createRoutesFromChildren,
|
|
271
|
+
matchRoutes,
|
|
272
|
+
useLocation,
|
|
273
|
+
useNavigationType,
|
|
274
|
+
} from "react-router";
|
|
275
|
+
import * as Sentry from "@sentry/react";
|
|
276
|
+
|
|
277
|
+
Sentry.init({
|
|
278
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
279
|
+
integrations: [
|
|
280
|
+
Sentry.reactRouterV7BrowserTracingIntegration({
|
|
281
|
+
useEffect: React.useEffect,
|
|
282
|
+
useLocation,
|
|
283
|
+
useNavigationType,
|
|
284
|
+
createRoutesFromChildren,
|
|
285
|
+
matchRoutes,
|
|
286
|
+
}),
|
|
287
|
+
],
|
|
288
|
+
tracesSampleRate: 1.0,
|
|
289
|
+
tracePropagationTargets: ["localhost", /^https:\/\/api\.myapp\.com/],
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// src/router.ts
|
|
295
|
+
import { createBrowserRouter } from "react-router";
|
|
296
|
+
import * as Sentry from "@sentry/react";
|
|
297
|
+
import { RootLayout, RootErrorBoundary } from "./layouts";
|
|
298
|
+
import { HomePage, UsersPage, UserDetailPage, DashboardPage } from "./pages";
|
|
299
|
+
|
|
300
|
+
// Wrap createBrowserRouter with Sentry instrumentation
|
|
301
|
+
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter);
|
|
302
|
+
|
|
303
|
+
export const router = sentryCreateBrowserRouter([
|
|
304
|
+
{
|
|
305
|
+
path: "/",
|
|
306
|
+
element: <RootLayout />,
|
|
307
|
+
errorElement: <RootErrorBoundary />, // see Error Boundary section below
|
|
308
|
+
children: [
|
|
309
|
+
{ index: true, element: <HomePage /> },
|
|
310
|
+
{ path: "users", element: <UsersPage /> },
|
|
311
|
+
{ path: "users/:userId", element: <UserDetailPage /> },
|
|
312
|
+
{ path: "dashboard", element: <DashboardPage />,
|
|
313
|
+
children: [
|
|
314
|
+
{ path: "analytics", element: <AnalyticsPage /> },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
]);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// src/main.tsx
|
|
324
|
+
import ReactDOM from "react-dom/client";
|
|
325
|
+
import { RouterProvider } from "react-router";
|
|
326
|
+
import "./instrument"; // ← MUST be first
|
|
327
|
+
import { router } from "./router";
|
|
328
|
+
|
|
329
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
330
|
+
<RouterProvider router={router} />
|
|
331
|
+
);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Lazy routes (SDK ≥10.39.0):** add `enableAsyncRouteHandlers` and declare all route
|
|
335
|
+
paths:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
Sentry.reactRouterV7BrowserTracingIntegration({
|
|
339
|
+
useEffect: React.useEffect,
|
|
340
|
+
useLocation,
|
|
341
|
+
useNavigationType,
|
|
342
|
+
createRoutesFromChildren,
|
|
343
|
+
matchRoutes,
|
|
344
|
+
enableAsyncRouteHandlers: true,
|
|
345
|
+
lazyRouteManifest: [
|
|
346
|
+
"/",
|
|
347
|
+
"/users",
|
|
348
|
+
"/users/:userId",
|
|
349
|
+
"/users/:userId/settings",
|
|
350
|
+
"/dashboard",
|
|
351
|
+
"/dashboard/analytics",
|
|
352
|
+
],
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Other router factories:**
|
|
357
|
+
|
|
358
|
+
| Factory | Sentry wrapper |
|
|
359
|
+
| --------------------- | --------------------------------------------------- |
|
|
360
|
+
| `createBrowserRouter` | `Sentry.wrapCreateBrowserRouterV7` |
|
|
361
|
+
| `createMemoryRouter` | `Sentry.wrapCreateMemoryRouterV7` |
|
|
362
|
+
| `createHashRouter` | `Sentry.wrapCreateBrowserRouterV7` (works for both) |
|
|
363
|
+
|
|
364
|
+
#### Method 2 — `<Routes>` Component
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import React from "react";
|
|
368
|
+
import ReactDOM from "react-dom/client";
|
|
369
|
+
import {
|
|
370
|
+
BrowserRouter, Routes, Route,
|
|
371
|
+
createRoutesFromChildren, matchRoutes,
|
|
372
|
+
useLocation, useNavigationType,
|
|
373
|
+
} from "react-router";
|
|
374
|
+
import * as Sentry from "@sentry/react";
|
|
375
|
+
|
|
376
|
+
Sentry.init({
|
|
377
|
+
// ... same init as Method 1
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Wrap Routes ONCE at the top level — do NOT wrap nested <Routes>
|
|
381
|
+
const SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes);
|
|
382
|
+
|
|
383
|
+
function App() {
|
|
384
|
+
return (
|
|
385
|
+
<BrowserRouter>
|
|
386
|
+
<SentryRoutes>
|
|
387
|
+
<Route path="/" element={<HomePage />} />
|
|
388
|
+
<Route path="/about" element={<AboutPage />} />
|
|
389
|
+
<Route path="/users/:userId" element={<UserDetailPage />} />
|
|
390
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
391
|
+
</SentryRoutes>
|
|
392
|
+
</BrowserRouter>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Also works with `MemoryRouter` and `HashRouter`.
|
|
398
|
+
|
|
399
|
+
#### Method 3 — `useRoutes` Hook
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { useRoutes, BrowserRouter } from "react-router";
|
|
403
|
+
import * as Sentry from "@sentry/react";
|
|
404
|
+
|
|
405
|
+
// MUST call wrapUseRoutesV7 OUTSIDE any React component
|
|
406
|
+
const useSentryRoutes = Sentry.wrapUseRoutesV7(useRoutes);
|
|
407
|
+
|
|
408
|
+
function App() {
|
|
409
|
+
return useSentryRoutes([
|
|
410
|
+
{ path: "/", element: <HomePage /> },
|
|
411
|
+
{ path: "/users/:userId", element: <UserDetailPage /> },
|
|
412
|
+
{ path: "/dashboard", element: <DashboardPage />,
|
|
413
|
+
children: [
|
|
414
|
+
{ path: "analytics", element: <AnalyticsPage /> },
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
]);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
421
|
+
<BrowserRouter><App /></BrowserRouter>
|
|
422
|
+
);
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
#### Error Boundary (Required for Production Error Capture)
|
|
426
|
+
|
|
427
|
+
React Router v7’s default `errorElement` swallows errors silently.
|
|
428
|
+
You must capture them manually:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
import { useRouteError } from "react-router";
|
|
432
|
+
import * as Sentry from "@sentry/react";
|
|
433
|
+
|
|
434
|
+
export function SentryRouteErrorBoundary() {
|
|
435
|
+
const error = useRouteError() as Error;
|
|
436
|
+
|
|
437
|
+
React.useEffect(() => {
|
|
438
|
+
if (error) Sentry.captureException(error);
|
|
439
|
+
}, [error]);
|
|
440
|
+
|
|
441
|
+
return (
|
|
442
|
+
<div role="alert">
|
|
443
|
+
<h1>Something went wrong</h1>
|
|
444
|
+
<p>{error?.message ?? "An unexpected error occurred."}</p>
|
|
445
|
+
</div>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Apply as errorElement on your root route and any nested boundaries:
|
|
450
|
+
const router = sentryCreateBrowserRouter([
|
|
451
|
+
{
|
|
452
|
+
path: "/",
|
|
453
|
+
element: <RootLayout />,
|
|
454
|
+
errorElement: <SentryRouteErrorBoundary />,
|
|
455
|
+
children: [
|
|
456
|
+
{
|
|
457
|
+
path: "checkout",
|
|
458
|
+
element: <CheckoutPage />,
|
|
459
|
+
errorElement: <SentryRouteErrorBoundary />, // nested boundary
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
]);
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
### React Router v6
|
|
469
|
+
|
|
470
|
+
**Package:** `react-router-dom` (v6)\
|
|
471
|
+
**Import source for hooks:** `"react-router-dom"`
|
|
472
|
+
|
|
473
|
+
#### Method 1 — `createBrowserRouter` (Recommended for v6.4+)
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
import React from "react";
|
|
477
|
+
import {
|
|
478
|
+
createBrowserRouter,
|
|
479
|
+
createRoutesFromChildren,
|
|
480
|
+
matchRoutes,
|
|
481
|
+
useLocation,
|
|
482
|
+
useNavigationType,
|
|
483
|
+
} from "react-router-dom";
|
|
484
|
+
import * as Sentry from "@sentry/react";
|
|
485
|
+
|
|
486
|
+
Sentry.init({
|
|
487
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
488
|
+
integrations: [
|
|
489
|
+
Sentry.reactRouterV6BrowserTracingIntegration({
|
|
490
|
+
useEffect: React.useEffect,
|
|
491
|
+
useLocation,
|
|
492
|
+
useNavigationType,
|
|
493
|
+
createRoutesFromChildren,
|
|
494
|
+
matchRoutes,
|
|
495
|
+
}),
|
|
496
|
+
],
|
|
497
|
+
tracesSampleRate: 1.0,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Wrap createBrowserRouter
|
|
501
|
+
const sentryCreateBrowserRouter =
|
|
502
|
+
Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
|
|
503
|
+
|
|
504
|
+
export const router = sentryCreateBrowserRouter([
|
|
505
|
+
{
|
|
506
|
+
path: "/",
|
|
507
|
+
element: <Root />,
|
|
508
|
+
children: [
|
|
509
|
+
{ index: true, element: <HomePage /> },
|
|
510
|
+
{ path: "users/:userId", element: <UserDetailPage /> },
|
|
511
|
+
{ path: "settings", element: <SettingsPage /> },
|
|
512
|
+
],
|
|
513
|
+
},
|
|
514
|
+
]);
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Other router factories (SDK ≥8.50.0):**
|
|
518
|
+
|
|
519
|
+
| Factory | Sentry wrapper |
|
|
520
|
+
| --------------------- | ---------------------------------- |
|
|
521
|
+
| `createBrowserRouter` | `Sentry.wrapCreateBrowserRouterV6` |
|
|
522
|
+
| `createMemoryRouter` | `Sentry.wrapCreateMemoryRouterV6` |
|
|
523
|
+
|
|
524
|
+
#### Method 2 — `<Routes>` Component
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
import {
|
|
528
|
+
BrowserRouter, Routes, Route,
|
|
529
|
+
createRoutesFromChildren, matchRoutes,
|
|
530
|
+
useLocation, useNavigationType,
|
|
531
|
+
} from "react-router-dom";
|
|
532
|
+
import * as Sentry from "@sentry/react";
|
|
533
|
+
|
|
534
|
+
Sentry.init({ /* ... same as above */ });
|
|
535
|
+
|
|
536
|
+
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
|
|
537
|
+
|
|
538
|
+
function App() {
|
|
539
|
+
return (
|
|
540
|
+
<BrowserRouter>
|
|
541
|
+
<SentryRoutes>
|
|
542
|
+
<Route path="/" element={<HomePage />} />
|
|
543
|
+
<Route path="/users/:userId" element={<UserPage />} />
|
|
544
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
545
|
+
</SentryRoutes>
|
|
546
|
+
</BrowserRouter>
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
#### Method 3 — `useRoutes` Hook
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
import { useRoutes, BrowserRouter } from "react-router-dom";
|
|
555
|
+
import * as Sentry from "@sentry/react";
|
|
556
|
+
|
|
557
|
+
// Call OUTSIDE components
|
|
558
|
+
const useSentryRoutes = Sentry.wrapUseRoutesV6(useRoutes);
|
|
559
|
+
|
|
560
|
+
function App() {
|
|
561
|
+
return useSentryRoutes([
|
|
562
|
+
{ path: "/", element: <Home /> },
|
|
563
|
+
{ path: "/users/:userId", element: <User /> },
|
|
564
|
+
]);
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
### React Router v4 / v5
|
|
571
|
+
|
|
572
|
+
**Package:** `react-router-dom` (v4 or v5) + `history`
|
|
573
|
+
|
|
574
|
+
#### Method 1 — `withSentryRouting` HOC (Recommended)
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
import React from "react";
|
|
578
|
+
import ReactDOM from "react-dom";
|
|
579
|
+
import { Route, Router, Switch } from "react-router-dom";
|
|
580
|
+
import { createBrowserHistory } from "history";
|
|
581
|
+
import * as Sentry from "@sentry/react";
|
|
582
|
+
|
|
583
|
+
// 1. Create a history instance
|
|
584
|
+
const history = createBrowserHistory();
|
|
585
|
+
|
|
586
|
+
// 2. Init with reactRouterV5BrowserTracingIntegration
|
|
587
|
+
Sentry.init({
|
|
588
|
+
dsn: "...",
|
|
589
|
+
integrations: [
|
|
590
|
+
Sentry.reactRouterV5BrowserTracingIntegration({ history }),
|
|
591
|
+
],
|
|
592
|
+
tracesSampleRate: 1.0,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// 3. Wrap Route with HOC — enables parameterized transaction names
|
|
596
|
+
const SentryRoute = Sentry.withSentryRouting(Route);
|
|
597
|
+
|
|
598
|
+
// 4. Use SentryRoute everywhere instead of Route
|
|
599
|
+
// ORDER MATTERS — most specific paths first (decreasing specificity)
|
|
600
|
+
function App() {
|
|
601
|
+
return (
|
|
602
|
+
<Router history={history}>
|
|
603
|
+
<Switch>
|
|
604
|
+
<SentryRoute path="/users/:userId/settings" component={UserSettingsPage} />
|
|
605
|
+
<SentryRoute path="/users/:userId" component={UserPage} />
|
|
606
|
+
<SentryRoute path="/users" component={UsersPage} />
|
|
607
|
+
<SentryRoute path="/" component={HomePage} />
|
|
608
|
+
</Switch>
|
|
609
|
+
</Router>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
ReactDOM.render(<App />, document.getElementById("root"));
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
#### Method 2 — Static Route Config (no HOC)
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
import { matchPath } from "react-router-dom";
|
|
620
|
+
import { createBrowserHistory } from "history";
|
|
621
|
+
import * as Sentry from "@sentry/react";
|
|
622
|
+
|
|
623
|
+
const history = createBrowserHistory();
|
|
624
|
+
|
|
625
|
+
// Define all routes; most specific first
|
|
626
|
+
const routes = [
|
|
627
|
+
{ path: "/users/:userId/settings" },
|
|
628
|
+
{ path: "/users/:userId" },
|
|
629
|
+
{ path: "/users" },
|
|
630
|
+
{ path: "/dashboard/analytics" },
|
|
631
|
+
{ path: "/dashboard" },
|
|
632
|
+
{ path: "/" },
|
|
633
|
+
];
|
|
634
|
+
|
|
635
|
+
Sentry.init({
|
|
636
|
+
dsn: "...",
|
|
637
|
+
integrations: [
|
|
638
|
+
Sentry.reactRouterV5BrowserTracingIntegration({
|
|
639
|
+
history,
|
|
640
|
+
routes,
|
|
641
|
+
matchPath, // from react-router-dom
|
|
642
|
+
}),
|
|
643
|
+
],
|
|
644
|
+
tracesSampleRate: 1.0,
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**React Router v4:** use `Sentry.reactRouterV4BrowserTracingIntegration` — the API is
|
|
649
|
+
identical to v5.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### TanStack Router
|
|
654
|
+
|
|
655
|
+
**Requires:** `@tanstack/react-router` ≥1.64.0
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
// src/main.tsx
|
|
659
|
+
import * as Sentry from "@sentry/react";
|
|
660
|
+
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
|
661
|
+
import { routeTree } from "./routeTree.gen"; // generated by TanStack Router
|
|
662
|
+
|
|
663
|
+
// 1. Create the router first
|
|
664
|
+
const router = createRouter({
|
|
665
|
+
routeTree,
|
|
666
|
+
defaultPreload: "intent",
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// 2. Init Sentry, passing the router instance
|
|
670
|
+
Sentry.init({
|
|
671
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
672
|
+
integrations: [
|
|
673
|
+
Sentry.tanstackRouterBrowserTracingIntegration(router),
|
|
674
|
+
],
|
|
675
|
+
tracesSampleRate: 1.0,
|
|
676
|
+
tracePropagationTargets: ["localhost", /^https:\/\/api\.myapp\.com/],
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// 3. Render
|
|
680
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
681
|
+
<StrictMode>
|
|
682
|
+
<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
|
|
683
|
+
<RouterProvider router={router} />
|
|
684
|
+
</Sentry.ErrorBoundary>
|
|
685
|
+
</StrictMode>
|
|
686
|
+
);
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
**Key difference vs React Router:** `tanstackRouterBrowserTracingIntegration` takes the
|
|
690
|
+
router instance directly — no hooks (`useLocation`, `useNavigationType`) or helpers
|
|
691
|
+
(`createRoutesFromChildren`, `matchRoutes`) are needed.
|
|
692
|
+
TanStack Router exposes its route definitions directly to the SDK.
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
### How Route Names Are Parameterized
|
|
697
|
+
|
|
698
|
+
All router integrations extract parameterized route patterns instead of literal URLs:
|
|
699
|
+
|
|
700
|
+
| Actual URL | Transaction Name |
|
|
701
|
+
| --------------------------- | ------------------------ |
|
|
702
|
+
| `/users/42` | `/users/:userId` |
|
|
703
|
+
| `/orders/abc-123/items` | `/orders/:orderId/items` |
|
|
704
|
+
| `/posts/2024/my-first-post` | `/posts/:year/:slug` |
|
|
705
|
+
|
|
706
|
+
This grouping is essential for meaningful performance data — without it, every user
|
|
707
|
+
generates a unique transaction name and nothing can be aggregated.
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
### Router Integration Quick-Reference
|
|
712
|
+
|
|
713
|
+
```
|
|
714
|
+
Are you using React Router?
|
|
715
|
+
├─ Framework mode (`@sentry/react-router`) ─► use the react-router-framework reference
|
|
716
|
+
│
|
|
717
|
+
├─ v7 (react-router package) ──────► reactRouterV7BrowserTracingIntegration
|
|
718
|
+
│ ├─ createBrowserRouter? ──────► wrapCreateBrowserRouterV7(createBrowserRouter)
|
|
719
|
+
│ ├─ createMemoryRouter? ───────► wrapCreateMemoryRouterV7(createMemoryRouter)
|
|
720
|
+
│ ├─ <Routes> component? ───────► withSentryReactRouterV7Routing(Routes)
|
|
721
|
+
│ └─ useRoutes hook? ────────────► wrapUseRoutesV7(useRoutes)
|
|
722
|
+
│
|
|
723
|
+
├─ v6 (react-router-dom) ──────────► reactRouterV6BrowserTracingIntegration
|
|
724
|
+
│ ├─ createBrowserRouter? ──────► wrapCreateBrowserRouterV6(createBrowserRouter)
|
|
725
|
+
│ ├─ createMemoryRouter? ───────► wrapCreateMemoryRouterV6(createMemoryRouter) [≥8.50.0]
|
|
726
|
+
│ ├─ <Routes> component? ───────► withSentryReactRouterV6Routing(Routes)
|
|
727
|
+
│ └─ useRoutes hook? ────────────► wrapUseRoutesV6(useRoutes)
|
|
728
|
+
│
|
|
729
|
+
├─ v4/v5 ───────────────────────────► reactRouterV5BrowserTracingIntegration({ history })
|
|
730
|
+
│ ├─ with static routes array ──► add { routes, matchPath }
|
|
731
|
+
│ └─ without static routes ─────► withSentryRouting(Route) HOC
|
|
732
|
+
│
|
|
733
|
+
└─ No router / unsupported router ──► browserTracingIntegration()
|
|
734
|
+
└─ custom router ──────────────► { instrumentPageLoad: false, instrumentNavigation: false }
|
|
735
|
+
+ startBrowserTracingPageLoadSpan
|
|
736
|
+
+ startBrowserTracingNavigationSpan
|
|
737
|
+
|
|
738
|
+
Are you using TanStack Router?
|
|
739
|
+
└─ Any version ≥1.64.0 ─────────────► tanstackRouterBrowserTracingIntegration(router)
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Custom Spans
|
|
745
|
+
|
|
746
|
+
### The Three Span APIs
|
|
747
|
+
|
|
748
|
+
#### `Sentry.startSpan()` — Active, Auto-Ending (Recommended)
|
|
749
|
+
|
|
750
|
+
Wraps a block of work.
|
|
751
|
+
The span is active (collects children) and automatically ends when the callback returns
|
|
752
|
+
or resolves:
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
// Asynchronous
|
|
756
|
+
const data = await Sentry.startSpan(
|
|
757
|
+
{
|
|
758
|
+
name: "fetchUserProfile",
|
|
759
|
+
op: "http.client",
|
|
760
|
+
attributes: {
|
|
761
|
+
"user.id": userId,
|
|
762
|
+
"cache.hit": false,
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
async () => {
|
|
766
|
+
const res = await fetch(`/api/users/${userId}`);
|
|
767
|
+
return res.json();
|
|
768
|
+
},
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
// Synchronous
|
|
772
|
+
const result = Sentry.startSpan({ name: "computeRecommendations", op: "function" }, () =>
|
|
773
|
+
expensiveComputation(),
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// Thrown errors are captured and the span is marked as error automatically
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### `Sentry.startSpanManual()` — Active, Manual End
|
|
780
|
+
|
|
781
|
+
Use when the span lifetime cannot be enclosed in a single callback — e.g., middleware
|
|
782
|
+
that calls `next()`:
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
786
|
+
return Sentry.startSpanManual({ name: "auth.verify", op: "middleware" }, (span) => {
|
|
787
|
+
// span is active inside this callback only
|
|
788
|
+
res.once("finish", () => {
|
|
789
|
+
span.setStatus({ code: res.statusCode < 400 ? 1 : 2 });
|
|
790
|
+
span.end(); // ← REQUIRED — will not end automatically
|
|
791
|
+
});
|
|
792
|
+
return next();
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
#### `Sentry.startInactiveSpan()` — Not Active, Manual End
|
|
798
|
+
|
|
799
|
+
For spans that cross event boundaries and should **not** automatically collect children
|
|
800
|
+
as parent:
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
let checkoutSpan: Sentry.Span | undefined;
|
|
804
|
+
|
|
805
|
+
// On flow start
|
|
806
|
+
document.getElementById("checkout-btn")!.addEventListener("click", () => {
|
|
807
|
+
checkoutSpan = Sentry.startInactiveSpan({
|
|
808
|
+
name: "checkout-flow",
|
|
809
|
+
op: "ui.flow",
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
// On flow end (later, in a different event handler)
|
|
814
|
+
document.getElementById("confirm-btn")!.addEventListener("click", () => {
|
|
815
|
+
checkoutSpan?.setAttribute("payment.method", "stripe");
|
|
816
|
+
checkoutSpan?.setStatus({ code: 1 });
|
|
817
|
+
checkoutSpan?.end(); // ← REQUIRED
|
|
818
|
+
});
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
Explicit parent-child wiring with inactive spans:
|
|
822
|
+
|
|
823
|
+
```typescript
|
|
824
|
+
const parentSpan = Sentry.startInactiveSpan({ name: "checkout-flow" });
|
|
825
|
+
|
|
826
|
+
const childA = Sentry.startInactiveSpan({
|
|
827
|
+
name: "validate-cart",
|
|
828
|
+
op: "function",
|
|
829
|
+
parentSpan, // ← explicit parent reference
|
|
830
|
+
});
|
|
831
|
+
await validateCart();
|
|
832
|
+
childA.end();
|
|
833
|
+
|
|
834
|
+
const childB = Sentry.startInactiveSpan({
|
|
835
|
+
name: "process-payment",
|
|
836
|
+
op: "function",
|
|
837
|
+
parentSpan,
|
|
838
|
+
});
|
|
839
|
+
await processPayment();
|
|
840
|
+
childB.end();
|
|
841
|
+
|
|
842
|
+
parentSpan.end();
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
### Span Options Reference
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
interface StartSpanOptions {
|
|
851
|
+
name: string; // Required — label shown in the UI
|
|
852
|
+
op?: string; // Operation category (see table below)
|
|
853
|
+
startTime?: number; // Unix timestamp in seconds (can be float)
|
|
854
|
+
attributes?: Record<string, string | number | boolean | string[] | number[] | boolean[]>;
|
|
855
|
+
parentSpan?: Span; // Override default parent (mainly for startInactiveSpan)
|
|
856
|
+
onlyIfParent?: boolean; // Drop this span if there is no currently active parent
|
|
857
|
+
forceTransaction?: boolean; // Force span to appear as a root transaction in the UI
|
|
858
|
+
}
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**Common `op` values:**
|
|
862
|
+
|
|
863
|
+
| `op` | When to use |
|
|
864
|
+
| ------------------------- | ------------------------------------- |
|
|
865
|
+
| `http.client` | Outgoing HTTP requests |
|
|
866
|
+
| `db.query` | Database queries |
|
|
867
|
+
| `ui.render` | React component render work |
|
|
868
|
+
| `ui.load` | Async data loading for a page/view |
|
|
869
|
+
| `ui.click` | User click event handling |
|
|
870
|
+
| `ui.flow` | Multi-step UI flow (checkout, wizard) |
|
|
871
|
+
| `function` | General JS function calls |
|
|
872
|
+
| `task` | Background/scheduled work |
|
|
873
|
+
| `cache.get` / `cache.set` | Cache reads/writes |
|
|
874
|
+
| `middleware` | Express/Koa/Fastify middleware |
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
### Enriching Spans
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
const span = Sentry.getActiveSpan();
|
|
882
|
+
|
|
883
|
+
if (span) {
|
|
884
|
+
// Single attribute
|
|
885
|
+
span.setAttribute("db.table", "users");
|
|
886
|
+
span.setAttribute("db.rows_affected", 5);
|
|
887
|
+
|
|
888
|
+
// Multiple attributes at once
|
|
889
|
+
span.setAttributes({
|
|
890
|
+
"http.method": "POST",
|
|
891
|
+
"http.status_code": 201,
|
|
892
|
+
"user.tier": "premium",
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
// Status codes: 0=unset, 1=ok, 2=error
|
|
896
|
+
span.setStatus({ code: 1 });
|
|
897
|
+
span.setStatus({ code: 2, message: "Upstream timeout" });
|
|
898
|
+
|
|
899
|
+
// HTTP-specific shorthand
|
|
900
|
+
span.setHttpStatus(404); // sets code=2, message="Not Found"
|
|
901
|
+
span.setHttpStatus(200); // sets code=1
|
|
902
|
+
|
|
903
|
+
// Rename at runtime
|
|
904
|
+
span.updateName("GET /users/:id");
|
|
905
|
+
|
|
906
|
+
// End with explicit timestamp (seconds since epoch)
|
|
907
|
+
span.end(Date.now() / 1000);
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
### Nesting Spans
|
|
914
|
+
|
|
915
|
+
Children nest automatically under the currently active span:
|
|
916
|
+
|
|
917
|
+
```typescript
|
|
918
|
+
await Sentry.startSpan({ name: "loadDashboard", op: "ui.load" }, async () => {
|
|
919
|
+
// These are children of "loadDashboard"
|
|
920
|
+
const [user, posts] = await Promise.all([
|
|
921
|
+
Sentry.startSpan({ name: "fetchUser", op: "http.client" }, () =>
|
|
922
|
+
fetch("/api/user").then((r) => r.json()),
|
|
923
|
+
),
|
|
924
|
+
Sentry.startSpan({ name: "fetchPosts", op: "http.client" }, () =>
|
|
925
|
+
fetch("/api/posts").then((r) => r.json()),
|
|
926
|
+
),
|
|
927
|
+
]);
|
|
928
|
+
|
|
929
|
+
// Sequential child — still nested under "loadDashboard"
|
|
930
|
+
await Sentry.startSpan({ name: "renderDashboard", op: "ui.render" }, async () => {
|
|
931
|
+
await renderContent(user, posts);
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
### `forceTransaction` — Standalone Root Span
|
|
937
|
+
|
|
938
|
+
Forces a span to appear as its own root transaction in the Sentry UI, independent of any
|
|
939
|
+
active parent. Useful for background workers, Web Workers, or queue processors:
|
|
940
|
+
|
|
941
|
+
```typescript
|
|
942
|
+
Sentry.startSpan({ name: "processEmailQueue", op: "task", forceTransaction: true }, async () => {
|
|
943
|
+
const batch = await queue.take(50);
|
|
944
|
+
await processBatch(batch);
|
|
945
|
+
});
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
### Browser Flat Span Hierarchy
|
|
949
|
+
|
|
950
|
+
In browsers, all child spans are attached **flat** to the root span (not nested under
|
|
951
|
+
intermediate parents).
|
|
952
|
+
This prevents incorrect parent-child attribution in parallel async flows.
|
|
953
|
+
|
|
954
|
+
To opt into true nesting (use with care):
|
|
955
|
+
|
|
956
|
+
```typescript
|
|
957
|
+
Sentry.init({
|
|
958
|
+
// ...
|
|
959
|
+
parentSpanIsAlwaysRootSpan: false,
|
|
960
|
+
});
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
## Distributed Tracing
|
|
966
|
+
|
|
967
|
+
Distributed tracing connects a browser page load to all backend API calls it triggers,
|
|
968
|
+
creating a single end-to-end waterfall.
|
|
969
|
+
|
|
970
|
+
### The Two Headers
|
|
971
|
+
|
|
972
|
+
| Header | Format | Purpose |
|
|
973
|
+
| -------------- | ------------------------------------------ | ------------------------------------ |
|
|
974
|
+
| `sentry-trace` | `{traceId}-{spanId}-{sampled}` | Carries trace context |
|
|
975
|
+
| `baggage` | W3C Baggage format with `sentry-*` entries | Carries sampling decision + metadata |
|
|
976
|
+
|
|
977
|
+
Both headers are automatically injected into `fetch()` and `XMLHttpRequest` for URLs
|
|
978
|
+
matching `tracePropagationTargets`.
|
|
979
|
+
|
|
980
|
+
### `tracePropagationTargets`
|
|
981
|
+
|
|
982
|
+
```typescript
|
|
983
|
+
Sentry.init({
|
|
984
|
+
tracePropagationTargets: [
|
|
985
|
+
// String = substring match against full URL
|
|
986
|
+
"localhost",
|
|
987
|
+
"api.myapp.com",
|
|
988
|
+
|
|
989
|
+
// RegExp = tested against full URL
|
|
990
|
+
/^https:\/\/api\.myapp\.com\//,
|
|
991
|
+
/^\/api\//, // same-origin relative paths
|
|
992
|
+
|
|
993
|
+
// Multiple backends
|
|
994
|
+
"https://auth.myapp.com",
|
|
995
|
+
"https://payments.myapp.com",
|
|
996
|
+
],
|
|
997
|
+
});
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
**Defaults:** Same-origin requests get headers automatically.
|
|
1001
|
+
Cross-origin requests need explicit entries.
|
|
1002
|
+
|
|
1003
|
+
**Disable completely:**
|
|
1004
|
+
|
|
1005
|
+
```typescript
|
|
1006
|
+
tracePropagationTargets: []; // no distributed tracing headers on any requests
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### CORS Requirements
|
|
1010
|
+
|
|
1011
|
+
Your backend APIs **must** allowlist these headers:
|
|
1012
|
+
|
|
1013
|
+
```
|
|
1014
|
+
Access-Control-Allow-Headers: sentry-trace, baggage
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
Express example:
|
|
1018
|
+
|
|
1019
|
+
```javascript
|
|
1020
|
+
app.use((_req, res, next) => {
|
|
1021
|
+
res.setHeader(
|
|
1022
|
+
"Access-Control-Allow-Headers",
|
|
1023
|
+
"Content-Type, Authorization, sentry-trace, baggage",
|
|
1024
|
+
);
|
|
1025
|
+
next();
|
|
1026
|
+
});
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
Without this, preflight requests fail and browsers suppress the headers.
|
|
1030
|
+
|
|
1031
|
+
### SSR / Meta Tag Approach
|
|
1032
|
+
|
|
1033
|
+
When your HTML is server-rendered, emit Sentry trace context into `<meta>` tags.
|
|
1034
|
+
`browserTracingIntegration` reads them on init and attaches the pageload span to the
|
|
1035
|
+
server’s trace — the full request becomes one continuous trace.
|
|
1036
|
+
|
|
1037
|
+
Server (Node.js/Express):
|
|
1038
|
+
|
|
1039
|
+
```typescript
|
|
1040
|
+
import * as Sentry from "@sentry/node";
|
|
1041
|
+
|
|
1042
|
+
app.get("/", (_req, res) => {
|
|
1043
|
+
const traceData = Sentry.getTraceData();
|
|
1044
|
+
// { "sentry-trace": "...", baggage: "..." }
|
|
1045
|
+
res.render("index", {
|
|
1046
|
+
sentryTrace: traceData["sentry-trace"],
|
|
1047
|
+
sentryBaggage: traceData["baggage"],
|
|
1048
|
+
});
|
|
1049
|
+
});
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
HTML template (EJS/Handlebars/Jinja/etc.):
|
|
1053
|
+
|
|
1054
|
+
```html
|
|
1055
|
+
<head>
|
|
1056
|
+
<meta name="sentry-trace" content="<%= sentryTrace %>" />
|
|
1057
|
+
<meta name="baggage" content="<%= sentryBaggage %>" />
|
|
1058
|
+
</head>
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
The browser SDK reads these tags automatically — no extra client config needed.
|
|
1062
|
+
|
|
1063
|
+
### Manual Propagation (WebSockets, Custom Channels)
|
|
1064
|
+
|
|
1065
|
+
For protocols that don’t support HTTP headers:
|
|
1066
|
+
|
|
1067
|
+
```typescript
|
|
1068
|
+
// Browser (sender)
|
|
1069
|
+
const traceData = Sentry.getTraceData();
|
|
1070
|
+
|
|
1071
|
+
socket.send(
|
|
1072
|
+
JSON.stringify({
|
|
1073
|
+
type: "rpc.updateProfile",
|
|
1074
|
+
payload: { name: "Alice" },
|
|
1075
|
+
_sentry: {
|
|
1076
|
+
trace: traceData["sentry-trace"],
|
|
1077
|
+
baggage: traceData["baggage"],
|
|
1078
|
+
},
|
|
1079
|
+
}),
|
|
1080
|
+
);
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
```typescript
|
|
1084
|
+
// Node.js server (receiver)
|
|
1085
|
+
import * as Sentry from "@sentry/node";
|
|
1086
|
+
import { propagation, context } from "@opentelemetry/api";
|
|
1087
|
+
|
|
1088
|
+
socket.on("message", (raw) => {
|
|
1089
|
+
const msg = JSON.parse(raw);
|
|
1090
|
+
const ctx = propagation.extract(context.active(), {
|
|
1091
|
+
"sentry-trace": msg._sentry.trace,
|
|
1092
|
+
baggage: msg._sentry.baggage,
|
|
1093
|
+
});
|
|
1094
|
+
context.with(ctx, () => {
|
|
1095
|
+
Sentry.startSpan({ name: "ws.updateProfile" }, () => handleMessage(msg));
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
### W3C `traceparent` Compatibility (SDK ≥10.10.0)
|
|
1101
|
+
|
|
1102
|
+
Add the W3C `traceparent` header alongside `sentry-trace` for OpenTelemetry-native
|
|
1103
|
+
backends:
|
|
1104
|
+
|
|
1105
|
+
```typescript
|
|
1106
|
+
Sentry.init({
|
|
1107
|
+
propagateTraceparent: true,
|
|
1108
|
+
});
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
---
|
|
1112
|
+
|
|
1113
|
+
## Sampling
|
|
1114
|
+
|
|
1115
|
+
### `tracesSampleRate` — Uniform Rate
|
|
1116
|
+
|
|
1117
|
+
```typescript
|
|
1118
|
+
Sentry.init({
|
|
1119
|
+
tracesSampleRate: 1.0, // 100% — dev / staging / low-traffic
|
|
1120
|
+
// tracesSampleRate: 0.2, // 20% — light production
|
|
1121
|
+
// tracesSampleRate: 0.05, // 5% — high-traffic production
|
|
1122
|
+
// tracesSampleRate: 0.01, // 1% — very high-traffic production
|
|
1123
|
+
});
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
### `tracesSampler` — Dynamic Per-Transaction
|
|
1127
|
+
|
|
1128
|
+
`tracesSampler` replaces `tracesSampleRate` (when both are set, `tracesSampler` wins):
|
|
1129
|
+
|
|
1130
|
+
```typescript
|
|
1131
|
+
Sentry.init({
|
|
1132
|
+
tracesSampler: ({ name, attributes, inheritOrSampleWith }) => {
|
|
1133
|
+
// Drop health checks and internal routes
|
|
1134
|
+
if (["/health", "/ping", "/readyz"].some((p) => name.includes(p))) return 0;
|
|
1135
|
+
|
|
1136
|
+
// Always capture critical flows
|
|
1137
|
+
if (name.startsWith("/checkout") || name.startsWith("/payment")) return 1.0;
|
|
1138
|
+
|
|
1139
|
+
// Sample admin routes at 50%
|
|
1140
|
+
if (name.startsWith("/admin")) return 0.5;
|
|
1141
|
+
|
|
1142
|
+
// High-volume search at 5%
|
|
1143
|
+
if (name.includes("/search")) return 0.05;
|
|
1144
|
+
|
|
1145
|
+
// For everything else: honor parent's decision, fall back to 10%
|
|
1146
|
+
return inheritOrSampleWith(0.1);
|
|
1147
|
+
},
|
|
1148
|
+
});
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### Full `samplingContext` Object
|
|
1152
|
+
|
|
1153
|
+
```typescript
|
|
1154
|
+
interface SamplingContext {
|
|
1155
|
+
name: string; // Span/transaction name (e.g. "GET /users/:id")
|
|
1156
|
+
attributes?: SpanAttributes; // Initial span attributes: op, url, http.method, etc.
|
|
1157
|
+
parentSampled?: boolean; // Was the parent trace sampled? undefined = no parent
|
|
1158
|
+
parentSampleRate?: number; // What rate was used upstream?
|
|
1159
|
+
inheritOrSampleWith: (fallbackRate: number) => number;
|
|
1160
|
+
}
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
### `inheritOrSampleWith` — Why It Matters
|
|
1164
|
+
|
|
1165
|
+
Use `inheritOrSampleWith(fallback)` instead of checking `parentSampled` directly.
|
|
1166
|
+
It enables:
|
|
1167
|
+
|
|
1168
|
+
- **Deterministic sampling:** the same rate decision is applied throughout the trace
|
|
1169
|
+
chain
|
|
1170
|
+
- **Accurate metric extrapolation:** Sentry’s performance metrics scale correctly only
|
|
1171
|
+
when consistent sample rates flow through all services
|
|
1172
|
+
- **Correct Sampled flag:** ensures the `sentry-sampled` value in downstream `baggage`
|
|
1173
|
+
matches the actual decision
|
|
1174
|
+
|
|
1175
|
+
### Returning Boolean vs Number
|
|
1176
|
+
|
|
1177
|
+
```typescript
|
|
1178
|
+
tracesSampler: ({ name }) => {
|
|
1179
|
+
if (name === "/critical") return true; // equivalent to 1.0
|
|
1180
|
+
if (name === "/noisy") return false; // equivalent to 0
|
|
1181
|
+
return 0.2;
|
|
1182
|
+
};
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
### Sampling Guidelines by Traffic Level
|
|
1186
|
+
|
|
1187
|
+
| Daily transactions | Recommended `tracesSampleRate` |
|
|
1188
|
+
| ------------------ | ------------------------------------------------------------------------ |
|
|
1189
|
+
| < 10K | `1.0` — capture everything |
|
|
1190
|
+
| 10K–100K | `0.2` — 20% |
|
|
1191
|
+
| 100K–1M | `0.05` – `0.1` |
|
|
1192
|
+
| > 1M | `0.01` – `0.02` with `tracesSampler` for priority routes at higher rates |
|
|
1193
|
+
|
|
1194
|
+
---
|
|
1195
|
+
|
|
1196
|
+
## Span Filtering
|
|
1197
|
+
|
|
1198
|
+
### `beforeSendTransaction` — Modify or Drop Whole Transactions
|
|
1199
|
+
|
|
1200
|
+
```typescript
|
|
1201
|
+
Sentry.init({
|
|
1202
|
+
beforeSendTransaction(event) {
|
|
1203
|
+
// Drop internal/dev routes
|
|
1204
|
+
if (event.transaction?.startsWith("/__internal")) return null;
|
|
1205
|
+
|
|
1206
|
+
// Scrub PII from transaction names
|
|
1207
|
+
if (event.transaction) {
|
|
1208
|
+
event.transaction = event.transaction.replace(/\/users\/[^/]+/, "/users/<redacted>");
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Add custom tags to every transaction
|
|
1212
|
+
event.tags = { ...event.tags, "app.build": BUILD_ID };
|
|
1213
|
+
|
|
1214
|
+
return event;
|
|
1215
|
+
},
|
|
1216
|
+
});
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
### `ignoreTransactions` — Declarative Transaction Filtering
|
|
1220
|
+
|
|
1221
|
+
```typescript
|
|
1222
|
+
Sentry.init({
|
|
1223
|
+
ignoreTransactions: [
|
|
1224
|
+
"/health", // string = substring match
|
|
1225
|
+
/^\/api\/internal/, // regex = full URL test
|
|
1226
|
+
"/__webpack_hmr",
|
|
1227
|
+
/\.(png|jpg|svg|ico|woff2)$/, // static assets
|
|
1228
|
+
],
|
|
1229
|
+
});
|
|
1230
|
+
```
|
|
1231
|
+
|
|
1232
|
+
### `beforeSendSpan` — Modify Individual Spans
|
|
1233
|
+
|
|
1234
|
+
> `beforeSendSpan` **cannot drop spans** — it can only modify them.
|
|
1235
|
+
> To suppress spans, use `ignoreSpans` (SDK ≥10.2.0).
|
|
1236
|
+
|
|
1237
|
+
```typescript
|
|
1238
|
+
Sentry.init({
|
|
1239
|
+
beforeSendSpan(span) {
|
|
1240
|
+
// Redact token from span descriptions
|
|
1241
|
+
if (span.op === "http.client" && span.description?.includes("/token")) {
|
|
1242
|
+
span.description = span.description.replace(/token=[^&]+/, "token=REDACTED");
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Enrich all spans with deployment info
|
|
1246
|
+
span.data = {
|
|
1247
|
+
...span.data,
|
|
1248
|
+
"deployment.region": import.meta.env.VITE_AWS_REGION ?? "unknown",
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
return span; // must return span — never return null
|
|
1252
|
+
},
|
|
1253
|
+
});
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
### `ignoreSpans` — Declarative Span Filtering (SDK ≥10.2.0)
|
|
1257
|
+
|
|
1258
|
+
```typescript
|
|
1259
|
+
Sentry.init({
|
|
1260
|
+
ignoreSpans: [
|
|
1261
|
+
// String — matches against span name/description
|
|
1262
|
+
"font-load",
|
|
1263
|
+
|
|
1264
|
+
// Regex against span name
|
|
1265
|
+
/^performance\.mark\./,
|
|
1266
|
+
|
|
1267
|
+
// Object — filter by op only
|
|
1268
|
+
{ op: "resource.script" },
|
|
1269
|
+
{ op: "resource.img" },
|
|
1270
|
+
{ op: "resource.css" },
|
|
1271
|
+
|
|
1272
|
+
// Object — filter by name and op together
|
|
1273
|
+
{ name: "beacon", op: "http.client" },
|
|
1274
|
+
|
|
1275
|
+
// Object — name regex
|
|
1276
|
+
{ name: /^(hotjar|analytics|gtag)/ },
|
|
1277
|
+
|
|
1278
|
+
// Object — filter by span attributes (SDK ≥10.6.0)
|
|
1279
|
+
// String attribute values use substring/RegExp matching; non-strings use strict equality
|
|
1280
|
+
{
|
|
1281
|
+
attributes: {
|
|
1282
|
+
"url.path": "/health", // substring match
|
|
1283
|
+
"http.response.status_code": 200, // strict equality
|
|
1284
|
+
},
|
|
1285
|
+
},
|
|
1286
|
+
],
|
|
1287
|
+
});
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
> **Warning:** If the root span (the transaction itself) matches an `ignoreSpans` rule,
|
|
1291
|
+
> the **entire local trace is dropped**.
|
|
1292
|
+
|
|
1293
|
+
---
|
|
1294
|
+
|
|
1295
|
+
## Custom Routing (Manual Spans)
|
|
1296
|
+
|
|
1297
|
+
For unsupported or custom routers, disable auto page spans and drive them yourself:
|
|
1298
|
+
|
|
1299
|
+
```typescript
|
|
1300
|
+
import * as Sentry from "@sentry/react";
|
|
1301
|
+
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from "@sentry/react";
|
|
1302
|
+
|
|
1303
|
+
const client = Sentry.init({
|
|
1304
|
+
dsn: "...",
|
|
1305
|
+
integrations: [
|
|
1306
|
+
Sentry.browserTracingIntegration({
|
|
1307
|
+
instrumentPageLoad: false, // handled manually
|
|
1308
|
+
instrumentNavigation: false, // handled manually
|
|
1309
|
+
}),
|
|
1310
|
+
],
|
|
1311
|
+
tracesSampleRate: 1.0,
|
|
1312
|
+
})!;
|
|
1313
|
+
|
|
1314
|
+
// Initial page load — name with URL until route is matched
|
|
1315
|
+
let pageLoadSpan = Sentry.startBrowserTracingPageLoadSpan(client, {
|
|
1316
|
+
name: window.location.pathname,
|
|
1317
|
+
attributes: {
|
|
1318
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url", // start with "url" source
|
|
1319
|
+
},
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// Once the router resolves the matched route
|
|
1323
|
+
myCustomRouter.on("routeResolved", (route) => {
|
|
1324
|
+
if (pageLoadSpan) {
|
|
1325
|
+
// Upgrade the pageload span's name to the parameterized pattern
|
|
1326
|
+
pageLoadSpan.updateName(route.pattern); // e.g. "/users/:id"
|
|
1327
|
+
pageLoadSpan.setAttribute(
|
|
1328
|
+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
|
|
1329
|
+
"route", // upgrade to "route" source
|
|
1330
|
+
);
|
|
1331
|
+
pageLoadSpan = undefined;
|
|
1332
|
+
} else {
|
|
1333
|
+
// Subsequent navigations
|
|
1334
|
+
Sentry.startBrowserTracingNavigationSpan(client, {
|
|
1335
|
+
op: "navigation",
|
|
1336
|
+
name: route.pattern,
|
|
1337
|
+
attributes: {
|
|
1338
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
1339
|
+
},
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
Both functions create **idle spans** — they close automatically after `idleTimeout`ms of
|
|
1346
|
+
no new child activity, matching the behavior of automatic pageload/navigation spans.
|
|
1347
|
+
|
|
1348
|
+
---
|
|
1349
|
+
|
|
1350
|
+
## Full Import Reference
|
|
1351
|
+
|
|
1352
|
+
```typescript
|
|
1353
|
+
import * as Sentry from "@sentry/react";
|
|
1354
|
+
|
|
1355
|
+
// ── Integrations ──────────────────────────────────────────────────────────
|
|
1356
|
+
Sentry.browserTracingIntegration(options);
|
|
1357
|
+
Sentry.reactRouterV7BrowserTracingIntegration(options);
|
|
1358
|
+
Sentry.reactRouterV6BrowserTracingIntegration(options);
|
|
1359
|
+
Sentry.reactRouterV5BrowserTracingIntegration(options);
|
|
1360
|
+
Sentry.reactRouterV4BrowserTracingIntegration(options);
|
|
1361
|
+
Sentry.tanstackRouterBrowserTracingIntegration(router);
|
|
1362
|
+
|
|
1363
|
+
// ── Router Wrappers — v7 ─────────────────────────────────────────────────
|
|
1364
|
+
Sentry.wrapCreateBrowserRouterV7(createBrowserRouter);
|
|
1365
|
+
Sentry.wrapCreateMemoryRouterV7(createMemoryRouter);
|
|
1366
|
+
Sentry.withSentryReactRouterV7Routing(Routes);
|
|
1367
|
+
Sentry.wrapUseRoutesV7(useRoutes);
|
|
1368
|
+
|
|
1369
|
+
// ── Router Wrappers — v6 ─────────────────────────────────────────────────
|
|
1370
|
+
Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
|
|
1371
|
+
Sentry.wrapCreateMemoryRouterV6(createMemoryRouter); // SDK ≥8.50.0
|
|
1372
|
+
Sentry.withSentryReactRouterV6Routing(Routes);
|
|
1373
|
+
Sentry.wrapUseRoutesV6(useRoutes);
|
|
1374
|
+
|
|
1375
|
+
// ── Router Wrappers — v5/v4 ──────────────────────────────────────────────
|
|
1376
|
+
Sentry.withSentryRouting(Route);
|
|
1377
|
+
|
|
1378
|
+
// ── Spans ────────────────────────────────────────────────────────────────
|
|
1379
|
+
Sentry.startSpan(options, callback);
|
|
1380
|
+
Sentry.startSpanManual(options, callback);
|
|
1381
|
+
Sentry.startInactiveSpan(options);
|
|
1382
|
+
Sentry.getActiveSpan();
|
|
1383
|
+
|
|
1384
|
+
// ── Custom Browser Tracing ───────────────────────────────────────────────
|
|
1385
|
+
Sentry.startBrowserTracingPageLoadSpan(client, options);
|
|
1386
|
+
Sentry.startBrowserTracingNavigationSpan(client, options);
|
|
1387
|
+
|
|
1388
|
+
// ── Distributed Tracing ──────────────────────────────────────────────────
|
|
1389
|
+
Sentry.getTraceData();
|
|
1390
|
+
// Returns: { "sentry-trace": string, baggage: string }
|
|
1391
|
+
|
|
1392
|
+
// ── Constants ────────────────────────────────────────────────────────────
|
|
1393
|
+
Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE; // "sentry.source"
|
|
1394
|
+
Sentry.SEMANTIC_ATTRIBUTE_SENTRY_OP; // "sentry.op"
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
---
|
|
1398
|
+
|
|
1399
|
+
## Troubleshooting
|
|
1400
|
+
|
|
1401
|
+
| Issue | Solution |
|
|
1402
|
+
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
1403
|
+
| No transactions in Performance dashboard | Verify `tracesSampleRate` > 0; confirm `browserTracingIntegration()` (or router variant) is in `integrations` array |
|
|
1404
|
+
| Transaction names show raw URLs (`/users/42`) instead of patterns | Add router integration matching your router version; ensure it’s replacing, not supplementing, `browserTracingIntegration()` |
|
|
1405
|
+
| Transaction named `<unknown>` | Router integration is missing or misconfigured; check `useEffect`, `useLocation`, `useNavigationType` are all passed correctly |
|
|
1406
|
+
| Distributed trace not linking frontend → backend | Add backend URL to `tracePropagationTargets`; verify `Access-Control-Allow-Headers` includes `sentry-trace, baggage` |
|
|
1407
|
+
| SSR page load not linked to server trace | Inject `<meta name="sentry-trace">` and `<meta name="baggage">` tags from `Sentry.getTraceData()` in server-rendered HTML |
|
|
1408
|
+
| API requests missing `sentry-trace` header | Check CORS preflight — backend must allow `sentry-trace` and `baggage` headers |
|
|
1409
|
+
| INP spans not appearing | In SDK 7.x, enable explicitly: `browserTracingIntegration({ enableInp: true })` |
|
|
1410
|
+
| Web Vitals missing | Confirm `browserTracingIntegration()` is in client init; check browser support (INP requires Chromium 96+) |
|
|
1411
|
+
| Spans missing after async gap | Browser uses flat hierarchy; use `startInactiveSpan` with explicit `parentSpan` to enforce parent-child across async boundaries |
|
|
1412
|
+
| High transaction volume / cost | Use `tracesSampler` to return `0` for health checks and asset routes; lower default rate with `inheritOrSampleWith(0.05)` |
|
|
1413
|
+
| `beforeSendSpan` returning `null` breaks the SDK | `beforeSendSpan` must always return the span — use `ignoreSpans` to drop spans declaratively |
|
|
1414
|
+
| Lazy routes not tracked | Upgrade to SDK ≥10.39.0; add `enableAsyncRouteHandlers: true` and `lazyRouteManifest` with all route paths |
|
|
1415
|
+
| TanStack Router transactions missing | Ensure router is created **before** `Sentry.init()` is called and the router instance is passed to the integration |
|