Skip to content

Commit e6e38a5

Browse files
JLargent42Jacob Largent
andauthored
fix(appcomposer): add main Application Composer telemetry (#1559)
## Problem: Application Composer does not have much telemetry, so we are not able to easily determine potential problems or pain points. ## Solution: This adds telemetry events for load time, FPS, add resource, add connection, open/close WFS, and generate failed. --------- Co-authored-by: Jacob Largent <[email protected]>
1 parent c50c7a6 commit e6e38a5

File tree

4 files changed

+276
-30
lines changed

4 files changed

+276
-30
lines changed

src/applicationcomposer/messageHandlers/deployMessageHandler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import { telemetry } from '../../shared/telemetry/telemetry'
1010

1111
export async function deployMessageHandler(message: DeployRequestMessage, context: WebviewContext) {
1212
// SAM already handles success/failure, so we only log that the user clicked the deploy button
13-
telemetry.appcomposer_deployClicked.emit()
13+
telemetry.appcomposer_deployClicked.emit({
14+
result: 'Succeeded',
15+
})
1416
const args = context.textDocument.uri
1517
/* TODO Rework this command so that a failure case is returned
1618
* We don't want to override the SAM Sync telemetry. The SAM telemetry catches all errors,

src/applicationcomposer/messageHandlers/emitTelemetryMessageHandler.ts

Lines changed: 150 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,157 @@
44
*/
55

66
import { EmitTelemetryMessage } from '../types'
7-
import { telemetry } from '../../shared/telemetry/telemetry'
7+
import {
8+
AppcomposerAddConnection,
9+
AppcomposerAddResource,
10+
AppcomposerCloseWfs,
11+
AppcomposerCustomerReady,
12+
AppcomposerFps,
13+
AppcomposerGenerateAccepted,
14+
AppcomposerGenerateClicked,
15+
AppcomposerGenerateRejected,
16+
AppcomposerInvalidGeneration,
17+
AppcomposerOpenWfs,
18+
AppcomposerPostProcess,
19+
AppcomposerRegenerateClicked,
20+
telemetry,
21+
} from '../../shared/telemetry/telemetry'
22+
import { getLogger } from '../../shared/logger'
823

924
export function emitTelemetryMessageHandler(message: EmitTelemetryMessage) {
10-
switch (message.eventType) {
11-
case 'GENERATE_CLICKED':
12-
telemetry.appcomposer_generateClicked.emit({
13-
result: 'Succeeded',
14-
resourceType: message.resourceId,
15-
})
16-
return
17-
case 'REGENERATE_CLICKED':
18-
telemetry.appcomposer_regenerateClicked.emit({
19-
result: 'Succeeded',
20-
resourceType: message.resourceId,
21-
})
22-
return
23-
case 'GENERATE_ACCEPTED':
24-
telemetry.appcomposer_generateAccepted.emit({
25-
result: 'Succeeded',
26-
resourceType: message.resourceId,
27-
})
28-
return
29-
case 'GENERATE_REJECTED':
30-
telemetry.appcomposer_generateRejected.emit({
31-
result: 'Succeeded',
32-
resourceType: message.resourceId,
33-
})
34-
return
25+
try {
26+
const parsedData = message.metadata ? JSON.parse(message.metadata) : {}
27+
switch (message.eventType) {
28+
case 'GENERATE_CLICKED':
29+
sendGenerateClicked(parsedData as AppcomposerGenerateClicked)
30+
return
31+
case 'REGENERATE_CLICKED':
32+
sendRegenerateClicked(parsedData as AppcomposerRegenerateClicked)
33+
return
34+
case 'GENERATE_ACCEPTED':
35+
sendGenerateAccepted(parsedData as AppcomposerGenerateAccepted)
36+
return
37+
case 'GENERATE_REJECTED':
38+
sendGenerateRejected(parsedData as AppcomposerGenerateRejected)
39+
return
40+
case 'INVALID_GENERATION':
41+
sendInvalidGeneration(parsedData as AppcomposerInvalidGeneration)
42+
return
43+
case 'POST_PROCESS':
44+
sendPostProcess(parsedData as AppcomposerPostProcess)
45+
return
46+
case 'CUSTOMER_READY':
47+
sendCustomerReady(parsedData as AppcomposerCustomerReady)
48+
return
49+
case 'FPS':
50+
sendFps(parsedData as AppcomposerFps)
51+
return
52+
case 'ADD_RESOURCE':
53+
sendAddResource(parsedData as AppcomposerAddResource)
54+
return
55+
case 'ADD_CONNECTION':
56+
sendAddConnection(parsedData as AppcomposerAddConnection)
57+
return
58+
case 'OPEN_WFS':
59+
sendOpenWfs(parsedData as AppcomposerOpenWfs)
60+
return
61+
case 'CLOSE_WFS':
62+
sendCloseWfs(parsedData as AppcomposerCloseWfs)
63+
return
64+
}
65+
} catch (e) {
66+
getLogger().error('Could not log telemetry for App Composer', e)
3567
}
3668
}
69+
70+
function sendGenerateClicked(metadata: AppcomposerGenerateClicked) {
71+
telemetry.appcomposer_generateClicked.emit({
72+
result: metadata.result ?? 'Succeeded',
73+
resourceType: metadata.resourceType,
74+
})
75+
}
76+
77+
function sendRegenerateClicked(metadata: AppcomposerRegenerateClicked) {
78+
telemetry.appcomposer_regenerateClicked.emit({
79+
result: metadata.result ?? 'Succeeded',
80+
resourceType: metadata.resourceType,
81+
})
82+
}
83+
84+
function sendGenerateAccepted(metadata: AppcomposerGenerateAccepted) {
85+
telemetry.appcomposer_generateAccepted.emit({
86+
result: metadata.result ?? 'Succeeded',
87+
resourceType: metadata.resourceType,
88+
numAttempts: metadata.numAttempts,
89+
})
90+
}
91+
92+
function sendGenerateRejected(metadata: AppcomposerGenerateRejected) {
93+
telemetry.appcomposer_generateRejected.emit({
94+
result: metadata.result ?? 'Succeeded',
95+
resourceType: metadata.resourceType,
96+
numAttempts: metadata.numAttempts,
97+
})
98+
}
99+
100+
function sendInvalidGeneration(metadata: AppcomposerInvalidGeneration) {
101+
telemetry.appcomposer_invalidGeneration.emit({
102+
result: metadata.result ?? 'Succeeded',
103+
resourceType: metadata.resourceType,
104+
generateFailure: metadata.generateFailure,
105+
})
106+
}
107+
108+
function sendPostProcess(metadata: AppcomposerPostProcess) {
109+
telemetry.appcomposer_postProcess.emit({
110+
result: metadata.result ?? 'Succeeded',
111+
resourceType: metadata.resourceType,
112+
pathsScrubbed: metadata.pathsScrubbed,
113+
})
114+
}
115+
116+
function sendCustomerReady(metadata: AppcomposerCustomerReady) {
117+
telemetry.appcomposer_customerReady.emit({
118+
result: metadata.result ?? 'Succeeded',
119+
loadFileTime: metadata.loadFileTime,
120+
initializeTime: metadata.initializeTime,
121+
saveFileTime: metadata.saveFileTime,
122+
})
123+
}
124+
125+
function sendFps(metadata: AppcomposerFps) {
126+
telemetry.appcomposer_fps.emit({
127+
result: metadata.result ?? 'Succeeded',
128+
fps: metadata.fps,
129+
})
130+
}
131+
132+
function sendAddResource(metadata: AppcomposerAddResource) {
133+
telemetry.appcomposer_addResource.emit({
134+
result: metadata.result ?? 'Succeeded',
135+
resourceType: metadata.resourceType,
136+
})
137+
}
138+
139+
function sendAddConnection(metadata: AppcomposerAddConnection) {
140+
telemetry.appcomposer_addConnection.emit({
141+
result: metadata.result ?? 'Succeeded',
142+
sourceResourceType: metadata.sourceResourceType,
143+
sourceFacetType: metadata.sourceFacetType,
144+
destResourceType: metadata.destResourceType,
145+
destFacetType: metadata.destFacetType,
146+
})
147+
}
148+
149+
function sendOpenWfs(metadata: AppcomposerOpenWfs) {
150+
telemetry.appcomposer_openWfs.emit({
151+
result: metadata.result ?? 'Succeeded',
152+
})
153+
}
154+
155+
function sendCloseWfs(metadata: AppcomposerCloseWfs) {
156+
telemetry.appcomposer_closeWfs.emit({
157+
result: metadata.result ?? 'Succeeded',
158+
didSave: metadata.didSave,
159+
})
160+
}

src/applicationcomposer/types.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ export enum MessageType {
4242
BROADCAST = 'BROADCAST',
4343
}
4444

45+
enum TelemetryType {
46+
GENERATE_CLICKED = 'GENERATE_CLICKED',
47+
REGENERATE_CLICKED = 'REGENERATE_CLICKED',
48+
GENERATE_ACCEPTED = 'GENERATE_ACCEPTED',
49+
GENERATE_REJECTED = 'GENERATE_REJECTED',
50+
INVALID_GENERATION = 'INVALID_GENERATION',
51+
POST_PROCESS = 'POST_PROCESS',
52+
CUSTOMER_READY = 'CUSTOMER_READY',
53+
FPS = 'FPS',
54+
ADD_RESOURCE = 'ADD_RESOURCE',
55+
ADD_CONNECTION = 'ADD_CONNECTION',
56+
OPEN_WFS = 'OPEN_WFS',
57+
CLOSE_WFS = 'CLOSE_WFS',
58+
}
59+
4560
export interface InitResponseMessage extends Message {
4661
templateFileName: string
4762
templateFilePath: string
@@ -121,8 +136,8 @@ export interface LogMessage extends Message {
121136

122137
export interface EmitTelemetryMessage extends Message {
123138
eventId?: string
124-
eventType: 'GENERATE_CLICKED' | 'REGENERATE_CLICKED' | 'GENERATE_ACCEPTED' | 'GENERATE_REJECTED'
125-
resourceId: string
139+
eventType: TelemetryType
140+
metadata?: string
126141
}
127142

128143
export interface ReferenceDetails {

src/shared/telemetry/vscodeTelemetry.json

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,72 @@
5858
"type": "boolean",
5959
"description": "Whether the user has access to CodeWhisperer Chat"
6060
},
61+
{
62+
"name": "numAttempts",
63+
"type": "int",
64+
"description": "Number of generations before the user accepted or rejected"
65+
},
6166
{
6267
"name": "resourceType",
6368
"type": "string",
6469
"description": "CloudFormation resource type"
6570
},
71+
{
72+
"name": "pathsScrubbed",
73+
"type": "int",
74+
"description": "Number of generation paths modified"
75+
},
76+
{
77+
"name": "generateFailure",
78+
"type": "string",
79+
"allowedValues": ["Parse"],
80+
"description": "Type of generation failure"
81+
},
82+
{
83+
"name": "loadFileTime",
84+
"type": "int",
85+
"description": "Time from opening Composer to loading the file"
86+
},
87+
{
88+
"name": "initializeTime",
89+
"type": "int",
90+
"description": "Time from opening Composer to Composer initialize"
91+
},
92+
{
93+
"name": "saveFileTime",
94+
"type": "int",
95+
"description": "Time from opening Composer to saving the file"
96+
},
97+
{
98+
"name": "fps",
99+
"type": "int",
100+
"description": "Average frames per second"
101+
},
102+
{
103+
"name": "sourceResourceType",
104+
"type": "string",
105+
"description": "CloudFormation resource type of connection start"
106+
},
107+
{
108+
"name": "destResourceType",
109+
"type": "string",
110+
"description": "CloudFormation resource type of connection end"
111+
},
112+
{
113+
"name": "sourceFacetType",
114+
"type": "string",
115+
"description": "Resource subtype of connection start"
116+
},
117+
{
118+
"name": "destFacetType",
119+
"type": "string",
120+
"description": "Resource subtype of connection end"
121+
},
122+
{
123+
"name": "didSave",
124+
"type": "boolean",
125+
"description": "Whether the user saved"
126+
},
66127
{
67128
"name": "cwsprChatTriggerInteraction",
68129
"type": "string",
@@ -549,13 +610,57 @@
549610
{
550611
"name": "appcomposer_generateAccepted",
551612
"description": "Called after accepting a generative AI suggestion",
552-
"metadata": [{ "type": "resourceType" }]
613+
"metadata": [{ "type": "resourceType" }, { "type": "numAttempts" }]
553614
},
554615
{
555616
"name": "appcomposer_generateRejected",
556617
"description": "Called after rejecting a generative AI suggestion",
618+
"metadata": [{ "type": "resourceType" }, { "type": "numAttempts" }]
619+
},
620+
{
621+
"name": "appcomposer_invalidGeneration",
622+
"description": "Called after a generate failed",
623+
"metadata": [{ "type": "resourceType" }, { "type": "generateFailure" }]
624+
},
625+
{
626+
"name": "appcomposer_postProcess",
627+
"description": "Called after a generate is processed",
628+
"metadata": [{ "type": "resourceType" }, { "type": "pathsScrubbed" }]
629+
},
630+
{
631+
"name": "appcomposer_customerReady",
632+
"description": "Called after a customer loads into Composeer",
633+
"metadata": [{ "type": "loadFileTime" }, { "type": "initializeTime" }, { "type": "saveFileTime" }]
634+
},
635+
{
636+
"name": "appcomposer_fps",
637+
"description": "Called regularly to record app performance",
638+
"metadata": [{ "type": "fps" }]
639+
},
640+
{
641+
"name": "appcomposer_addResource",
642+
"description": "Called when a resource is added to the canvas",
557643
"metadata": [{ "type": "resourceType" }]
558644
},
645+
{
646+
"name": "appcomposer_addConnection",
647+
"description": "Called when two resources are connected",
648+
"metadata": [
649+
{ "type": "sourceResourceType" },
650+
{ "type": "sourceFacetType" },
651+
{ "type": "destResourceType" },
652+
{ "type": "destFacetType" }
653+
]
654+
},
655+
{
656+
"name": "appcomposer_openWfs",
657+
"description": "Called when Step Functions Workflow Studio is opened"
658+
},
659+
{
660+
"name": "appcomposer_closeWfs",
661+
"description": "Called when Step Functions Workflow Studio is closed",
662+
"metadata": [{ "type": "didSave" }]
663+
},
559664
{
560665
"name": "codewhisperer_codeScanIssueHover",
561666
"description": "Called when a code scan issue is hovered over",

0 commit comments

Comments
 (0)