@sentry/react 11.0.0-alpha.0 → 11.0.0-alpha.2
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/profiler.js +12 -15
- package/build/cjs/profiler.js.map +1 -1
- package/build/cjs/reactrouter-compat-utils/instrumentation.js +43 -33
- package/build/cjs/reactrouter-compat-utils/instrumentation.js.map +1 -1
- package/build/cjs/reactrouter-compat-utils/utils.js +2 -1
- package/build/cjs/reactrouter-compat-utils/utils.js.map +1 -1
- package/build/cjs/reactrouter.js +8 -6
- package/build/cjs/reactrouter.js.map +1 -1
- package/build/cjs/reactrouterv3.js +6 -4
- package/build/cjs/reactrouterv3.js.map +1 -1
- package/build/cjs/tanstackrouter.js +20 -9
- package/build/cjs/tanstackrouter.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/profiler.js +14 -17
- package/build/esm/profiler.js.map +1 -1
- package/build/esm/reactrouter-compat-utils/instrumentation.js +45 -35
- package/build/esm/reactrouter-compat-utils/instrumentation.js.map +1 -1
- package/build/esm/reactrouter-compat-utils/utils.js +2 -1
- package/build/esm/reactrouter-compat-utils/utils.js.map +1 -1
- package/build/esm/reactrouter.js +10 -8
- package/build/esm/reactrouter.js.map +1 -1
- package/build/esm/reactrouterv3.js +8 -6
- package/build/esm/reactrouterv3.js.map +1 -1
- package/build/esm/tanstackrouter.js +23 -12
- 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.map +1 -1
- package/build/types/reactrouter.d.ts.map +1 -1
- package/build/types/reactrouterv3.d.ts.map +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
package/build/cjs/profiler.js
CHANGED
|
@@ -19,10 +19,9 @@ class Profiler extends React.Component {
|
|
|
19
19
|
name: `<${name}>`,
|
|
20
20
|
onlyIfParent: true,
|
|
21
21
|
attributes: {
|
|
22
|
-
|
|
23
|
-
[attributes.SENTRY_OP]: "ui.mount",
|
|
22
|
+
[attributes.SENTRY_OP]: op.UI_MOUNT,
|
|
24
23
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
25
|
-
|
|
24
|
+
[attributes.UI_COMPONENT_NAME]: name
|
|
26
25
|
}
|
|
27
26
|
});
|
|
28
27
|
}
|
|
@@ -43,10 +42,9 @@ class Profiler extends React.Component {
|
|
|
43
42
|
onlyIfParent: true,
|
|
44
43
|
startTime: now,
|
|
45
44
|
attributes: {
|
|
46
|
-
|
|
47
|
-
[attributes.SENTRY_OP]: "ui.update",
|
|
45
|
+
[attributes.SENTRY_OP]: op.UI_UPDATE,
|
|
48
46
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
49
|
-
|
|
47
|
+
[attributes.UI_COMPONENT_NAME]: this.props.name,
|
|
50
48
|
"ui.react.changed_props": changedProps
|
|
51
49
|
}
|
|
52
50
|
});
|
|
@@ -67,16 +65,16 @@ class Profiler extends React.Component {
|
|
|
67
65
|
const endTimestamp = core.timestampInSeconds();
|
|
68
66
|
const { name, includeRender = true } = this.props;
|
|
69
67
|
if (this._mountSpan && includeRender) {
|
|
70
|
-
const startTime = core.spanToJSON(this._mountSpan).
|
|
68
|
+
const startTime = core.spanToJSON(this._mountSpan).end_timestamp;
|
|
71
69
|
core.withActiveSpan(this._mountSpan, () => {
|
|
72
70
|
const renderSpan = browser.startInactiveSpan({
|
|
73
71
|
onlyIfParent: true,
|
|
74
72
|
name: `<${name}>`,
|
|
75
73
|
startTime,
|
|
76
74
|
attributes: {
|
|
77
|
-
[attributes.SENTRY_OP]: op.
|
|
75
|
+
[attributes.SENTRY_OP]: op.UI_RENDER,
|
|
78
76
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
79
|
-
|
|
77
|
+
[attributes.UI_COMPONENT_NAME]: name
|
|
80
78
|
}
|
|
81
79
|
});
|
|
82
80
|
if (renderSpan) {
|
|
@@ -115,10 +113,9 @@ function useProfiler(name, options = {
|
|
|
115
113
|
name: `<${name}>`,
|
|
116
114
|
onlyIfParent: true,
|
|
117
115
|
attributes: {
|
|
118
|
-
|
|
119
|
-
[attributes.SENTRY_OP]: "ui.mount",
|
|
116
|
+
[attributes.SENTRY_OP]: op.UI_MOUNT,
|
|
120
117
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
121
|
-
|
|
118
|
+
[attributes.UI_COMPONENT_NAME]: name
|
|
122
119
|
}
|
|
123
120
|
});
|
|
124
121
|
});
|
|
@@ -128,16 +125,16 @@ function useProfiler(name, options = {
|
|
|
128
125
|
}
|
|
129
126
|
return () => {
|
|
130
127
|
if (mountSpan && options.hasRenderSpan) {
|
|
131
|
-
const startTime = core.spanToJSON(mountSpan).
|
|
128
|
+
const startTime = core.spanToJSON(mountSpan).end_timestamp;
|
|
132
129
|
const endTimestamp = core.timestampInSeconds();
|
|
133
130
|
const renderSpan = browser.startInactiveSpan({
|
|
134
131
|
name: `<${name}>`,
|
|
135
132
|
onlyIfParent: true,
|
|
136
133
|
startTime,
|
|
137
134
|
attributes: {
|
|
138
|
-
[attributes.SENTRY_OP]: op.
|
|
135
|
+
[attributes.SENTRY_OP]: op.UI_RENDER,
|
|
139
136
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.react.profiler",
|
|
140
|
-
|
|
137
|
+
[attributes.UI_COMPONENT_NAME]: name
|
|
141
138
|
}
|
|
142
139
|
});
|
|
143
140
|
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).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).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":["startInactiveSpan","SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","timestampInSeconds","withActiveSpan","spanToJSON","BROWSER_UI_RENDER_SPAN_OP","hoistNonReactStatics"],"mappings":"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"}
|
|
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":["startInactiveSpan","SENTRY_OP","UI_MOUNT","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","UI_COMPONENT_NAME","timestampInSeconds","withActiveSpan","UI_UPDATE","spanToJSON","UI_RENDER","hoistNonReactStatics"],"mappings":"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"}
|
|
@@ -114,12 +114,12 @@ function processResolvedRoutes(resolvedRoutes, parentRoute, currentLocation = nu
|
|
|
114
114
|
}
|
|
115
115
|
const targetSpan = capturedSpan ?? utils.getActiveRootSpan();
|
|
116
116
|
if (targetSpan) {
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
117
|
+
const { end_timestamp, attributes: attributes$1 } = core.spanToJSON(targetSpan);
|
|
118
|
+
if (end_timestamp) {
|
|
119
119
|
debugBuild.DEBUG_BUILD && core.debug.warn("[React Router] Lazy handler resolved after span ended - skipping update");
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
|
-
const spanOp =
|
|
122
|
+
const spanOp = attributes$1[attributes.SENTRY_OP];
|
|
123
123
|
let location = currentLocation;
|
|
124
124
|
if (!location && !capturedSpan) {
|
|
125
125
|
if (typeof browser.WINDOW !== "undefined") {
|
|
@@ -144,12 +144,11 @@ function processResolvedRoutes(resolvedRoutes, parentRoute, currentLocation = nu
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate = false, matchRoutes) {
|
|
147
|
-
const
|
|
148
|
-
const currentName = spanJson.description;
|
|
147
|
+
const { name: currentName, end_timestamp, attributes: attributes$1 } = core.spanToJSON(activeRootSpan);
|
|
149
148
|
const hasBeenNamed = activeRootSpan?.__sentry_navigation_name_set__;
|
|
150
149
|
const currentNameHasWildcard = currentName && utils.transactionNameHasWildcard(currentName);
|
|
151
150
|
const shouldUpdate = !hasBeenNamed || forceUpdate || currentNameHasWildcard;
|
|
152
|
-
if (shouldUpdate && !
|
|
151
|
+
if (shouldUpdate && !end_timestamp) {
|
|
153
152
|
const currentBranches = matchRoutes(allRoutes2, location);
|
|
154
153
|
const [name, source] = utils.resolveRouteNameAndSource(
|
|
155
154
|
location,
|
|
@@ -160,14 +159,16 @@ function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate
|
|
|
160
159
|
_lazyRouteManifest,
|
|
161
160
|
_enableAsyncRouteHandlers
|
|
162
161
|
);
|
|
163
|
-
const currentSource =
|
|
162
|
+
const currentSource = attributes$1[attributes.SENTRY_SEGMENT_NAME_SOURCE];
|
|
164
163
|
const isImprovement = name && (!currentName || // No current name - always set
|
|
165
164
|
!hasBeenNamed && (currentSource !== "route" || source === "route") || // Not finalized - allow unless downgrading route→url
|
|
166
165
|
currentSource !== "route" && source === "route" || // URL → route upgrade
|
|
167
166
|
currentSource === "route" && source === "route" && currentNameHasWildcard);
|
|
168
167
|
if (isImprovement) {
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
const client = core.getClient();
|
|
169
|
+
const isUnparameterizedStreamedNavigation = source !== "route" && !!client && core.hasSpanStreamingEnabled(client);
|
|
170
|
+
activeRootSpan.updateName(isUnparameterizedStreamedNavigation ? core.NAVIGATION_SPAN_NAME_FALLBACK : name);
|
|
171
|
+
activeRootSpan.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
171
172
|
if (source === "route") {
|
|
172
173
|
activeRootSpan.setAttribute(attributes.URL_TEMPLATE, name);
|
|
173
174
|
}
|
|
@@ -179,14 +180,14 @@ function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate
|
|
|
179
180
|
}
|
|
180
181
|
function setupRouterSubscription(router, routes, version, basename, activeRootSpan) {
|
|
181
182
|
let isInitialPageloadComplete = false;
|
|
182
|
-
let hasSeenPageloadSpan = !!activeRootSpan && core.spanToJSON(activeRootSpan).
|
|
183
|
+
let hasSeenPageloadSpan = !!activeRootSpan && core.spanToJSON(activeRootSpan).attributes[attributes.SENTRY_OP] === "pageload";
|
|
183
184
|
let hasSeenPopAfterPageload = false;
|
|
184
185
|
let scheduledNavigationHandler = null;
|
|
185
186
|
let lastHandledPathname = null;
|
|
186
187
|
router.subscribe((state) => {
|
|
187
188
|
if (!isInitialPageloadComplete) {
|
|
188
189
|
const currentRootSpan = utils.getActiveRootSpan();
|
|
189
|
-
const isCurrentlyInPageload = currentRootSpan && core.spanToJSON(currentRootSpan).
|
|
190
|
+
const isCurrentlyInPageload = currentRootSpan && core.spanToJSON(currentRootSpan).attributes[attributes.SENTRY_OP] === "pageload";
|
|
190
191
|
if (isCurrentlyInPageload) {
|
|
191
192
|
hasSeenPageloadSpan = true;
|
|
192
193
|
} else if (hasSeenPageloadSpan) {
|
|
@@ -372,9 +373,11 @@ function createReactRouterV6CompatibleTracingIntegration(options, version) {
|
|
|
372
373
|
const initPathName = browser.WINDOW.location?.pathname;
|
|
373
374
|
if (instrumentPageLoad && initPathName) {
|
|
374
375
|
browser.startBrowserTracingPageLoadSpan(client, {
|
|
375
|
-
|
|
376
|
+
// With span streaming, span names have to be low cardinality. The route is only resolved
|
|
377
|
+
// once the router renders, which updates the span name then.
|
|
378
|
+
name: core.hasSpanStreamingEnabled(client) ? core.PAGELOAD_SPAN_NAME_FALLBACK : initPathName,
|
|
376
379
|
attributes: {
|
|
377
|
-
[
|
|
380
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
378
381
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload",
|
|
379
382
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.pageload.react.reactrouter${version ? `_v${version}` : ""}`
|
|
380
383
|
}
|
|
@@ -462,8 +465,8 @@ function wrapPatchRoutesOnNavigation(opts, isMemoryRouter = false, capturedSpan)
|
|
|
462
465
|
}
|
|
463
466
|
}
|
|
464
467
|
const spanJson = activeRootSpan ? core.spanToJSON(activeRootSpan) : void 0;
|
|
465
|
-
if (targetPath && activeRootSpan && spanJson && !spanJson.
|
|
466
|
-
spanJson.
|
|
468
|
+
if (targetPath && activeRootSpan && spanJson && !spanJson.end_timestamp && // Span hasn't ended yet
|
|
469
|
+
spanJson.attributes[attributes.SENTRY_OP] === "navigation") {
|
|
467
470
|
updateNavigationSpan(
|
|
468
471
|
activeRootSpan,
|
|
469
472
|
{ pathname: targetPath, search: "", hash: "", state: null, key: "default" },
|
|
@@ -488,8 +491,8 @@ function wrapPatchRoutesOnNavigation(opts, isMemoryRouter = false, capturedSpan)
|
|
|
488
491
|
}
|
|
489
492
|
}
|
|
490
493
|
const spanJson = activeRootSpan ? core.spanToJSON(activeRootSpan) : void 0;
|
|
491
|
-
if (activeRootSpan && spanJson && !spanJson.
|
|
492
|
-
spanJson.
|
|
494
|
+
if (activeRootSpan && spanJson && !spanJson.end_timestamp && // Span hasn't ended yet
|
|
495
|
+
spanJson.attributes[attributes.SENTRY_OP] === "navigation") {
|
|
493
496
|
const pathname = targetPath;
|
|
494
497
|
if (pathname) {
|
|
495
498
|
updateNavigationSpan(
|
|
@@ -518,7 +521,7 @@ function handleNavigation(opts) {
|
|
|
518
521
|
return;
|
|
519
522
|
}
|
|
520
523
|
const activeRootSpan = utils.getActiveRootSpan();
|
|
521
|
-
if (activeRootSpan && core.spanToJSON(activeRootSpan).
|
|
524
|
+
if (activeRootSpan && core.spanToJSON(activeRootSpan).attributes[attributes.SENTRY_OP] === "pageload" && navigationType === "POP") {
|
|
522
525
|
return;
|
|
523
526
|
}
|
|
524
527
|
if ((navigationType === "PUSH" || navigationType === "POP") && branches) {
|
|
@@ -533,7 +536,7 @@ function handleNavigation(opts) {
|
|
|
533
536
|
);
|
|
534
537
|
const locationKey = computeLocationKey(location);
|
|
535
538
|
const trackedNav = activeNavigationSpans.get(client);
|
|
536
|
-
const trackedSpanHasEnded = trackedNav && !trackedNav.isPlaceholder ? !!core.spanToJSON(trackedNav.span).
|
|
539
|
+
const trackedSpanHasEnded = trackedNav && !trackedNav.isPlaceholder ? !!core.spanToJSON(trackedNav.span).end_timestamp : false;
|
|
537
540
|
const { skip, shouldUpdate } = shouldSkipNavigation(trackedNav, locationKey, name, trackedSpanHasEnded);
|
|
538
541
|
if (skip) {
|
|
539
542
|
if (shouldUpdate && trackedNav) {
|
|
@@ -544,8 +547,9 @@ function handleNavigation(opts) {
|
|
|
544
547
|
`[Tracing] Updated placeholder navigation name from "${oldName}" to "${name}" (will apply to real span)`
|
|
545
548
|
);
|
|
546
549
|
} else {
|
|
547
|
-
|
|
548
|
-
trackedNav.span.
|
|
550
|
+
const isUnparameterizedStreamedNavigation = source !== "route" && core.hasSpanStreamingEnabled(client);
|
|
551
|
+
trackedNav.span.updateName(isUnparameterizedStreamedNavigation ? core.NAVIGATION_SPAN_NAME_FALLBACK : name);
|
|
552
|
+
trackedNav.span.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
549
553
|
if (source === "route") {
|
|
550
554
|
trackedNav.span.setAttribute(attributes.URL_TEMPLATE, name);
|
|
551
555
|
}
|
|
@@ -571,10 +575,11 @@ function handleNavigation(opts) {
|
|
|
571
575
|
let navigationSpan;
|
|
572
576
|
try {
|
|
573
577
|
navigationSpan = browser.startBrowserTracingNavigationSpan(client, {
|
|
574
|
-
|
|
575
|
-
//
|
|
578
|
+
// Use placeholder's routeName in case it was updated. With span streaming, span names have to
|
|
579
|
+
// be low cardinality, so we can't fall back to the URL.
|
|
580
|
+
name: source === "route" || !core.hasSpanStreamingEnabled(client) ? placeholderEntry.routeName : core.NAVIGATION_SPAN_NAME_FALLBACK,
|
|
576
581
|
attributes: {
|
|
577
|
-
[
|
|
582
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: source,
|
|
578
583
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
|
|
579
584
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter${version ? `_v${version}` : ""}`,
|
|
580
585
|
...source === "route" && { [attributes.URL_TEMPLATE]: placeholderEntry.routeName }
|
|
@@ -649,8 +654,10 @@ function updatePageloadTransaction({
|
|
|
649
654
|
);
|
|
650
655
|
core.getCurrentScope().setTransactionName(name || "/");
|
|
651
656
|
if (activeRootSpan) {
|
|
652
|
-
|
|
653
|
-
|
|
657
|
+
const client = core.getClient();
|
|
658
|
+
const isUnparameterizedStreamedPageload = source !== "route" && !!client && core.hasSpanStreamingEnabled(client);
|
|
659
|
+
activeRootSpan.updateName(isUnparameterizedStreamedPageload ? core.PAGELOAD_SPAN_NAME_FALLBACK : name);
|
|
660
|
+
activeRootSpan.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
654
661
|
if (source === "route") {
|
|
655
662
|
activeRootSpan.setAttribute(attributes.URL_TEMPLATE, name);
|
|
656
663
|
}
|
|
@@ -678,7 +685,7 @@ function shouldUpdateWildcardSpanName(currentName, currentSource, newName, newSo
|
|
|
678
685
|
}
|
|
679
686
|
function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, routes, basename, spanType, allRoutes2) {
|
|
680
687
|
try {
|
|
681
|
-
const currentSource = spanJson.
|
|
688
|
+
const currentSource = spanJson.attributes[attributes.SENTRY_SEGMENT_NAME_SOURCE];
|
|
682
689
|
if (currentSource === "route" && currentName && !utils.transactionNameHasWildcard(currentName)) {
|
|
683
690
|
return;
|
|
684
691
|
}
|
|
@@ -698,10 +705,13 @@ function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, route
|
|
|
698
705
|
_enableAsyncRouteHandlers
|
|
699
706
|
);
|
|
700
707
|
const isImprovement = shouldUpdateWildcardSpanName(currentName, currentSource, name, source, true);
|
|
701
|
-
const spanNotEnded = spanType === "pageload" || !spanJson.
|
|
708
|
+
const spanNotEnded = spanType === "pageload" || !spanJson.end_timestamp;
|
|
702
709
|
if (isImprovement && spanNotEnded) {
|
|
703
|
-
|
|
704
|
-
|
|
710
|
+
const client = core.getClient();
|
|
711
|
+
const isUnparameterizedStreamedSpan = source !== "route" && !!client && core.hasSpanStreamingEnabled(client);
|
|
712
|
+
const fallbackName = spanType === "pageload" ? core.PAGELOAD_SPAN_NAME_FALLBACK : core.NAVIGATION_SPAN_NAME_FALLBACK;
|
|
713
|
+
span.updateName(isUnparameterizedStreamedSpan ? fallbackName : name);
|
|
714
|
+
span.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, source);
|
|
705
715
|
if (source === "route") {
|
|
706
716
|
span.setAttribute(attributes.URL_TEMPLATE, name);
|
|
707
717
|
}
|
|
@@ -725,8 +735,8 @@ function patchSpanEnd(span, location, routes, basename, spanType) {
|
|
|
725
735
|
endCalled = true;
|
|
726
736
|
const endTimestamp = args.length > 0 ? args[0] : Date.now() / 1e3;
|
|
727
737
|
const spanJson = core.spanToJSON(span);
|
|
728
|
-
const currentName = spanJson.
|
|
729
|
-
const currentSource = spanJson.
|
|
738
|
+
const currentName = spanJson.name;
|
|
739
|
+
const currentSource = spanJson.attributes[attributes.SENTRY_SEGMENT_NAME_SOURCE];
|
|
730
740
|
const cleanupNavigationSpan = () => {
|
|
731
741
|
const client = core.getClient();
|
|
732
742
|
if (client && spanType === "navigation") {
|
|
@@ -761,7 +771,7 @@ function patchSpanEnd(span, location, routes, basename, spanType) {
|
|
|
761
771
|
tryUpdateSpanNameBeforeEnd(
|
|
762
772
|
span,
|
|
763
773
|
updatedSpanJson,
|
|
764
|
-
updatedSpanJson.
|
|
774
|
+
updatedSpanJson.name,
|
|
765
775
|
location,
|
|
766
776
|
routes,
|
|
767
777
|
basename,
|