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:
Vemparala Surya Vamsi 2024-07-26 21:12:31 +05:30 committed by GitHub
parent 20aac6bdb6
commit 4bf6671cc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 6 deletions

View File

@ -18,6 +18,12 @@ export REACT_APP_SENTRY_RELEASE=$GIT_SHA
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
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"

View File

@ -39,6 +39,7 @@ const tracerProvider = new WebTracerProvider({
const nrTracesExporter = new OTLPTraceExporter({
url: `${otlpEndpoint}/v1/traces`,
compression: CompressionAlgorithm.GZIP,
headers: {
"api-key": otlpLicenseKey,
},

View File

@ -91,3 +91,15 @@ export function wrapFnWithParentTraceContext(parentSpan: Span, fn: () => any) {
const parentContext = trace.setSpan(context.active(), parentSpan);
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);
}

View 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>
);
};

View File

@ -3,7 +3,6 @@ import withMeta from "widgets/MetaHOC";
import { withLazyRender } from "widgets/withLazyRender";
import type BaseWidget from "widgets/BaseWidget";
import withWidgetProps from "widgets/withWidgetProps";
import * as Sentry from "@sentry/react";
import { withLayoutSystemWidgetHOC } from "../../layoutSystems/withLayoutSystemWidgetHOC";
import { flow, identity } from "lodash";
@ -26,8 +25,5 @@ export const withBaseWidgetHOC = (
// Adds/Enhances widget props
withWidgetProps,
// Wraps the widget to be profiled via sentry
Sentry.withProfiler,
])(Widget);
};

View File

@ -49,6 +49,9 @@ import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
import { isWidgetSelectedForPropertyPane } from "selectors/propertyPaneSelectors";
import WidgetFactory from "WidgetProvider/factory";
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_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;