123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- Object.defineProperty(exports, '__esModule', { value: true });
- const browser = require('@sentry/browser');
- const core = require('@sentry/core');
- const utils = require('@sentry/utils');
- const hoistNonReactStatics = require('hoist-non-react-statics');
- const React = require('react');
- const constants = require('./constants.js');
- const _interopDefault = e => e && e.__esModule ? e.default : e;
- function _interopNamespace(e) {
- if (e && e.__esModule) return e;
- const n = Object.create(null);
- if (e) {
- for (const k in e) {
- n[k] = e[k];
- }
- }
- n.default = e;
- return n;
- }
- const hoistNonReactStatics__default = /*#__PURE__*/_interopDefault(hoistNonReactStatics);
- const React__namespace = /*#__PURE__*/_interopNamespace(React);
- const _jsxFileName = "/home/runner/work/sentry-javascript/sentry-javascript/packages/react/src/profiler.tsx";
- const UNKNOWN_COMPONENT = 'unknown';
- /**
- * The Profiler component leverages Sentry's Tracing integration to generate
- * spans based on component lifecycles.
- */
- class Profiler extends React__namespace.Component {
- /**
- * The span of the mount activity
- * Made protected for the React Native SDK to access
- */
- /**
- * The span that represents the duration of time between shouldComponentUpdate and componentDidUpdate
- */
- // eslint-disable-next-line @typescript-eslint/member-ordering
- static __initStatic() {this.defaultProps = {
- disabled: false,
- includeRender: true,
- includeUpdates: true,
- };}
- constructor(props) {
- super(props);
- const { name, disabled = false } = this.props;
- if (disabled) {
- return;
- }
- this._mountSpan = browser.startInactiveSpan({
- name: `<${name}>`,
- onlyIfParent: true,
- op: constants.REACT_MOUNT_OP,
- origin: 'auto.ui.react.profiler',
- attributes: { 'ui.component_name': name },
- });
- }
- // If a component mounted, we can finish the mount activity.
- componentDidMount() {
- if (this._mountSpan) {
- this._mountSpan.end();
- }
- }
- shouldComponentUpdate({ updateProps, includeUpdates = true }) {
- // Only generate an update span if includeUpdates is true, if there is a valid mountSpan,
- // and if the updateProps have changed. It is ok to not do a deep equality check here as it is expensive.
- // We are just trying to give baseline clues for further investigation.
- if (includeUpdates && this._mountSpan && updateProps !== this.props.updateProps) {
- // See what props haved changed between the previous props, and the current props. This is
- // set as data on the span. We just store the prop keys as the values could be potenially very large.
- const changedProps = Object.keys(updateProps).filter(k => updateProps[k] !== this.props.updateProps[k]);
- if (changedProps.length > 0) {
- const now = utils.timestampInSeconds();
- this._updateSpan = core.withActiveSpan(this._mountSpan, () => {
- return browser.startInactiveSpan({
- name: `<${this.props.name}>`,
- onlyIfParent: true,
- op: constants.REACT_UPDATE_OP,
- origin: 'auto.ui.react.profiler',
- startTimestamp: now,
- attributes: {
- 'ui.component_name': this.props.name,
- 'ui.react.changed_props': changedProps,
- },
- });
- });
- }
- }
- return true;
- }
- componentDidUpdate() {
- if (this._updateSpan) {
- this._updateSpan.end();
- this._updateSpan = undefined;
- }
- }
- // If a component is unmounted, we can say it is no longer on the screen.
- // This means we can finish the span representing the component render.
- componentWillUnmount() {
- const endTimestamp = utils.timestampInSeconds();
- const { name, includeRender = true } = this.props;
- if (this._mountSpan && includeRender) {
- const startTimestamp = core.spanToJSON(this._mountSpan).timestamp;
- core.withActiveSpan(this._mountSpan, () => {
- const renderSpan = browser.startInactiveSpan({
- onlyIfParent: true,
- name: `<${name}>`,
- op: constants.REACT_RENDER_OP,
- origin: 'auto.ui.react.profiler',
- startTimestamp,
- attributes: { 'ui.component_name': name },
- });
- if (renderSpan) {
- // Have to cast to Span because the type of _mountSpan is Span | undefined
- // and not getting narrowed properly
- renderSpan.end(endTimestamp);
- }
- });
- }
- }
- render() {
- return this.props.children;
- }
- } Profiler.__initStatic();
- /**
- * withProfiler is a higher order component that wraps a
- * component in a {@link Profiler} component. It is recommended that
- * the higher order component be used over the regular {@link Profiler} component.
- *
- * @param WrappedComponent component that is wrapped by Profiler
- * @param options the {@link ProfilerProps} you can pass into the Profiler
- */
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- function withProfiler(
- WrappedComponent,
- // We do not want to have `updateProps` given in options, it is instead filled through the HOC.
- options,
- ) {
- const componentDisplayName =
- (options && options.name) || WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
- const Wrapped = (props) => (
- React__namespace.createElement(Profiler, { ...options, name: componentDisplayName, updateProps: props, __self: this, __source: {fileName: _jsxFileName, lineNumber: 159}}
- , React__namespace.createElement(WrappedComponent, { ...props, __self: this, __source: {fileName: _jsxFileName, lineNumber: 160}} )
- )
- );
- Wrapped.displayName = `profiler(${componentDisplayName})`;
- // Copy over static methods from Wrapped component to Profiler HOC
- // See: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
- hoistNonReactStatics__default(Wrapped, WrappedComponent);
- return Wrapped;
- }
- /**
- *
- * `useProfiler` is a React hook that profiles a React component.
- *
- * Requires React 16.8 or above.
- * @param name displayName of component being profiled
- */
- function useProfiler(
- name,
- options = {
- disabled: false,
- hasRenderSpan: true,
- },
- ) {
- const [mountSpan] = React__namespace.useState(() => {
- if (options && options.disabled) {
- return undefined;
- }
- return browser.startInactiveSpan({
- name: `<${name}>`,
- onlyIfParent: true,
- op: constants.REACT_MOUNT_OP,
- origin: 'auto.ui.react.profiler',
- attributes: { 'ui.component_name': name },
- });
- });
- React__namespace.useEffect(() => {
- if (mountSpan) {
- mountSpan.end();
- }
- return () => {
- if (mountSpan && options.hasRenderSpan) {
- const startTimestamp = core.spanToJSON(mountSpan).timestamp;
- const endTimestamp = utils.timestampInSeconds();
- const renderSpan = browser.startInactiveSpan({
- name: `<${name}>`,
- onlyIfParent: true,
- op: constants.REACT_RENDER_OP,
- origin: 'auto.ui.react.profiler',
- startTimestamp,
- attributes: { 'ui.component_name': name },
- });
- if (renderSpan) {
- // Have to cast to Span because the type of _mountSpan is Span | undefined
- // and not getting narrowed properly
- renderSpan.end(endTimestamp);
- }
- }
- };
- // We only want this to run once.
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- }
- exports.Profiler = Profiler;
- exports.UNKNOWN_COMPONENT = UNKNOWN_COMPONENT;
- exports.useProfiler = useProfiler;
- exports.withProfiler = withProfiler;
- //# sourceMappingURL=profiler.js.map
|