chore: Added profiling for widget components performance (#34912)
## Description Capturing telemetry of widget renders and only enabling the profiled build for community users. This should cause a minor performance overhead and in case performance degrades too much we can disable the widget profiled telemetry by disabling the new relic flag(set this to false `APPSMITH_NEW_RELIC_ACCOUNT_ENABLE`). Fixes #35184 > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Sanity" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10111247316> > Commit: 38310d7341e7a2acb30491e478edaedbbc16a739 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10111247316&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Sanity` > Spec: > <hr>Fri, 26 Jul 2024 12:55:43 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Introduced `WidgetProfiler` component to track rendering performance metrics. - Added a new function for enhanced telemetry span management. - **Improvements** - Updated build script to allow for profiled builds based on environment variables, improving performance analysis. - Implemented GZIP compression for OTLP trace data transmission, optimizing bandwidth usage. - **Refactor** - Removed `Sentry.withProfiler` for widget profiling, simplifying performance monitoring. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
20aac6bdb6
commit
4bf6671cc4
|
|
@ -18,6 +18,12 @@ export REACT_APP_SENTRY_RELEASE=$GIT_SHA
|
||||||
export REACT_APP_CLIENT_LOG_LEVEL=ERROR
|
export REACT_APP_CLIENT_LOG_LEVEL=ERROR
|
||||||
# Disable CRA built-in ESLint checks since we have our own config and a separate step for this
|
# Disable CRA built-in ESLint checks since we have our own config and a separate step for this
|
||||||
export DISABLE_ESLINT_PLUGIN=true
|
export DISABLE_ESLINT_PLUGIN=true
|
||||||
craco --max-old-space-size=7168 build --config craco.build.config.js
|
if [ "$APPSMITH_CLOUD_HOSTING" == "true" ]; then
|
||||||
|
echo "Building profiled build"
|
||||||
|
craco --max-old-space-size=7168 build --profile --config craco.build.config.js --verbose
|
||||||
|
else
|
||||||
|
craco --max-old-space-size=7168 build --config craco.build.config.js
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo "build finished"
|
echo "build finished"
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ const tracerProvider = new WebTracerProvider({
|
||||||
|
|
||||||
const nrTracesExporter = new OTLPTraceExporter({
|
const nrTracesExporter = new OTLPTraceExporter({
|
||||||
url: `${otlpEndpoint}/v1/traces`,
|
url: `${otlpEndpoint}/v1/traces`,
|
||||||
|
compression: CompressionAlgorithm.GZIP,
|
||||||
headers: {
|
headers: {
|
||||||
"api-key": otlpLicenseKey,
|
"api-key": otlpLicenseKey,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -91,3 +91,15 @@ export function wrapFnWithParentTraceContext(parentSpan: Span, fn: () => any) {
|
||||||
const parentContext = trace.setSpan(context.active(), parentSpan);
|
const parentContext = trace.setSpan(context.active(), parentSpan);
|
||||||
return context.with(parentContext, fn);
|
return context.with(parentContext, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function startAndEndSpan(
|
||||||
|
spanName: string,
|
||||||
|
startTime: number,
|
||||||
|
difference: number,
|
||||||
|
spanAttributes: SpanAttributes = {},
|
||||||
|
) {
|
||||||
|
const endTime = startTime + Math.floor(difference);
|
||||||
|
|
||||||
|
const span = startRootSpan(spanName, spanAttributes, startTime);
|
||||||
|
span.end(endTime);
|
||||||
|
}
|
||||||
|
|
|
||||||
27
app/client/src/widgets/BaseWidgetHOC/WidgetProfiler.tsx
Normal file
27
app/client/src/widgets/BaseWidgetHOC/WidgetProfiler.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React, { Profiler, useCallback } from "react";
|
||||||
|
import { startAndEndSpan } from "UITelemetry/generateTraces";
|
||||||
|
export const WidgetProfiler = ({
|
||||||
|
children,
|
||||||
|
type,
|
||||||
|
widgetId,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
type: string;
|
||||||
|
widgetId: string;
|
||||||
|
}) => {
|
||||||
|
const onRender = useCallback(
|
||||||
|
(id: string, phase: string, actualDuration: number) => {
|
||||||
|
startAndEndSpan("widgetRender", Date.now(), actualDuration, {
|
||||||
|
widgetType: type,
|
||||||
|
// mount or update phase
|
||||||
|
phase,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[type],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Profiler id={widgetId} onRender={onRender}>
|
||||||
|
{children}
|
||||||
|
</Profiler>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -3,7 +3,6 @@ import withMeta from "widgets/MetaHOC";
|
||||||
import { withLazyRender } from "widgets/withLazyRender";
|
import { withLazyRender } from "widgets/withLazyRender";
|
||||||
import type BaseWidget from "widgets/BaseWidget";
|
import type BaseWidget from "widgets/BaseWidget";
|
||||||
import withWidgetProps from "widgets/withWidgetProps";
|
import withWidgetProps from "widgets/withWidgetProps";
|
||||||
import * as Sentry from "@sentry/react";
|
|
||||||
import { withLayoutSystemWidgetHOC } from "../../layoutSystems/withLayoutSystemWidgetHOC";
|
import { withLayoutSystemWidgetHOC } from "../../layoutSystems/withLayoutSystemWidgetHOC";
|
||||||
import { flow, identity } from "lodash";
|
import { flow, identity } from "lodash";
|
||||||
|
|
||||||
|
|
@ -26,8 +25,5 @@ export const withBaseWidgetHOC = (
|
||||||
|
|
||||||
// Adds/Enhances widget props
|
// Adds/Enhances widget props
|
||||||
withWidgetProps,
|
withWidgetProps,
|
||||||
|
|
||||||
// Wraps the widget to be profiled via sentry
|
|
||||||
Sentry.withProfiler,
|
|
||||||
])(Widget);
|
])(Widget);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
|
||||||
import { isWidgetSelectedForPropertyPane } from "selectors/propertyPaneSelectors";
|
import { isWidgetSelectedForPropertyPane } from "selectors/propertyPaneSelectors";
|
||||||
import WidgetFactory from "WidgetProvider/factory";
|
import WidgetFactory from "WidgetProvider/factory";
|
||||||
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
|
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
|
||||||
|
import { WidgetProfiler } from "./BaseWidgetHOC/WidgetProfiler";
|
||||||
|
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||||
|
const { newRelic } = getAppsmithConfigs();
|
||||||
|
|
||||||
const WIDGETS_WITH_CHILD_WIDGETS = ["LIST_WIDGET", "FORM_WIDGET"];
|
const WIDGETS_WITH_CHILD_WIDGETS = ["LIST_WIDGET", "FORM_WIDGET"];
|
||||||
const WIDGETS_REQUIRING_SELECTED_ANCESTRY = ["MODAL_WIDGET", "TABS_WIDGET"];
|
const WIDGETS_REQUIRING_SELECTED_ANCESTRY = ["MODAL_WIDGET", "TABS_WIDGET"];
|
||||||
|
|
@ -353,7 +356,15 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <WrappedWidget {...widgetProps} />;
|
if (!newRelic.enableNewRelic) {
|
||||||
|
return <WrappedWidget {...widgetProps} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WidgetProfiler type={type} widgetId={widgetId}>
|
||||||
|
<WrappedWidget {...widgetProps} />
|
||||||
|
</WidgetProfiler>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WrappedPropsComponent;
|
return WrappedPropsComponent;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user