Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add first load telemetry to evaluations #33989

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 13 additions & 20 deletions app/client/src/UITelemetry/generateTraces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { matchBuilderPath, matchViewerPath } from "constants/routes";

const GENERATOR_TRACE = "generator-tracer";

export type OtlpSpan = Span;
export type SpanAttributes = Attributes;

const getCommonTelemetryAttributes = () => {
const pathname = window.location.pathname;
const isEditorUrl = matchBuilderPath(pathname);
Expand All @@ -33,16 +36,13 @@ const getCommonTelemetryAttributes = () => {

export function startRootSpan(
spanName: string,
spanAttributes: Attributes = {},
spanAttributes: SpanAttributes = {},
startTime?: TimeInput,
) {
const tracer = trace.getTracer(GENERATOR_TRACE);
if (!spanName) {
return;
}
const commonAttributes = getCommonTelemetryAttributes();

return tracer?.startSpan(spanName, {
return tracer.startSpan(spanName, {
kind: SpanKind.CLIENT,
attributes: {
...commonAttributes,
Expand All @@ -56,15 +56,10 @@ export const generateContext = (span: Span) => {
};
export function startNestedSpan(
spanName: string,
parentSpan?: Span,
spanAttributes: Attributes = {},
parentSpan: Span,
spanAttributes: SpanAttributes = {},
startTime?: TimeInput,
) {
if (!spanName || !parentSpan) {
// do not generate nested span without parentSpan..we cannot generate context out of it
return;
}

const parentContext = generateContext(parentSpan);

const generatorTrace = trace.getTracer(GENERATOR_TRACE);
Expand All @@ -81,20 +76,18 @@ export function startNestedSpan(
return generatorTrace.startSpan(spanName, spanOptions, parentContext);
}

export function endSpan(span?: Span) {
span?.end();
export function endSpan(span: Span) {
span.end();
}

export function setAttributesToSpan(span: Span, spanAttributes: Attributes) {
if (!span) {
return;
}
export function setAttributesToSpan(
span: Span,
spanAttributes: SpanAttributes,
) {
span.setAttributes(spanAttributes);
}

export function wrapFnWithParentTraceContext(parentSpan: Span, fn: () => any) {
const parentContext = trace.setSpan(context.active(), parentSpan);
return context.with(parentContext, fn);
}

export type OtlpSpan = Span;
24 changes: 18 additions & 6 deletions app/client/src/UITelemetry/generateWebWorkerTraces.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { OtlpSpan, SpanAttributes } from "./generateTraces";
import { startNestedSpan } from "./generateTraces";
import type { TimeInput, Attributes, Span } from "@opentelemetry/api";
import type { TimeInput } from "@opentelemetry/api";

export interface WebworkerSpanData {
attributes: Attributes;
attributes: SpanAttributes;
spanName: string;
startTime: TimeInput;
endTime: TimeInput;
Expand All @@ -13,7 +14,7 @@ export interface WebworkerSpanData {
//to regular otlp telemetry data and subsequently exported to our telemetry collector
export const newWebWorkerSpanData = (
spanName: string,
attributes: Attributes,
attributes: SpanAttributes,
): WebworkerSpanData => {
return {
attributes,
Expand All @@ -29,8 +30,8 @@ const addEndTimeForWebWorkerSpanData = (span: WebworkerSpanData) => {

export const profileFn = (
spanName: string,
attributes: Attributes = {},
allSpans: Record<string, WebworkerSpanData>,
attributes: SpanAttributes = {},
allSpans: Record<string, WebworkerSpanData | SpanAttributes>,
fn: (...args: any[]) => any,
) => {
const span = newWebWorkerSpanData(spanName, attributes);
Expand All @@ -42,7 +43,7 @@ export const profileFn = (

//convert webworker spans to OTLP spans
export const convertWebworkerSpansToRegularSpans = (
parentSpan: Span,
parentSpan: OtlpSpan,
allSpans: Record<string, WebworkerSpanData> = {},
) => {
Object.values(allSpans)
Expand All @@ -53,3 +54,14 @@ export const convertWebworkerSpansToRegularSpans = (
span?.end(endTime);
});
};

export const filterSpanData = (
spanData: Record<string, WebworkerSpanData | SpanAttributes>,
): Record<string, WebworkerSpanData> => {
return Object.keys(spanData)
.filter((key) => !key.startsWith("__"))
.reduce<Record<string, WebworkerSpanData>>((obj, key) => {
Comment on lines +61 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we performing this filter operation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Webworker is sending sub span evaluation data and attributes for the root span in the same object. The attributes start with __. This piece of code returns sub span evaluation data to be processed as spans

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay got it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please approve the PR if the changes look good ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

obj[key] = spanData[key] as WebworkerSpanData;
return obj;
}, {});
};
3 changes: 2 additions & 1 deletion app/client/src/ce/workers/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { WebworkerSpanData } from "UITelemetry/generateWebWorkerTraces";
import type { SpanAttributes } from "UITelemetry/generateTraces";

export enum AppsmithWorkers {
LINT_WORKER = "LINT_WORKER",
Expand All @@ -12,5 +13,5 @@ export enum WorkerErrorTypes {
export interface WorkerRequest<TData, TActions> {
method: TActions;
data: TData;
webworkerTelemetry: Record<string, WebworkerSpanData>;
webworkerTelemetry: Record<string, WebworkerSpanData | SpanAttributes>;
}
4 changes: 3 additions & 1 deletion app/client/src/sagas/ActionExecution/PluginActionSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,9 @@ function* handleUpdateActionData(
EVAL_WORKER_ACTIONS.UPDATE_ACTION_DATA,
actionDataPayload,
);
endSpan(parentSpan);
if (parentSpan) {
endSpan(parentSpan);
}
}

export function* watchPluginActionExecutionSagas() {
Expand Down
71 changes: 48 additions & 23 deletions app/client/src/utils/WorkerUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import { uniqueId } from "lodash";
import log from "loglevel";
import type { TMessage } from "./MessageUtil";
import { MessageType, sendMessage } from "./MessageUtil";
import type { OtlpSpan } from "UITelemetry/generateTraces";
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
import type { OtlpSpan, SpanAttributes } from "UITelemetry/generateTraces";
import {
endSpan,
setAttributesToSpan,
startRootSpan,
} from "UITelemetry/generateTraces";
import type { WebworkerSpanData } from "UITelemetry/generateWebWorkerTraces";
import {
convertWebworkerSpansToRegularSpans,
filterSpanData,
newWebWorkerSpanData,
} from "UITelemetry/generateWebWorkerTraces";

Expand Down Expand Up @@ -156,33 +161,35 @@ export class GracefulWorkerService {
startTime,
webworkerTelemetry,
}: {
webworkerTelemetry: Record<string, WebworkerSpanData>;
webworkerTelemetry:
| Record<string, WebworkerSpanData | SpanAttributes>
| undefined;
rootSpan: OtlpSpan | undefined;
method: string;
startTime: number;
endTime: number;
}) {
const webworkerTelemetryResponse = webworkerTelemetry as Record<
string,
WebworkerSpanData
>;
if (!webworkerTelemetry) {
return;
}

if (webworkerTelemetryResponse) {
const { transferDataToMainThread } = webworkerTelemetryResponse;
if (transferDataToMainThread) {
transferDataToMainThread.endTime = Date.now();
}
/// Add the completeWebworkerComputation span to the root span
webworkerTelemetryResponse["completeWebworkerComputation"] = {
startTime,
endTime,
attributes: {},
spanName: "completeWebworkerComputation",
};
const { transferDataToMainThread } = webworkerTelemetry;
if (transferDataToMainThread) {
transferDataToMainThread.endTime = Date.now();
}
/// Add the completeWebworkerComputation span to the root span
webworkerTelemetry["completeWebworkerComputation"] = {
startTime,
endTime,
attributes: {},
spanName: "completeWebworkerComputation",
};
//we are attaching the child spans to the root span over here
rootSpan &&
convertWebworkerSpansToRegularSpans(rootSpan, webworkerTelemetryResponse);
convertWebworkerSpansToRegularSpans(
rootSpan,
filterSpanData(webworkerTelemetry),
);

//genereate separate completeWebworkerComputationRoot root span
// this span does not contain any child spans, it just captures the webworker computation alone
Expand Down Expand Up @@ -218,11 +225,15 @@ export class GracefulWorkerService {
let timeTaken;
const rootSpan = startRootSpan(method);

const webworkerTelemetryData: Record<string, WebworkerSpanData> = {
const webworkerTelemetryData: Record<
string,
WebworkerSpanData | SpanAttributes
> = {
transferDataToWorkerThread: newWebWorkerSpanData(
"transferDataToWorkerThread",
{},
),
__spanAttributes: {},
};

const body = {
Expand All @@ -231,6 +242,11 @@ export class GracefulWorkerService {
webworkerTelemetry: webworkerTelemetryData,
};

let webworkerTelemetryResponse: Record<
string,
WebworkerSpanData | SpanAttributes
> = {};

try {
sendMessage.call(this._Worker, {
messageType: MessageType.REQUEST,
Expand All @@ -241,9 +257,10 @@ export class GracefulWorkerService {
// The `this._broker` method is listening to events and will pass response to us over this channel.
const response = yield take(ch);
const { data, endTime, startTime } = response;
const { webworkerTelemetry } = data;
webworkerTelemetryResponse = data.webworkerTelemetry;

this.addChildSpansToRootSpan({
webworkerTelemetry,
webworkerTelemetry: webworkerTelemetryResponse,
rootSpan,
method,
startTime,
Expand All @@ -268,6 +285,14 @@ export class GracefulWorkerService {
log.debug(` Worker ${method} took ${timeTaken}ms`);
log.debug(` Transfer ${method} took ${transferTime}ms`);
}

if (webworkerTelemetryResponse) {
setAttributesToSpan(
rootSpan,
webworkerTelemetryResponse.__spanAttributes as SpanAttributes,
);
}

endSpan(rootSpan);
// Cleanup
ch.close();
Expand Down
4 changes: 4 additions & 0 deletions app/client/src/workers/Evaluation/handlers/evalTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
profileFn,
newWebWorkerSpanData,
} from "UITelemetry/generateWebWorkerTraces";
import type { SpanAttributes } from "UITelemetry/generateTraces";
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";

Expand Down Expand Up @@ -85,6 +86,9 @@ export function evalTree(request: EvalWorkerSyncRequest) {
let isNewTree = false;

try {
(webworkerTelemetry.__spanAttributes as SpanAttributes)["firstEvaluation"] =
!dataTreeEvaluator;

if (!dataTreeEvaluator) {
isCreateFirstTree = true;
replayMap = replayMap || {};
Expand Down
3 changes: 2 additions & 1 deletion app/client/src/workers/Evaluation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { WorkerRequest } from "@appsmith/workers/common/types";
import type { DataTreeDiff } from "@appsmith/workers/Evaluation/evaluationUtils";
import type { APP_MODE } from "entities/App";
import type { WebworkerSpanData } from "UITelemetry/generateWebWorkerTraces";
import type { SpanAttributes } from "UITelemetry/generateTraces";
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";

export type EvalWorkerSyncRequest<T = any> = WorkerRequest<
Expand Down Expand Up @@ -59,6 +60,6 @@ export interface EvalTreeResponseData {
isNewWidgetAdded: boolean;
undefinedEvalValuesMap: Record<string, boolean>;
jsVarsCreatedEvent?: { path: string; type: string }[];
webworkerTelemetry?: Record<string, WebworkerSpanData>;
webworkerTelemetry?: Record<string, WebworkerSpanData | SpanAttributes>;
updates: string;
}
5 changes: 3 additions & 2 deletions app/client/src/workers/common/DataTreeEvaluator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ import {
profileFn,
type WebworkerSpanData,
} from "UITelemetry/generateWebWorkerTraces";
import type { SpanAttributes } from "UITelemetry/generateTraces";
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";
import generateOverrideContext from "@appsmith/workers/Evaluation/generateOverrideContext";

Expand Down Expand Up @@ -233,7 +234,7 @@ export default class DataTreeEvaluator {
setupFirstTree(
unEvalTree: any,
configTree: ConfigTree,
webworkerTelemetry: Record<string, WebworkerSpanData> = {},
webworkerTelemetry: Record<string, WebworkerSpanData | SpanAttributes> = {},
): {
jsUpdates: Record<string, JSUpdate>;
evalOrder: string[];
Expand Down Expand Up @@ -489,7 +490,7 @@ export default class DataTreeEvaluator {
setupUpdateTree(
unEvalTree: any,
configTree: ConfigTree,
webworkerTelemetry: Record<string, WebworkerSpanData> = {},
webworkerTelemetry: Record<string, WebworkerSpanData | SpanAttributes> = {},
affectedJSObjects: AffectedJSObjects = { isAllAffected: false, ids: [] },
): {
unEvalUpdates: DataTreeDiff[];
Expand Down
Loading