@sentry/react 11.0.0-alpha.1 → 11.0.0-beta.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/build/cjs/error.js +4 -4
- package/build/cjs/error.js.map +1 -1
- package/build/cjs/profiler.js +10 -13
- package/build/cjs/profiler.js.map +1 -1
- package/build/cjs/reactrouter-compat-utils/instrumentation.js +30 -18
- package/build/cjs/reactrouter-compat-utils/instrumentation.js.map +1 -1
- package/build/cjs/reactrouter-compat-utils/route-manifest.js +4 -4
- package/build/cjs/reactrouter-compat-utils/route-manifest.js.map +1 -1
- package/build/cjs/reactrouter-compat-utils/utils.js +5 -5
- package/build/cjs/reactrouter-compat-utils/utils.js.map +1 -1
- package/build/cjs/reactrouter.compat.js +0 -3
- package/build/cjs/reactrouter.compat.js.map +1 -1
- package/build/cjs/reactrouter.js +10 -7
- package/build/cjs/reactrouter.js.map +1 -1
- package/build/cjs/reactrouterv3.js +12 -9
- package/build/cjs/reactrouterv3.js.map +1 -1
- package/build/cjs/reactrouterv6.js +0 -3
- package/build/cjs/reactrouterv6.js.map +1 -1
- package/build/cjs/reactrouterv7.js +0 -3
- package/build/cjs/reactrouterv7.js.map +1 -1
- package/build/cjs/redux.js +6 -6
- package/build/cjs/redux.js.map +1 -1
- package/build/cjs/sdk.js +7 -7
- package/build/cjs/sdk.js.map +1 -1
- package/build/cjs/tanstackrouter.js +25 -14
- package/build/cjs/tanstackrouter.js.map +1 -1
- package/build/esm/error.js +1 -1
- package/build/esm/error.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/profiler.js +12 -15
- package/build/esm/profiler.js.map +1 -1
- package/build/esm/reactrouter-compat-utils/instrumentation.js +32 -20
- package/build/esm/reactrouter-compat-utils/instrumentation.js.map +1 -1
- package/build/esm/reactrouter-compat-utils/route-manifest.js +1 -1
- package/build/esm/reactrouter-compat-utils/route-manifest.js.map +1 -1
- package/build/esm/reactrouter-compat-utils/utils.js +1 -1
- package/build/esm/reactrouter-compat-utils/utils.js.map +1 -1
- package/build/esm/reactrouter.compat.js +0 -3
- package/build/esm/reactrouter.compat.js.map +1 -1
- package/build/esm/reactrouter.js +12 -9
- package/build/esm/reactrouter.js.map +1 -1
- package/build/esm/reactrouterv3.js +11 -8
- package/build/esm/reactrouterv3.js.map +1 -1
- package/build/esm/reactrouterv6.js +0 -3
- package/build/esm/reactrouterv6.js.map +1 -1
- package/build/esm/reactrouterv7.js +0 -3
- package/build/esm/reactrouterv7.js.map +1 -1
- package/build/esm/redux.js +1 -1
- package/build/esm/redux.js.map +1 -1
- package/build/esm/sdk.js +1 -1
- package/build/esm/sdk.js.map +1 -1
- package/build/esm/tanstackrouter.js +25 -14
- package/build/esm/tanstackrouter.js.map +1 -1
- package/build/types/profiler.d.ts.map +1 -1
- package/build/types/reactrouter-compat-utils/instrumentation.d.ts.map +1 -1
- package/build/types/reactrouter-compat-utils/utils.d.ts +1 -1
- package/build/types/reactrouter-compat-utils/utils.d.ts.map +1 -1
- package/build/types/reactrouter.d.ts.map +1 -1
- package/build/types/reactrouterv3.d.ts +1 -1
- package/build/types/reactrouterv3.d.ts.map +1 -1
- package/build/types/redux.d.ts +1 -1
- package/build/types/redux.d.ts.map +1 -1
- package/build/types/sdk.d.ts +1 -1
- package/build/types/sdk.d.ts.map +1 -1
- package/build/types/tanstackrouter.d.ts +1 -1
- package/build/types/tanstackrouter.d.ts.map +1 -1
- package/build/types/vendor/tanstackrouter-types.d.ts +1 -0
- package/build/types/vendor/tanstackrouter-types.d.ts.map +1 -1
- package/package.json +5 -4
|
@@ -2,8 +2,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const browser = require('@sentry/browser');
|
|
4
4
|
const core = require('@sentry/core');
|
|
5
|
-
const browser$1 = require('@sentry/core/browser');
|
|
6
5
|
const attributes = require('@sentry/conventions/attributes');
|
|
6
|
+
const op = require('@sentry/conventions/op');
|
|
7
7
|
|
|
8
8
|
function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
9
9
|
const castRouterInstance = router;
|
|
@@ -27,7 +27,7 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
27
27
|
};
|
|
28
28
|
const applyRouteMatch = (span, match, toLocation, fallbackName) => {
|
|
29
29
|
span.updateName(match ? match.routeId : fallbackName);
|
|
30
|
-
span.setAttribute(
|
|
30
|
+
span.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, match ? "route" : "url");
|
|
31
31
|
span.setAttributes({
|
|
32
32
|
...match && { [attributes.URL_TEMPLATE]: match.routeId },
|
|
33
33
|
...locationToSpanUrlAttributes(castRouterInstance, toLocation),
|
|
@@ -36,16 +36,18 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
36
36
|
};
|
|
37
37
|
const initialWindowLocation = browser.WINDOW.location;
|
|
38
38
|
if (instrumentPageLoad && initialWindowLocation) {
|
|
39
|
-
const
|
|
39
|
+
const initialRouterLocation = castRouterInstance.state?.location;
|
|
40
|
+
const routeMatch = initialRouterLocation ? resolveRouteMatch(initialRouterLocation.pathname, initialRouterLocation.search) : resolveRouteMatch(
|
|
40
41
|
initialWindowLocation.pathname,
|
|
41
42
|
castRouterInstance.options.parseSearch(initialWindowLocation.search)
|
|
42
43
|
);
|
|
43
44
|
const pageloadSpan = browser.startBrowserTracingPageLoadSpan(client, {
|
|
44
|
-
|
|
45
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to the URL.
|
|
46
|
+
name: routeMatch ? routeMatch.routeId : core.hasSpanStreamingEnabled(client) ? core.PAGELOAD_SPAN_NAME_FALLBACK : initialWindowLocation.pathname,
|
|
45
47
|
attributes: {
|
|
46
|
-
[
|
|
47
|
-
[
|
|
48
|
-
[
|
|
48
|
+
[attributes.SENTRY_OP]: op.PAGELOAD,
|
|
49
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.react.tanstack_router",
|
|
50
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: routeMatch ? "route" : "url",
|
|
49
51
|
...routeMatch && { [attributes.URL_TEMPLATE]: routeMatch.routeId },
|
|
50
52
|
...routeMatchToParamSpanAttributes(routeMatch)
|
|
51
53
|
}
|
|
@@ -57,7 +59,12 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
57
59
|
}
|
|
58
60
|
const { toLocation } = onResolvedArgs;
|
|
59
61
|
const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
|
|
60
|
-
applyRouteMatch(
|
|
62
|
+
applyRouteMatch(
|
|
63
|
+
pageloadSpan,
|
|
64
|
+
resolvedMatch,
|
|
65
|
+
toLocation,
|
|
66
|
+
core.hasSpanStreamingEnabled(client) ? core.PAGELOAD_SPAN_NAME_FALLBACK : toLocation.pathname
|
|
67
|
+
);
|
|
61
68
|
});
|
|
62
69
|
}
|
|
63
70
|
if (instrumentNavigation) {
|
|
@@ -68,7 +75,7 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
68
75
|
return;
|
|
69
76
|
}
|
|
70
77
|
const routeMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
|
|
71
|
-
const fallbackName = browser.WINDOW.location?.pathname || toLocation.pathname;
|
|
78
|
+
const fallbackName = core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : browser.WINDOW.location?.pathname || toLocation.pathname;
|
|
72
79
|
if (inFlightNavigationSpan) {
|
|
73
80
|
applyRouteMatch(inFlightNavigationSpan, routeMatch, toLocation, fallbackName);
|
|
74
81
|
return;
|
|
@@ -78,9 +85,9 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
78
85
|
{
|
|
79
86
|
name: routeMatch ? routeMatch.routeId : fallbackName,
|
|
80
87
|
attributes: {
|
|
81
|
-
[
|
|
82
|
-
[
|
|
83
|
-
[
|
|
88
|
+
[attributes.SENTRY_OP]: op.NAVIGATION,
|
|
89
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react.tanstack_router",
|
|
90
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: routeMatch ? "route" : "url",
|
|
84
91
|
...routeMatch && { [attributes.URL_TEMPLATE]: routeMatch.routeId },
|
|
85
92
|
...routeMatchToParamSpanAttributes(routeMatch)
|
|
86
93
|
}
|
|
@@ -97,7 +104,12 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
|
|
|
97
104
|
const { toLocation } = onResolvedArgs;
|
|
98
105
|
const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
|
|
99
106
|
if (resolvedMatch) {
|
|
100
|
-
applyRouteMatch(
|
|
107
|
+
applyRouteMatch(
|
|
108
|
+
span,
|
|
109
|
+
resolvedMatch,
|
|
110
|
+
toLocation,
|
|
111
|
+
core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : browser.WINDOW.location?.pathname || toLocation.pathname
|
|
112
|
+
);
|
|
101
113
|
}
|
|
102
114
|
});
|
|
103
115
|
}
|
|
@@ -122,7 +134,6 @@ function routeMatchToParamSpanAttributes(match) {
|
|
|
122
134
|
}
|
|
123
135
|
const paramAttributes = {};
|
|
124
136
|
Object.entries(match.params).forEach(([key, value]) => {
|
|
125
|
-
paramAttributes[`url.path.params.${key}`] = value;
|
|
126
137
|
paramAttributes[`${attributes.URL_PATH_PARAMETER_KEY_BASE}.${key}`] = value;
|
|
127
138
|
paramAttributes[`${attributes.PARAMS_KEY_BASE}.${key}`] = value;
|
|
128
139
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstackrouter.js","sources":["../../src/tanstackrouter.ts"],"sourcesContent":["import {\n browserTracingIntegration as originalBrowserTracingIntegration,\n getAbsoluteUrl,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration } from '@sentry/core/browser';\nimport { filterCollectedUrl } from '@sentry/core';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core/browser';\nimport type { VendoredTanstackRouter, VendoredTanstackRouterRouteMatch } from './vendor/tanstackrouter-types';\nimport {\n PARAMS_KEY_BASE,\n URL_FULL,\n URL_PATH,\n URL_PATH_PARAMETER_KEY_BASE,\n URL_TEMPLATE,\n} from '@sentry/conventions/attributes';\n\ninterface TanstackRouterLocation {\n pathname: string;\n search: Record<string, unknown>;\n state?: unknown;\n}\n\n/**\n * A custom browser tracing integration for TanStack Router.\n *\n * The minimum compatible version of `@tanstack/react-router` is `1.64.0`.\n *\n * @param router A TanStack Router `Router` instance that should be used for routing instrumentation.\n * @param options Sentry browser tracing configuration.\n */\nexport function tanstackRouterBrowserTracingIntegration(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n router: any, // This is `any` because we don't want any type mismatches if TanStack Router changes their types\n options: Parameters<typeof originalBrowserTracingIntegration>[0] = {},\n): Integration {\n const castRouterInstance: VendoredTanstackRouter = router;\n\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n ...options,\n instrumentNavigation: false,\n instrumentPageLoad: false,\n });\n\n const { instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...browserTracingIntegrationInstance,\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n\n const resolveRouteMatch = (pathname: string, search: unknown): VendoredTanstackRouterRouteMatch | undefined => {\n const matchedRoutes = castRouterInstance.matchRoutes(pathname, search as {}, {\n preload: false,\n throwOnError: false,\n });\n const lastMatch = matchedRoutes[matchedRoutes.length - 1];\n // If we only match __root__, we ended up not matching any route at all, so\n // we fall back to the pathname.\n return lastMatch?.routeId !== '__root__' ? lastMatch : undefined;\n };\n\n const applyRouteMatch = (\n span: NonNullable<ReturnType<typeof startBrowserTracingPageLoadSpan>>,\n match: VendoredTanstackRouterRouteMatch | undefined,\n toLocation: TanstackRouterLocation,\n fallbackName: string,\n ): void => {\n span.updateName(match ? match.routeId : fallbackName);\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, match ? 'route' : 'url');\n span.setAttributes({\n ...(match && { [URL_TEMPLATE]: match.routeId }),\n ...locationToSpanUrlAttributes(castRouterInstance, toLocation),\n ...routeMatchToParamSpanAttributes(match),\n });\n };\n\n const initialWindowLocation = WINDOW.location;\n if (instrumentPageLoad && initialWindowLocation) {\n const routeMatch = resolveRouteMatch(\n initialWindowLocation.pathname,\n castRouterInstance.options.parseSearch(initialWindowLocation.search),\n );\n\n const pageloadSpan = startBrowserTracingPageLoadSpan(client, {\n name: routeMatch ? routeMatch.routeId : initialWindowLocation.pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n });\n\n // A redirect thrown during the initial pageload leaves the span named after the pre-redirect\n // route, so correct it to the resolved route once.\n const unsubscribePageloadResolved = castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n unsubscribePageloadResolved();\n if (!pageloadSpan) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n applyRouteMatch(pageloadSpan, resolvedMatch, toLocation, toLocation.pathname);\n });\n }\n\n if (instrumentNavigation) {\n // Navigation is driven by `onBeforeLoad` (accurate start) + `onResolved` (final route), not\n // `onBeforeNavigate`, which TanStack stops firing after any loader redirect (TanStack/router#3920).\n // A redirect chain emits one `onBeforeLoad` per load but a single `onResolved`, so we start the\n // span on the first `onBeforeLoad`, rename it on later ones, and clear it on `onResolved`.\n let inFlightNavigationSpan: ReturnType<typeof startBrowserTracingNavigationSpan> | undefined;\n\n castRouterInstance.subscribe('onBeforeLoad', onBeforeLoadArgs => {\n const { toLocation, fromLocation } = onBeforeLoadArgs;\n // Skip the initial pageload (no fromLocation) and no-op reloads (same state).\n if (!fromLocation || toLocation.state === fromLocation.state) {\n return;\n }\n\n const routeMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n const fallbackName = WINDOW.location?.pathname || toLocation.pathname;\n\n if (inFlightNavigationSpan) {\n // Redirect continuation within the same navigation: keep the span, update the target.\n applyRouteMatch(inFlightNavigationSpan, routeMatch, toLocation, fallbackName);\n return;\n }\n\n inFlightNavigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n name: routeMatch ? routeMatch.routeId : fallbackName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n },\n { url: locationToAbsoluteUrl(castRouterInstance, toLocation) },\n );\n });\n\n castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n const span = inFlightNavigationSpan;\n inFlightNavigationSpan = undefined;\n if (!span) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n if (resolvedMatch) {\n applyRouteMatch(span, resolvedMatch, toLocation, WINDOW.location?.pathname || toLocation.pathname);\n }\n });\n }\n },\n };\n}\n\nfunction locationToAbsoluteUrl(router: VendoredTanstackRouter, location: TanstackRouterLocation): string {\n const search = router.options.stringifySearch?.(location.search) ?? '';\n const pathWithSearch = `${location.pathname}${search && search !== '?' ? search : ''}`;\n\n return getAbsoluteUrl(pathWithSearch);\n}\n\nfunction locationToSpanUrlAttributes(\n router: VendoredTanstackRouter,\n location: TanstackRouterLocation,\n): Record<string, string> {\n const absoluteUrl = locationToAbsoluteUrl(router, location);\n\n return {\n [URL_PATH]: location.pathname,\n [URL_FULL]: filterCollectedUrl(absoluteUrl),\n };\n}\n\nfunction routeMatchToParamSpanAttributes(match: VendoredTanstackRouterRouteMatch | undefined): Record<string, string> {\n if (!match) {\n return {};\n }\n\n const paramAttributes: Record<string, string> = {};\n Object.entries(match.params).forEach(([key, value]) => {\n paramAttributes[`url.path.params.${key}`] = value; // TODO(v11): remove attribute which does not adhere to Sentry's semantic convention\n paramAttributes[`${URL_PATH_PARAMETER_KEY_BASE}.${key}`] = value;\n paramAttributes[`${PARAMS_KEY_BASE}.${key}`] = value; // params.[key] is an alias\n });\n\n return paramAttributes;\n}\n"],"names":["originalBrowserTracingIntegration","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","URL_TEMPLATE","WINDOW","startBrowserTracingPageLoadSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startBrowserTracingNavigationSpan","getAbsoluteUrl","URL_PATH","URL_FULL","filterCollectedUrl","URL_PATH_PARAMETER_KEY_BASE","PARAMS_KEY_BASE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tanstackrouter.js","sources":["../../src/tanstackrouter.ts"],"sourcesContent":["import {\n browserTracingIntegration as originalBrowserTracingIntegration,\n getAbsoluteUrl,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration } from '@sentry/core';\nimport {\n filterCollectedUrl,\n hasSpanStreamingEnabled,\n NAVIGATION_SPAN_NAME_FALLBACK,\n PAGELOAD_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport type { VendoredTanstackRouter, VendoredTanstackRouterRouteMatch } from './vendor/tanstackrouter-types';\nimport {\n PARAMS_KEY_BASE,\n SENTRY_OP,\n SENTRY_SEGMENT_NAME_SOURCE,\n URL_FULL,\n URL_PATH,\n URL_PATH_PARAMETER_KEY_BASE,\n URL_TEMPLATE,\n} from '@sentry/conventions/attributes';\nimport { NAVIGATION, PAGELOAD } from '@sentry/conventions/op';\n\ninterface TanstackRouterLocation {\n pathname: string;\n search: Record<string, unknown>;\n state?: unknown;\n}\n\n/**\n * A custom browser tracing integration for TanStack Router.\n *\n * The minimum compatible version of `@tanstack/react-router` is `1.64.0`.\n *\n * @param router A TanStack Router `Router` instance that should be used for routing instrumentation.\n * @param options Sentry browser tracing configuration.\n */\nexport function tanstackRouterBrowserTracingIntegration(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n router: any, // This is `any` because we don't want any type mismatches if TanStack Router changes their types\n options: Parameters<typeof originalBrowserTracingIntegration>[0] = {},\n): Integration {\n const castRouterInstance: VendoredTanstackRouter = router;\n\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n ...options,\n instrumentNavigation: false,\n instrumentPageLoad: false,\n });\n\n const { instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...browserTracingIntegrationInstance,\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n\n const resolveRouteMatch = (pathname: string, search: unknown): VendoredTanstackRouterRouteMatch | undefined => {\n const matchedRoutes = castRouterInstance.matchRoutes(pathname, search as {}, {\n preload: false,\n throwOnError: false,\n });\n const lastMatch = matchedRoutes[matchedRoutes.length - 1];\n // If we only match __root__, we ended up not matching any route at all, so\n // we fall back to the pathname.\n return lastMatch?.routeId !== '__root__' ? lastMatch : undefined;\n };\n\n const applyRouteMatch = (\n span: NonNullable<ReturnType<typeof startBrowserTracingPageLoadSpan>>,\n match: VendoredTanstackRouterRouteMatch | undefined,\n toLocation: TanstackRouterLocation,\n fallbackName: string,\n ): void => {\n span.updateName(match ? match.routeId : fallbackName);\n span.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, match ? 'route' : 'url');\n span.setAttributes({\n ...(match && { [URL_TEMPLATE]: match.routeId }),\n ...locationToSpanUrlAttributes(castRouterInstance, toLocation),\n ...routeMatchToParamSpanAttributes(match),\n });\n };\n\n const initialWindowLocation = WINDOW.location;\n if (instrumentPageLoad && initialWindowLocation) {\n const initialRouterLocation = castRouterInstance.state?.location;\n const routeMatch = initialRouterLocation\n ? resolveRouteMatch(initialRouterLocation.pathname, initialRouterLocation.search)\n : resolveRouteMatch(\n initialWindowLocation.pathname,\n castRouterInstance.options.parseSearch(initialWindowLocation.search),\n );\n\n const pageloadSpan = startBrowserTracingPageLoadSpan(client, {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n name: routeMatch\n ? routeMatch.routeId\n : hasSpanStreamingEnabled(client)\n ? PAGELOAD_SPAN_NAME_FALLBACK\n : initialWindowLocation.pathname,\n attributes: {\n [SENTRY_OP]: PAGELOAD,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.tanstack_router',\n [SENTRY_SEGMENT_NAME_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n });\n\n // A redirect thrown during the initial pageload leaves the span named after the pre-redirect\n // route, so correct it to the resolved route once.\n const unsubscribePageloadResolved = castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n unsubscribePageloadResolved();\n if (!pageloadSpan) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n applyRouteMatch(\n pageloadSpan,\n resolvedMatch,\n toLocation,\n hasSpanStreamingEnabled(client) ? PAGELOAD_SPAN_NAME_FALLBACK : toLocation.pathname,\n );\n });\n }\n\n if (instrumentNavigation) {\n // Navigation is driven by `onBeforeLoad` (accurate start) + `onResolved` (final route), not\n // `onBeforeNavigate`, which TanStack stops firing after any loader redirect (TanStack/router#3920).\n // A redirect chain emits one `onBeforeLoad` per load but a single `onResolved`, so we start the\n // span on the first `onBeforeLoad`, rename it on later ones, and clear it on `onResolved`.\n let inFlightNavigationSpan: ReturnType<typeof startBrowserTracingNavigationSpan> | undefined;\n\n castRouterInstance.subscribe('onBeforeLoad', onBeforeLoadArgs => {\n const { toLocation, fromLocation } = onBeforeLoadArgs;\n // Skip the initial pageload (no fromLocation) and no-op reloads (same state).\n if (!fromLocation || toLocation.state === fromLocation.state) {\n return;\n }\n\n const routeMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n const fallbackName = hasSpanStreamingEnabled(client)\n ? NAVIGATION_SPAN_NAME_FALLBACK\n : WINDOW.location?.pathname || toLocation.pathname;\n\n if (inFlightNavigationSpan) {\n // Redirect continuation within the same navigation: keep the span, update the target.\n applyRouteMatch(inFlightNavigationSpan, routeMatch, toLocation, fallbackName);\n return;\n }\n\n inFlightNavigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n name: routeMatch ? routeMatch.routeId : fallbackName,\n attributes: {\n [SENTRY_OP]: NAVIGATION,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.tanstack_router',\n [SENTRY_SEGMENT_NAME_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n },\n { url: locationToAbsoluteUrl(castRouterInstance, toLocation) },\n );\n });\n\n castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n const span = inFlightNavigationSpan;\n inFlightNavigationSpan = undefined;\n if (!span) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n if (resolvedMatch) {\n applyRouteMatch(\n span,\n resolvedMatch,\n toLocation,\n hasSpanStreamingEnabled(client)\n ? NAVIGATION_SPAN_NAME_FALLBACK\n : WINDOW.location?.pathname || toLocation.pathname,\n );\n }\n });\n }\n },\n };\n}\n\nfunction locationToAbsoluteUrl(router: VendoredTanstackRouter, location: TanstackRouterLocation): string {\n const search = router.options.stringifySearch?.(location.search) ?? '';\n const pathWithSearch = `${location.pathname}${search && search !== '?' ? search : ''}`;\n\n return getAbsoluteUrl(pathWithSearch);\n}\n\nfunction locationToSpanUrlAttributes(\n router: VendoredTanstackRouter,\n location: TanstackRouterLocation,\n): Record<string, string> {\n const absoluteUrl = locationToAbsoluteUrl(router, location);\n\n return {\n [URL_PATH]: location.pathname,\n [URL_FULL]: filterCollectedUrl(absoluteUrl),\n };\n}\n\nfunction routeMatchToParamSpanAttributes(match: VendoredTanstackRouterRouteMatch | undefined): Record<string, string> {\n if (!match) {\n return {};\n }\n\n const paramAttributes: Record<string, string> = {};\n Object.entries(match.params).forEach(([key, value]) => {\n paramAttributes[`${URL_PATH_PARAMETER_KEY_BASE}.${key}`] = value;\n paramAttributes[`${PARAMS_KEY_BASE}.${key}`] = value; // params.[key] is an alias\n });\n\n return paramAttributes;\n}\n"],"names":["originalBrowserTracingIntegration","SENTRY_SEGMENT_NAME_SOURCE","URL_TEMPLATE","WINDOW","startBrowserTracingPageLoadSpan","hasSpanStreamingEnabled","PAGELOAD_SPAN_NAME_FALLBACK","SENTRY_OP","PAGELOAD","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","NAVIGATION_SPAN_NAME_FALLBACK","startBrowserTracingNavigationSpan","NAVIGATION","getAbsoluteUrl","URL_PATH","URL_FULL","filterCollectedUrl","URL_PATH_PARAMETER_KEY_BASE","PARAMS_KEY_BASE"],"mappings":"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"}
|
package/build/esm/error.js
CHANGED
package/build/esm/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sources":["../../src/error.ts"],"sourcesContent":["import { captureException } from '@sentry/browser';\nimport { isError } from '@sentry/core
|
|
1
|
+
{"version":3,"file":"error.js","sources":["../../src/error.ts"],"sourcesContent":["import { captureException } from '@sentry/browser';\nimport { isError } from '@sentry/core';\nimport type { ErrorInfo } from 'react';\n\n/**\n * Recurse through `error.cause` chain to set cause on an error.\n */\nexport function setCause(error: Error & { cause?: Error }, cause: Error): void {\n const seenErrors = new WeakSet();\n\n function recurse(error: Error & { cause?: Error }, cause: Error): void {\n // If we've already seen the error, there is a recursive loop somewhere in the error's\n // cause chain. Let's just bail out then to prevent a stack overflow.\n if (seenErrors.has(error)) {\n return;\n }\n if (error.cause) {\n seenErrors.add(error);\n return recurse(error.cause, cause);\n }\n error.cause = cause;\n }\n\n recurse(error, cause);\n}\n\n/**\n * Captures an error that was thrown by a React ErrorBoundary or React root.\n *\n * @param error The error to capture.\n * @param errorInfo The errorInfo provided by React.\n * @param hint Optional additional data to attach to the Sentry event.\n * @returns the id of the captured Sentry event.\n */\nexport function captureReactException(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n error: any,\n { componentStack }: ErrorInfo,\n hint?: Parameters<typeof captureException>[1],\n): string {\n // Create a stack trace from the componentStack param and link it to the original error\n // using `error.cause`. Linking errors requires the `LinkedErrors` integration be enabled.\n // See: https://reactjs.org/blog/2020/08/10/react-v17-rc.html#native-component-stacks\n //\n // Although `componentDidCatch` is typed to accept an `Error` object, it can also be invoked\n // with non-error objects. This is why we need to check if the error is an error-like object.\n // See: https://github.com/getsentry/sentry-javascript/issues/6167\n if (isError(error) && componentStack) {\n const errorBoundaryError = new Error(error.message);\n errorBoundaryError.name = `React ErrorBoundary ${error.name}`;\n errorBoundaryError.stack = componentStack;\n\n // Using the `LinkedErrors` integration to link the errors together.\n setCause(error, errorBoundaryError);\n }\n\n return captureException(error, hint);\n}\n\n/**\n * Creates an error handler that can be used with the `onCaughtError`, `onUncaughtError`,\n * and `onRecoverableError` options in `createRoot` and `hydrateRoot` React DOM methods.\n *\n * @param callback An optional callback that will be called after the error is captured.\n * Use this to add custom handling for errors.\n *\n * @example\n *\n * ```JavaScript\n * const root = createRoot(container, {\n * onCaughtError: Sentry.reactErrorHandler(),\n * onUncaughtError: Sentry.reactErrorHandler((error, errorInfo) => {\n * console.warn('Caught error', error, errorInfo.componentStack);\n * });\n * });\n * ```\n */\nexport function reactErrorHandler(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n callback?: (error: any, errorInfo: ErrorInfo, eventId: string) => void,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): (error: any, errorInfo: ErrorInfo) => void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (error: any, errorInfo: ErrorInfo) => {\n const hasCallback = !!callback;\n const eventId = captureReactException(error, errorInfo, {\n mechanism: { handled: hasCallback, type: 'auto.function.react.error_handler' },\n });\n if (hasCallback) {\n callback(error, errorInfo, eventId);\n }\n };\n}\n"],"names":["error","cause"],"mappings":";;;AAOO,SAAS,QAAA,CAAS,OAAkC,KAAA,EAAoB;AAC7E,EAAA,MAAM,UAAA,uBAAiB,OAAA,EAAQ;AAE/B,EAAA,SAAS,OAAA,CAAQA,QAAkCC,MAAAA,EAAoB;AAGrE,IAAA,IAAI,UAAA,CAAW,GAAA,CAAID,MAAK,CAAA,EAAG;AACzB,MAAA;AAAA,IACF;AACA,IAAA,IAAIA,OAAM,KAAA,EAAO;AACf,MAAA,UAAA,CAAW,IAAIA,MAAK,CAAA;AACpB,MAAA,OAAO,OAAA,CAAQA,MAAAA,CAAM,KAAA,EAAOC,MAAK,CAAA;AAAA,IACnC;AACA,IAAAD,OAAM,KAAA,GAAQC,MAAAA;AAAA,EAChB;AAEA,EAAA,OAAA,CAAQ,OAAO,KAAK,CAAA;AACtB;AAUO,SAAS,qBAAA,CAEd,KAAA,EACA,EAAE,cAAA,IACF,IAAA,EACQ;AAQR,EAAA,IAAI,OAAA,CAAQ,KAAK,CAAA,IAAK,cAAA,EAAgB;AACpC,IAAA,MAAM,kBAAA,GAAqB,IAAI,KAAA,CAAM,KAAA,CAAM,OAAO,CAAA;AAClD,IAAA,kBAAA,CAAmB,IAAA,GAAO,CAAA,oBAAA,EAAuB,KAAA,CAAM,IAAI,CAAA,CAAA;AAC3D,IAAA,kBAAA,CAAmB,KAAA,GAAQ,cAAA;AAG3B,IAAA,QAAA,CAAS,OAAO,kBAAkB,CAAA;AAAA,EACpC;AAEA,EAAA,OAAO,gBAAA,CAAiB,OAAO,IAAI,CAAA;AACrC;AAoBO,SAAS,kBAEd,QAAA,EAE4C;AAE5C,EAAA,OAAO,CAAC,OAAY,SAAA,KAAyB;AAC3C,IAAA,MAAM,WAAA,GAAc,CAAC,CAAC,QAAA;AACtB,IAAA,MAAM,OAAA,GAAU,qBAAA,CAAsB,KAAA,EAAO,SAAA,EAAW;AAAA,MACtD,SAAA,EAAW,EAAE,OAAA,EAAS,WAAA,EAAa,MAAM,mCAAA;AAAoC,KAC9E,CAAA;AACD,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,QAAA,CAAS,KAAA,EAAO,WAAW,OAAO,CAAA;AAAA,IACpC;AAAA,EACF,CAAA;AACF;;;;"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.0.0-
|
|
1
|
+
{"type":"module","version":"11.0.0-beta.0","sideEffects":false}
|
package/build/esm/profiler.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { startInactiveSpan } from '@sentry/browser';
|
|
2
2
|
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, timestampInSeconds, withActiveSpan, spanToJSON } from '@sentry/core';
|
|
3
|
-
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
4
|
-
import {
|
|
3
|
+
import { UI_COMPONENT_NAME, SENTRY_OP } from '@sentry/conventions/attributes';
|
|
4
|
+
import { UI_MOUNT, UI_UPDATE, UI_RENDER } from '@sentry/conventions/op';
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { hoistNonReactStatics } from './hoist-non-react-statics.js';
|
|
7
7
|
|
|
@@ -17,10 +17,9 @@ class Profiler extends React.Component {
|
|
|
17
17
|
name: `<${name}>`,
|
|
18
18
|
onlyIfParent: true,
|
|
19
19
|
attributes: {
|
|
20
|
-
|
|
21
|
-
[SENTRY_OP]: "ui.mount",
|
|
20
|
+
[SENTRY_OP]: UI_MOUNT,
|
|
22
21
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
23
|
-
|
|
22
|
+
[UI_COMPONENT_NAME]: name
|
|
24
23
|
}
|
|
25
24
|
});
|
|
26
25
|
}
|
|
@@ -41,10 +40,9 @@ class Profiler extends React.Component {
|
|
|
41
40
|
onlyIfParent: true,
|
|
42
41
|
startTime: now,
|
|
43
42
|
attributes: {
|
|
44
|
-
|
|
45
|
-
[SENTRY_OP]: "ui.update",
|
|
43
|
+
[SENTRY_OP]: UI_UPDATE,
|
|
46
44
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
47
|
-
|
|
45
|
+
[UI_COMPONENT_NAME]: this.props.name,
|
|
48
46
|
"ui.react.changed_props": changedProps
|
|
49
47
|
}
|
|
50
48
|
});
|
|
@@ -72,9 +70,9 @@ class Profiler extends React.Component {
|
|
|
72
70
|
name: `<${name}>`,
|
|
73
71
|
startTime,
|
|
74
72
|
attributes: {
|
|
75
|
-
[SENTRY_OP]:
|
|
73
|
+
[SENTRY_OP]: UI_RENDER,
|
|
76
74
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
77
|
-
|
|
75
|
+
[UI_COMPONENT_NAME]: name
|
|
78
76
|
}
|
|
79
77
|
});
|
|
80
78
|
if (renderSpan) {
|
|
@@ -113,10 +111,9 @@ function useProfiler(name, options = {
|
|
|
113
111
|
name: `<${name}>`,
|
|
114
112
|
onlyIfParent: true,
|
|
115
113
|
attributes: {
|
|
116
|
-
|
|
117
|
-
[SENTRY_OP]: "ui.mount",
|
|
114
|
+
[SENTRY_OP]: UI_MOUNT,
|
|
118
115
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
119
|
-
|
|
116
|
+
[UI_COMPONENT_NAME]: name
|
|
120
117
|
}
|
|
121
118
|
});
|
|
122
119
|
});
|
|
@@ -133,9 +130,9 @@ function useProfiler(name, options = {
|
|
|
133
130
|
onlyIfParent: true,
|
|
134
131
|
startTime,
|
|
135
132
|
attributes: {
|
|
136
|
-
[SENTRY_OP]:
|
|
133
|
+
[SENTRY_OP]: UI_RENDER,
|
|
137
134
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
138
|
-
|
|
135
|
+
[UI_COMPONENT_NAME]: name
|
|
139
136
|
}
|
|
140
137
|
});
|
|
141
138
|
if (renderSpan) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiler.js","sources":["../../src/profiler.tsx"],"sourcesContent":["import { startInactiveSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON, timestampInSeconds, withActiveSpan } from '@sentry/core';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { BROWSER_UI_RENDER_SPAN_OP } from '@sentry/conventions/op';\nimport * as React from 'react';\nimport { hoistNonReactStatics } from './hoist-non-react-statics';\n\nexport const UNKNOWN_COMPONENT = 'unknown';\n\nexport type ProfilerProps = {\n // The name of the component being profiled.\n name: string;\n // If the Profiler is disabled. False by default. This is useful if you want to disable profilers\n // in certain environments.\n disabled?: boolean;\n // If time component is on page should be displayed as spans. True by default.\n includeRender?: boolean;\n // If component updates should be displayed as spans. True by default.\n includeUpdates?: boolean;\n // Component that is being profiled.\n children?: React.ReactNode;\n // props given to component being profiled.\n updateProps: { [key: string]: unknown };\n};\n\n/**\n * The Profiler component leverages Sentry's Tracing integration to generate\n * spans based on component lifecycles.\n */\nclass Profiler extends React.Component<ProfilerProps> {\n /**\n * The span of the mount activity\n * Made protected for the React Native SDK to access\n */\n protected _mountSpan: Span | undefined;\n /**\n * The span that represents the duration of time between shouldComponentUpdate and componentDidUpdate\n */\n protected _updateSpan: Span | undefined;\n\n public constructor(props: ProfilerProps) {\n super(props);\n const { name, disabled = false } = this.props;\n\n if (disabled) {\n return;\n }\n\n this._mountSpan = startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n attributes: {\n // TODO(conventions): Replace `'ui.mount'` with the `ui.mount` span op constant once it is released in `@sentry/conventions`.\n [SENTRY_OP]: 'ui.mount',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n 'ui.component_name': name,\n },\n });\n }\n\n // If a component mounted, we can finish the mount activity.\n public componentDidMount(): void {\n if (this._mountSpan) {\n this._mountSpan.end();\n }\n }\n\n public shouldComponentUpdate({ updateProps, includeUpdates = true }: ProfilerProps): boolean {\n // Only generate an update span if includeUpdates is true, if there is a valid mountSpan,\n // and if the updateProps have changed. It is ok to not do a deep equality check here as it is expensive.\n // We are just trying to give baseline clues for further investigation.\n if (includeUpdates && this._mountSpan && updateProps !== this.props.updateProps) {\n // See what props have changed between the previous props, and the current props. This is\n // set as data on the span. We just store the prop keys as the values could be potentially very large.\n const changedProps = Object.keys(updateProps).filter(k => updateProps[k] !== this.props.updateProps[k]);\n if (changedProps.length > 0) {\n const now = timestampInSeconds();\n this._updateSpan = withActiveSpan(this._mountSpan, () => {\n return startInactiveSpan({\n name: `<${this.props.name}>`,\n onlyIfParent: true,\n startTime: now,\n attributes: {\n // TODO(conventions): Replace `'ui.update'` with the `ui.update` span op constant once it is released in `@sentry/conventions`.\n [SENTRY_OP]: 'ui.update',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n 'ui.component_name': this.props.name,\n 'ui.react.changed_props': changedProps,\n },\n });\n });\n }\n }\n\n return true;\n }\n\n public componentDidUpdate(): void {\n if (this._updateSpan) {\n this._updateSpan.end();\n this._updateSpan = undefined;\n }\n }\n\n // If a component is unmounted, we can say it is no longer on the screen.\n // This means we can finish the span representing the component render.\n public componentWillUnmount(): void {\n const endTimestamp = timestampInSeconds();\n const { name, includeRender = true } = this.props;\n\n if (this._mountSpan && includeRender) {\n const startTime = spanToJSON(this._mountSpan).end_timestamp;\n withActiveSpan(this._mountSpan, () => {\n const renderSpan = startInactiveSpan({\n onlyIfParent: true,\n name: `<${name}>`,\n startTime,\n attributes: {\n [SENTRY_OP]: BROWSER_UI_RENDER_SPAN_OP,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n 'ui.component_name': name,\n },\n });\n if (renderSpan) {\n // Have to cast to Span because the type of _mountSpan is Span | undefined\n // and not getting narrowed properly\n renderSpan.end(endTimestamp);\n }\n });\n }\n }\n\n public render(): React.ReactNode {\n return this.props.children;\n }\n}\n\n// React.Component default props are defined as static property on the class\nObject.assign(Profiler, {\n defaultProps: {\n disabled: false,\n includeRender: true,\n includeUpdates: true,\n },\n});\n\n/**\n * withProfiler is a higher order component that wraps a\n * component in a {@link Profiler} component. It is recommended that\n * the higher order component be used over the regular {@link Profiler} component.\n *\n * @param WrappedComponent component that is wrapped by Profiler\n * @param options the {@link ProfilerProps} you can pass into the Profiler\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction withProfiler<P extends Record<string, any>>(\n WrappedComponent: React.ComponentType<P>,\n // We do not want to have `updateProps` given in options, it is instead filled through the HOC.\n options?: Pick<Partial<ProfilerProps>, Exclude<keyof ProfilerProps, 'updateProps' | 'children'>>,\n): React.FC<P> {\n const componentDisplayName =\n options?.name || WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;\n\n const Wrapped: React.FC<P> = (props: P) => (\n <Profiler {...options} name={componentDisplayName} updateProps={props}>\n <WrappedComponent {...props} />\n </Profiler>\n );\n\n Wrapped.displayName = `profiler(${componentDisplayName})`;\n\n // Copy over static methods from Wrapped component to Profiler HOC\n // See: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over\n hoistNonReactStatics(Wrapped, WrappedComponent);\n return Wrapped;\n}\n\n/**\n *\n * `useProfiler` is a React hook that profiles a React component.\n *\n * @param name displayName of component being profiled\n */\nfunction useProfiler(\n name: string,\n options: { disabled?: boolean; hasRenderSpan?: boolean } = {\n disabled: false,\n hasRenderSpan: true,\n },\n): void {\n const [mountSpan] = React.useState(() => {\n if (options?.disabled) {\n return undefined;\n }\n\n return startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n attributes: {\n // TODO(conventions): Replace `'ui.mount'` with the `ui.mount` span op constant once it is released in `@sentry/conventions`.\n [SENTRY_OP]: 'ui.mount',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n 'ui.component_name': name,\n },\n });\n });\n\n React.useEffect(() => {\n if (mountSpan) {\n mountSpan.end();\n }\n\n return (): void => {\n if (mountSpan && options.hasRenderSpan) {\n const startTime = spanToJSON(mountSpan).end_timestamp;\n const endTimestamp = timestampInSeconds();\n\n const renderSpan = startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n startTime,\n attributes: {\n [SENTRY_OP]: BROWSER_UI_RENDER_SPAN_OP,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n 'ui.component_name': name,\n },\n });\n if (renderSpan) {\n // Have to cast to Span because the type of _mountSpan is Span | undefined\n // and not getting narrowed properly\n renderSpan.end(endTimestamp);\n }\n }\n };\n // We only want this to run once.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n\nexport { Profiler, useProfiler, withProfiler };\n"],"names":[],"mappings":";;;;;;;AAQO,MAAM,iBAAA,GAAoB;AAsBjC,MAAM,QAAA,SAAiB,MAAM,SAAA,CAAyB;AAAA,EAW7C,YAAY,KAAA,EAAsB;AACvC,IAAA,KAAA,CAAM,KAAK,CAAA;AACX,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,GAAW,KAAA,KAAU,IAAA,CAAK,KAAA;AAExC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,aAAa,iBAAA,CAAkB;AAAA,MAClC,IAAA,EAAM,IAAI,IAAI,CAAA,CAAA,CAAA;AAAA,MACd,YAAA,EAAc,IAAA;AAAA,MACd,UAAA,EAAY;AAAA;AAAA,QAEV,CAAC,SAAS,GAAG,UAAA;AAAA,QACb,CAAC,gCAAgC,GAAG,wBAAA;AAAA,QACpC,mBAAA,EAAqB;AAAA;AACvB,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGO,iBAAA,GAA0B;AAC/B,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,IAAA,CAAK,WAAW,GAAA,EAAI;AAAA,IACtB;AAAA,EACF;AAAA,EAEO,qBAAA,CAAsB,EAAE,WAAA,EAAa,cAAA,GAAiB,MAAK,EAA2B;AAI3F,IAAA,IAAI,kBAAkB,IAAA,CAAK,UAAA,IAAc,WAAA,KAAgB,IAAA,CAAK,MAAM,WAAA,EAAa;AAG/E,MAAA,MAAM,YAAA,GAAe,MAAA,CAAO,IAAA,CAAK,WAAW,EAAE,MAAA,CAAO,CAAA,CAAA,KAAK,WAAA,CAAY,CAAC,CAAA,KAAM,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,CAAC,CAAC,CAAA;AACtG,MAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,QAAA,MAAM,MAAM,kBAAA,EAAmB;AAC/B,QAAA,IAAA,CAAK,WAAA,GAAc,cAAA,CAAe,IAAA,CAAK,UAAA,EAAY,MAAM;AACvD,UAAA,OAAO,iBAAA,CAAkB;AAAA,YACvB,IAAA,EAAM,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,CAAA,CAAA;AAAA,YACzB,YAAA,EAAc,IAAA;AAAA,YACd,SAAA,EAAW,GAAA;AAAA,YACX,UAAA,EAAY;AAAA;AAAA,cAEV,CAAC,SAAS,GAAG,WAAA;AAAA,cACb,CAAC,gCAAgC,GAAG,wBAAA;AAAA,cACpC,mBAAA,EAAqB,KAAK,KAAA,CAAM,IAAA;AAAA,cAChC,wBAAA,EAA0B;AAAA;AAC5B,WACD,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEO,kBAAA,GAA2B;AAChC,IAAA,IAAI,KAAK,WAAA,EAAa;AACpB,MAAA,IAAA,CAAK,YAAY,GAAA,EAAI;AACrB,MAAA,IAAA,CAAK,WAAA,GAAc,MAAA;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA,EAIO,oBAAA,GAA6B;AAClC,IAAA,MAAM,eAAe,kBAAA,EAAmB;AACxC,IAAA,MAAM,EAAE,IAAA,EAAM,aAAA,GAAgB,IAAA,KAAS,IAAA,CAAK,KAAA;AAE5C,IAAA,IAAI,IAAA,CAAK,cAAc,aAAA,EAAe;AACpC,MAAA,MAAM,SAAA,GAAY,UAAA,CAAW,IAAA,CAAK,UAAU,CAAA,CAAE,aAAA;AAC9C,MAAA,cAAA,CAAe,IAAA,CAAK,YAAY,MAAM;AACpC,QAAA,MAAM,aAAa,iBAAA,CAAkB;AAAA,UACnC,YAAA,EAAc,IAAA;AAAA,UACd,IAAA,EAAM,IAAI,IAAI,CAAA,CAAA,CAAA;AAAA,UACd,SAAA;AAAA,UACA,UAAA,EAAY;AAAA,YACV,CAAC,SAAS,GAAG,yBAAA;AAAA,YACb,CAAC,gCAAgC,GAAG,wBAAA;AAAA,YACpC,mBAAA,EAAqB;AAAA;AACvB,SACD,CAAA;AACD,QAAA,IAAI,UAAA,EAAY;AAGd,UAAA,UAAA,CAAW,IAAI,YAAY,CAAA;AAAA,QAC7B;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEO,MAAA,GAA0B;AAC/B,IAAA,OAAO,KAAK,KAAA,CAAM,QAAA;AAAA,EACpB;AACF;AAGA,MAAA,CAAO,OAAO,QAAA,EAAU;AAAA,EACtB,YAAA,EAAc;AAAA,IACZ,QAAA,EAAU,KAAA;AAAA,IACV,aAAA,EAAe,IAAA;AAAA,IACf,cAAA,EAAgB;AAAA;AAEpB,CAAC,CAAA;AAWD,SAAS,YAAA,CACP,kBAEA,OAAA,EACa;AACb,EAAA,MAAM,uBACJ,OAAA,EAAS,IAAA,IAAQ,gBAAA,CAAiB,WAAA,IAAe,iBAAiB,IAAA,IAAQ,iBAAA;AAE5E,EAAA,MAAM,OAAA,GAAuB,CAAC,KAAA,qBAC5B,KAAA,CAAA,aAAA,CAAC,YAAU,GAAG,OAAA,EAAS,IAAA,EAAM,oBAAA,EAAsB,aAAa,KAAA,EAAA,kBAC9D,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAkB,GAAG,OAAO,CAC/B,CAAA;AAGF,EAAA,OAAA,CAAQ,WAAA,GAAc,YAAY,oBAAoB,CAAA,CAAA,CAAA;AAItD,EAAA,oBAAA,CAAqB,SAAS,gBAAgB,CAAA;AAC9C,EAAA,OAAO,OAAA;AACT;AAQA,SAAS,WAAA,CACP,MACA,OAAA,GAA2D;AAAA,EACzD,QAAA,EAAU,KAAA;AAAA,EACV,aAAA,EAAe;AACjB,CAAA,EACM;AACN,EAAA,MAAM,CAAC,SAAS,CAAA,GAAI,KAAA,CAAM,SAAS,MAAM;AACvC,IAAA,IAAI,SAAS,QAAA,EAAU;AACrB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,OAAO,iBAAA,CAAkB;AAAA,MACvB,IAAA,EAAM,IAAI,IAAI,CAAA,CAAA,CAAA;AAAA,MACd,YAAA,EAAc,IAAA;AAAA,MACd,UAAA,EAAY;AAAA;AAAA,QAEV,CAAC,SAAS,GAAG,UAAA;AAAA,QACb,CAAC,gCAAgC,GAAG,wBAAA;AAAA,QACpC,mBAAA,EAAqB;AAAA;AACvB,KACD,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,SAAA,CAAU,GAAA,EAAI;AAAA,IAChB;AAEA,IAAA,OAAO,MAAY;AACjB,MAAA,IAAI,SAAA,IAAa,QAAQ,aAAA,EAAe;AACtC,QAAA,MAAM,SAAA,GAAY,UAAA,CAAW,SAAS,CAAA,CAAE,aAAA;AACxC,QAAA,MAAM,eAAe,kBAAA,EAAmB;AAExC,QAAA,MAAM,aAAa,iBAAA,CAAkB;AAAA,UACnC,IAAA,EAAM,IAAI,IAAI,CAAA,CAAA,CAAA;AAAA,UACd,YAAA,EAAc,IAAA;AAAA,UACd,SAAA;AAAA,UACA,UAAA,EAAY;AAAA,YACV,CAAC,SAAS,GAAG,yBAAA;AAAA,YACb,CAAC,gCAAgC,GAAG,wBAAA;AAAA,YACpC,mBAAA,EAAqB;AAAA;AACvB,SACD,CAAA;AACD,QAAA,IAAI,UAAA,EAAY;AAGd,UAAA,UAAA,CAAW,IAAI,YAAY,CAAA;AAAA,QAC7B;AAAA,MACF;AAAA,IACF,CAAA;AAAA,EAGF,CAAA,EAAG,EAAE,CAAA;AACP;;;;"}
|
|
1
|
+
{"version":3,"file":"profiler.js","sources":["../../src/profiler.tsx"],"sourcesContent":["import { startInactiveSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON, timestampInSeconds, withActiveSpan } from '@sentry/core';\nimport { SENTRY_OP, UI_COMPONENT_NAME } from '@sentry/conventions/attributes';\nimport { UI_MOUNT, UI_RENDER, UI_UPDATE } from '@sentry/conventions/op';\nimport * as React from 'react';\nimport { hoistNonReactStatics } from './hoist-non-react-statics';\n\nexport const UNKNOWN_COMPONENT = 'unknown';\n\nexport type ProfilerProps = {\n // The name of the component being profiled.\n name: string;\n // If the Profiler is disabled. False by default. This is useful if you want to disable profilers\n // in certain environments.\n disabled?: boolean;\n // If time component is on page should be displayed as spans. True by default.\n includeRender?: boolean;\n // If component updates should be displayed as spans. True by default.\n includeUpdates?: boolean;\n // Component that is being profiled.\n children?: React.ReactNode;\n // props given to component being profiled.\n updateProps: { [key: string]: unknown };\n};\n\n/**\n * The Profiler component leverages Sentry's Tracing integration to generate\n * spans based on component lifecycles.\n */\nclass Profiler extends React.Component<ProfilerProps> {\n /**\n * The span of the mount activity\n * Made protected for the React Native SDK to access\n */\n protected _mountSpan: Span | undefined;\n /**\n * The span that represents the duration of time between shouldComponentUpdate and componentDidUpdate\n */\n protected _updateSpan: Span | undefined;\n\n public constructor(props: ProfilerProps) {\n super(props);\n const { name, disabled = false } = this.props;\n\n if (disabled) {\n return;\n }\n\n this._mountSpan = startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n attributes: {\n [SENTRY_OP]: UI_MOUNT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n [UI_COMPONENT_NAME]: name,\n },\n });\n }\n\n // If a component mounted, we can finish the mount activity.\n public componentDidMount(): void {\n if (this._mountSpan) {\n this._mountSpan.end();\n }\n }\n\n public shouldComponentUpdate({ updateProps, includeUpdates = true }: ProfilerProps): boolean {\n // Only generate an update span if includeUpdates is true, if there is a valid mountSpan,\n // and if the updateProps have changed. It is ok to not do a deep equality check here as it is expensive.\n // We are just trying to give baseline clues for further investigation.\n if (includeUpdates && this._mountSpan && updateProps !== this.props.updateProps) {\n // See what props have changed between the previous props, and the current props. This is\n // set as data on the span. We just store the prop keys as the values could be potentially very large.\n const changedProps = Object.keys(updateProps).filter(k => updateProps[k] !== this.props.updateProps[k]);\n if (changedProps.length > 0) {\n const now = timestampInSeconds();\n this._updateSpan = withActiveSpan(this._mountSpan, () => {\n return startInactiveSpan({\n name: `<${this.props.name}>`,\n onlyIfParent: true,\n startTime: now,\n attributes: {\n [SENTRY_OP]: UI_UPDATE,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n [UI_COMPONENT_NAME]: this.props.name,\n 'ui.react.changed_props': changedProps,\n },\n });\n });\n }\n }\n\n return true;\n }\n\n public componentDidUpdate(): void {\n if (this._updateSpan) {\n this._updateSpan.end();\n this._updateSpan = undefined;\n }\n }\n\n // If a component is unmounted, we can say it is no longer on the screen.\n // This means we can finish the span representing the component render.\n public componentWillUnmount(): void {\n const endTimestamp = timestampInSeconds();\n const { name, includeRender = true } = this.props;\n\n if (this._mountSpan && includeRender) {\n const startTime = spanToJSON(this._mountSpan).end_timestamp;\n withActiveSpan(this._mountSpan, () => {\n const renderSpan = startInactiveSpan({\n onlyIfParent: true,\n name: `<${name}>`,\n startTime,\n attributes: {\n [SENTRY_OP]: UI_RENDER,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n [UI_COMPONENT_NAME]: name,\n },\n });\n if (renderSpan) {\n // Have to cast to Span because the type of _mountSpan is Span | undefined\n // and not getting narrowed properly\n renderSpan.end(endTimestamp);\n }\n });\n }\n }\n\n public render(): React.ReactNode {\n return this.props.children;\n }\n}\n\n// React.Component default props are defined as static property on the class\nObject.assign(Profiler, {\n defaultProps: {\n disabled: false,\n includeRender: true,\n includeUpdates: true,\n },\n});\n\n/**\n * withProfiler is a higher order component that wraps a\n * component in a {@link Profiler} component. It is recommended that\n * the higher order component be used over the regular {@link Profiler} component.\n *\n * @param WrappedComponent component that is wrapped by Profiler\n * @param options the {@link ProfilerProps} you can pass into the Profiler\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction withProfiler<P extends Record<string, any>>(\n WrappedComponent: React.ComponentType<P>,\n // We do not want to have `updateProps` given in options, it is instead filled through the HOC.\n options?: Pick<Partial<ProfilerProps>, Exclude<keyof ProfilerProps, 'updateProps' | 'children'>>,\n): React.FC<P> {\n const componentDisplayName =\n options?.name || WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;\n\n const Wrapped: React.FC<P> = (props: P) => (\n <Profiler {...options} name={componentDisplayName} updateProps={props}>\n <WrappedComponent {...props} />\n </Profiler>\n );\n\n Wrapped.displayName = `profiler(${componentDisplayName})`;\n\n // Copy over static methods from Wrapped component to Profiler HOC\n // See: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over\n hoistNonReactStatics(Wrapped, WrappedComponent);\n return Wrapped;\n}\n\n/**\n *\n * `useProfiler` is a React hook that profiles a React component.\n *\n * @param name displayName of component being profiled\n */\nfunction useProfiler(\n name: string,\n options: { disabled?: boolean; hasRenderSpan?: boolean } = {\n disabled: false,\n hasRenderSpan: true,\n },\n): void {\n const [mountSpan] = React.useState(() => {\n if (options?.disabled) {\n return undefined;\n }\n\n return startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n attributes: {\n [SENTRY_OP]: UI_MOUNT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n [UI_COMPONENT_NAME]: name,\n },\n });\n });\n\n React.useEffect(() => {\n if (mountSpan) {\n mountSpan.end();\n }\n\n return (): void => {\n if (mountSpan && options.hasRenderSpan) {\n const startTime = spanToJSON(mountSpan).end_timestamp;\n const endTimestamp = timestampInSeconds();\n\n const renderSpan = startInactiveSpan({\n name: `<${name}>`,\n onlyIfParent: true,\n startTime,\n attributes: {\n [SENTRY_OP]: UI_RENDER,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.react.profiler',\n [UI_COMPONENT_NAME]: name,\n },\n });\n if (renderSpan) {\n // Have to cast to Span because the type of _mountSpan is Span | undefined\n // and not getting narrowed properly\n renderSpan.end(endTimestamp);\n }\n }\n };\n // We only want this to run once.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n\nexport { Profiler, useProfiler, withProfiler };\n"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { WINDOW, browserTracingIntegration, startBrowserTracingPageLoadSpan, startBrowserTracingNavigationSpan } from '@sentry/browser';
|
|
2
|
-
import {
|
|
2
|
+
import { PAGELOAD_SPAN_NAME_FALLBACK, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, hasSpanStreamingEnabled, debug, addNonEnumerableProperty, spanToJSON, getCurrentScope, getClient, NAVIGATION_SPAN_NAME_FALLBACK } from '@sentry/core';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { DEBUG_BUILD } from '../debug-build.js';
|
|
5
5
|
import { hoistNonReactStatics } from '../hoist-non-react-statics.js';
|
|
6
6
|
import { checkRouteForAsyncHandler } from './lazy-routes.js';
|
|
7
7
|
import { getActiveRootSpan, setNavigationContext, clearNavigationContext, resolveRouteNameAndSource, transactionNameHasWildcard, initializeRouterUtils } from './utils.js';
|
|
8
|
-
import { SENTRY_OP, URL_TEMPLATE } from '@sentry/conventions/attributes';
|
|
8
|
+
import { SENTRY_OP, SENTRY_SEGMENT_NAME_SOURCE, URL_TEMPLATE } from '@sentry/conventions/attributes';
|
|
9
|
+
import { PAGELOAD, NAVIGATION } from '@sentry/conventions/op';
|
|
9
10
|
|
|
10
11
|
let _useEffect;
|
|
11
12
|
let _useLocation;
|
|
@@ -157,14 +158,16 @@ function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate
|
|
|
157
158
|
_lazyRouteManifest,
|
|
158
159
|
_enableAsyncRouteHandlers
|
|
159
160
|
);
|
|
160
|
-
const currentSource = attributes[
|
|
161
|
+
const currentSource = attributes[SENTRY_SEGMENT_NAME_SOURCE];
|
|
161
162
|
const isImprovement = name && (!currentName || // No current name - always set
|
|
162
163
|
!hasBeenNamed && (currentSource !== "route" || source === "route") || // Not finalized - allow unless downgrading route→url
|
|
163
164
|
currentSource !== "route" && source === "route" || // URL → route upgrade
|
|
164
165
|
currentSource === "route" && source === "route" && currentNameHasWildcard);
|
|
165
166
|
if (isImprovement) {
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
const client = getClient();
|
|
168
|
+
const isUnparameterizedStreamedNavigation = source !== "route" && !!client && hasSpanStreamingEnabled(client);
|
|
169
|
+
activeRootSpan.updateName(isUnparameterizedStreamedNavigation ? NAVIGATION_SPAN_NAME_FALLBACK : name);
|
|
170
|
+
activeRootSpan.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
168
171
|
if (source === "route") {
|
|
169
172
|
activeRootSpan.setAttribute(URL_TEMPLATE, name);
|
|
170
173
|
}
|
|
@@ -369,10 +372,12 @@ function createReactRouterV6CompatibleTracingIntegration(options, version) {
|
|
|
369
372
|
const initPathName = WINDOW.location?.pathname;
|
|
370
373
|
if (instrumentPageLoad && initPathName) {
|
|
371
374
|
startBrowserTracingPageLoadSpan(client, {
|
|
372
|
-
|
|
375
|
+
// With span streaming, span names have to be low cardinality. The route is only resolved
|
|
376
|
+
// once the router renders, which updates the span name then.
|
|
377
|
+
name: hasSpanStreamingEnabled(client) ? PAGELOAD_SPAN_NAME_FALLBACK : initPathName,
|
|
373
378
|
attributes: {
|
|
374
|
-
[
|
|
375
|
-
[
|
|
379
|
+
[SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
380
|
+
[SENTRY_OP]: PAGELOAD,
|
|
376
381
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.pageload.react.reactrouter${version ? `_v${version}` : ""}`
|
|
377
382
|
}
|
|
378
383
|
});
|
|
@@ -541,8 +546,9 @@ function handleNavigation(opts) {
|
|
|
541
546
|
`[Tracing] Updated placeholder navigation name from "${oldName}" to "${name}" (will apply to real span)`
|
|
542
547
|
);
|
|
543
548
|
} else {
|
|
544
|
-
|
|
545
|
-
trackedNav.span.
|
|
549
|
+
const isUnparameterizedStreamedNavigation = source !== "route" && hasSpanStreamingEnabled(client);
|
|
550
|
+
trackedNav.span.updateName(isUnparameterizedStreamedNavigation ? NAVIGATION_SPAN_NAME_FALLBACK : name);
|
|
551
|
+
trackedNav.span.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
546
552
|
if (source === "route") {
|
|
547
553
|
trackedNav.span.setAttribute(URL_TEMPLATE, name);
|
|
548
554
|
}
|
|
@@ -568,11 +574,12 @@ function handleNavigation(opts) {
|
|
|
568
574
|
let navigationSpan;
|
|
569
575
|
try {
|
|
570
576
|
navigationSpan = startBrowserTracingNavigationSpan(client, {
|
|
571
|
-
|
|
572
|
-
//
|
|
577
|
+
// Use placeholder's routeName in case it was updated. With span streaming, span names have to
|
|
578
|
+
// be low cardinality, so we can't fall back to the URL.
|
|
579
|
+
name: source === "route" || !hasSpanStreamingEnabled(client) ? placeholderEntry.routeName : NAVIGATION_SPAN_NAME_FALLBACK,
|
|
573
580
|
attributes: {
|
|
574
|
-
[
|
|
575
|
-
[
|
|
581
|
+
[SENTRY_SEGMENT_NAME_SOURCE]: source,
|
|
582
|
+
[SENTRY_OP]: NAVIGATION,
|
|
576
583
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter${version ? `_v${version}` : ""}`,
|
|
577
584
|
...source === "route" && { [URL_TEMPLATE]: placeholderEntry.routeName }
|
|
578
585
|
}
|
|
@@ -646,8 +653,10 @@ function updatePageloadTransaction({
|
|
|
646
653
|
);
|
|
647
654
|
getCurrentScope().setTransactionName(name || "/");
|
|
648
655
|
if (activeRootSpan) {
|
|
649
|
-
|
|
650
|
-
|
|
656
|
+
const client = getClient();
|
|
657
|
+
const isUnparameterizedStreamedPageload = source !== "route" && !!client && hasSpanStreamingEnabled(client);
|
|
658
|
+
activeRootSpan.updateName(isUnparameterizedStreamedPageload ? PAGELOAD_SPAN_NAME_FALLBACK : name);
|
|
659
|
+
activeRootSpan.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
651
660
|
if (source === "route") {
|
|
652
661
|
activeRootSpan.setAttribute(URL_TEMPLATE, name);
|
|
653
662
|
}
|
|
@@ -675,7 +684,7 @@ function shouldUpdateWildcardSpanName(currentName, currentSource, newName, newSo
|
|
|
675
684
|
}
|
|
676
685
|
function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, routes, basename, spanType, allRoutes2) {
|
|
677
686
|
try {
|
|
678
|
-
const currentSource = spanJson.attributes[
|
|
687
|
+
const currentSource = spanJson.attributes[SENTRY_SEGMENT_NAME_SOURCE];
|
|
679
688
|
if (currentSource === "route" && currentName && !transactionNameHasWildcard(currentName)) {
|
|
680
689
|
return;
|
|
681
690
|
}
|
|
@@ -697,8 +706,11 @@ function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, route
|
|
|
697
706
|
const isImprovement = shouldUpdateWildcardSpanName(currentName, currentSource, name, source, true);
|
|
698
707
|
const spanNotEnded = spanType === "pageload" || !spanJson.end_timestamp;
|
|
699
708
|
if (isImprovement && spanNotEnded) {
|
|
700
|
-
|
|
701
|
-
|
|
709
|
+
const client = getClient();
|
|
710
|
+
const isUnparameterizedStreamedSpan = source !== "route" && !!client && hasSpanStreamingEnabled(client);
|
|
711
|
+
const fallbackName = spanType === "pageload" ? PAGELOAD_SPAN_NAME_FALLBACK : NAVIGATION_SPAN_NAME_FALLBACK;
|
|
712
|
+
span.updateName(isUnparameterizedStreamedSpan ? fallbackName : name);
|
|
713
|
+
span.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
702
714
|
if (source === "route") {
|
|
703
715
|
span.setAttribute(URL_TEMPLATE, name);
|
|
704
716
|
}
|
|
@@ -723,7 +735,7 @@ function patchSpanEnd(span, location, routes, basename, spanType) {
|
|
|
723
735
|
const endTimestamp = args.length > 0 ? args[0] : Date.now() / 1e3;
|
|
724
736
|
const spanJson = spanToJSON(span);
|
|
725
737
|
const currentName = spanJson.name;
|
|
726
|
-
const currentSource = spanJson.attributes[
|
|
738
|
+
const currentSource = spanJson.attributes[SENTRY_SEGMENT_NAME_SOURCE];
|
|
727
739
|
const cleanupNavigationSpan = () => {
|
|
728
740
|
const client = getClient();
|
|
729
741
|
if (client && spanType === "navigation") {
|