Skip to content

Commit 00f0303

Browse files
authored
telemetry(amazonq): Add telemetry tracing for amazon q inline (aws#5655)
## Problem - The flow from when a message is shown to the user and rejected/accepted by the user isn't instrumented with any way to trace events ## Solution - Create a trace from when the event is shown (either by manual invocation or auto trigger) and then connect all the telemetry events through a traceId. ### Implementation details - The traceId is initially created when manual invocation/auto trigger occurs - For every telemetry event thats in the function hierarchy of the manual invocation/auto trigger, they automatically assume the same traceId - When an event listener is called it uses the traceId we've set in TelemetryHelper to setup the next traceId. This is because the event no longer belongs to the same context - Inside of commands we use `telemetry.record` because it allows us to override the default telemetry id that gets automatically created and instead associate it with the known trace
1 parent 8788dfe commit 00f0303

File tree

8 files changed

+146
-99
lines changed

8 files changed

+146
-99
lines changed

packages/core/src/codewhisperer/activation.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,11 @@ export async function activate(context: ExtContext): Promise<void> {
200200
await openSettings('amazonQ')
201201
}
202202
}),
203-
Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean = false) => {
203+
Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean) => {
204+
telemetry.record({
205+
traceId: TelemetryHelper.instance.traceId,
206+
})
207+
204208
const editor = vscode.window.activeTextEditor
205209
if (editor) {
206210
if (forceProceed) {

packages/core/src/codewhisperer/commands/onInlineAcceptance.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { session } from '../util/codeWhispererSession'
3030
import path from 'path'
3131
import { RecommendationService } from '../service/recommendationService'
3232
import { Container } from '../service/serviceContainer'
33+
import { telemetry } from '../../shared/telemetry'
34+
import { TelemetryHelper } from '../util/telemetryHelper'
3335

3436
export const acceptSuggestion = Commands.declare(
3537
'aws.amazonq.accept',
@@ -46,6 +48,10 @@ export const acceptSuggestion = Commands.declare(
4648
language: CodewhispererLanguage,
4749
references: codewhispererClient.References
4850
) => {
51+
telemetry.record({
52+
traceId: TelemetryHelper.instance.traceId,
53+
})
54+
4955
RecommendationService.instance.incrementAcceptedCount()
5056
const editor = vscode.window.activeTextEditor
5157
await Container.instance.lineAnnotationController.refresh(editor, 'codewhisperer')

packages/core/src/codewhisperer/service/keyStrokeHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export class KeyStrokeHandler {
164164
if (!editor) {
165165
return
166166
}
167+
167168
// RecommendationHandler.instance.reportUserDecisionOfRecommendation(editor, -1)
168169
await RecommendationService.instance.generateRecommendation(
169170
client,

packages/core/src/codewhisperer/service/recommendationHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ const nextCommand = Commands.declare('editor.action.inlineSuggest.showNext', ()
5959
})
6060

6161
const rejectCommand = Commands.declare('aws.amazonq.rejectCodeSuggestion', () => async () => {
62+
telemetry.record({
63+
traceId: TelemetryHelper.instance.traceId,
64+
})
65+
6266
if (!isCloud9('any')) {
6367
await vscode.commands.executeCommand('editor.action.inlineSuggest.hide')
6468
}

packages/core/src/codewhisperer/service/recommendationService.ts

Lines changed: 96 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ import * as vscode from 'vscode'
66
import { ConfigurationEntry, GetRecommendationsResponse, vsCodeState } from '../models/model'
77
import { isCloud9 } from '../../shared/extensionUtilities'
88
import { isInlineCompletionEnabled } from '../util/commonUtil'
9-
import { CodewhispererAutomatedTriggerType, CodewhispererTriggerType } from '../../shared/telemetry/telemetry'
9+
import {
10+
CodewhispererAutomatedTriggerType,
11+
CodewhispererTriggerType,
12+
telemetry,
13+
} from '../../shared/telemetry/telemetry'
1014
import { AuthUtil } from '../util/authUtil'
1115
import { isIamConnection } from '../../auth/connection'
1216
import { RecommendationHandler } from '../service/recommendationHandler'
1317
import { InlineCompletionService } from '../service/inlineCompletionService'
1418
import { ClassifierTrigger } from './classifierTrigger'
1519
import { DefaultCodeWhispererClient } from '../client/codewhisperer'
20+
import { randomUUID } from '../../shared/crypto'
21+
import { TelemetryHelper } from '../util/telemetryHelper'
1622

1723
export interface SuggestionActionEvent {
1824
readonly editor: vscode.TextEditor | undefined
@@ -68,99 +74,107 @@ export class RecommendationService {
6874
return
6975
}
7076

71-
if (isCloud9('any')) {
72-
// C9 manual trigger key alt/option + C is ALWAYS enabled because the VSC version C9 is on doesn't support setContextKey which is used for CODEWHISPERER_ENABLED
73-
// therefore we need a connection check if there is ANY connection(regardless of the connection's state) connected to CodeWhisperer on C9
74-
if (triggerType === 'OnDemand' && !AuthUtil.instance.isConnected()) {
75-
return
76-
}
77+
/**
78+
* Use an existing trace ID if invoked through a command (e.g., manual invocation),
79+
* otherwise generate a new trace ID
80+
*/
81+
const traceId = telemetry.attributes?.traceId ?? randomUUID()
82+
TelemetryHelper.instance.setTraceId(traceId)
83+
await telemetry.withTraceId(async () => {
84+
if (isCloud9('any')) {
85+
// C9 manual trigger key alt/option + C is ALWAYS enabled because the VSC version C9 is on doesn't support setContextKey which is used for CODEWHISPERER_ENABLED
86+
// therefore we need a connection check if there is ANY connection(regardless of the connection's state) connected to CodeWhisperer on C9
87+
if (triggerType === 'OnDemand' && !AuthUtil.instance.isConnected()) {
88+
return
89+
}
7790

78-
RecommendationHandler.instance.checkAndResetCancellationTokens()
79-
vsCodeState.isIntelliSenseActive = false
80-
this._isRunning = true
81-
let response: GetRecommendationsResponse = {
82-
result: 'Failed',
83-
errorMessage: undefined,
84-
recommendationCount: 0,
85-
}
91+
RecommendationHandler.instance.checkAndResetCancellationTokens()
92+
vsCodeState.isIntelliSenseActive = false
93+
this._isRunning = true
94+
let response: GetRecommendationsResponse = {
95+
result: 'Failed',
96+
errorMessage: undefined,
97+
recommendationCount: 0,
98+
}
8699

87-
try {
88-
this._onSuggestionActionEvent.fire({
89-
editor: editor,
90-
isRunning: true,
91-
triggerType: triggerType,
92-
response: undefined,
93-
})
100+
try {
101+
this._onSuggestionActionEvent.fire({
102+
editor: editor,
103+
isRunning: true,
104+
triggerType: triggerType,
105+
response: undefined,
106+
})
94107

95-
if (isCloud9('classic') || isIamConnection(AuthUtil.instance.conn)) {
96-
response = await RecommendationHandler.instance.getRecommendations(
97-
client,
98-
editor,
99-
triggerType,
100-
config,
101-
autoTriggerType,
102-
false
103-
)
104-
} else {
105-
if (AuthUtil.instance.isConnectionExpired()) {
106-
await AuthUtil.instance.showReauthenticatePrompt()
108+
if (isCloud9('classic') || isIamConnection(AuthUtil.instance.conn)) {
109+
response = await RecommendationHandler.instance.getRecommendations(
110+
client,
111+
editor,
112+
triggerType,
113+
config,
114+
autoTriggerType,
115+
false
116+
)
117+
} else {
118+
if (AuthUtil.instance.isConnectionExpired()) {
119+
await AuthUtil.instance.showReauthenticatePrompt()
120+
}
121+
response = await RecommendationHandler.instance.getRecommendations(
122+
client,
123+
editor,
124+
triggerType,
125+
config,
126+
autoTriggerType,
127+
true
128+
)
129+
}
130+
if (RecommendationHandler.instance.canShowRecommendationInIntelliSense(editor, true, response)) {
131+
await vscode.commands.executeCommand('editor.action.triggerSuggest').then(() => {
132+
vsCodeState.isIntelliSenseActive = true
133+
})
107134
}
108-
response = await RecommendationHandler.instance.getRecommendations(
135+
} finally {
136+
this._isRunning = false
137+
this._onSuggestionActionEvent.fire({
138+
editor: editor,
139+
isRunning: false,
140+
triggerType: triggerType,
141+
response: response,
142+
})
143+
}
144+
} else if (isInlineCompletionEnabled()) {
145+
if (triggerType === 'OnDemand') {
146+
ClassifierTrigger.instance.recordClassifierResultForManualTrigger(editor)
147+
}
148+
149+
this._isRunning = true
150+
let response: GetRecommendationsResponse | undefined = undefined
151+
152+
try {
153+
this._onSuggestionActionEvent.fire({
154+
editor: editor,
155+
isRunning: true,
156+
triggerType: triggerType,
157+
response: undefined,
158+
})
159+
160+
response = await InlineCompletionService.instance.getPaginatedRecommendation(
109161
client,
110162
editor,
111163
triggerType,
112164
config,
113165
autoTriggerType,
114-
true
166+
event
115167
)
116-
}
117-
if (RecommendationHandler.instance.canShowRecommendationInIntelliSense(editor, true, response)) {
118-
await vscode.commands.executeCommand('editor.action.triggerSuggest').then(() => {
119-
vsCodeState.isIntelliSenseActive = true
168+
} finally {
169+
this._isRunning = false
170+
this._onSuggestionActionEvent.fire({
171+
editor: editor,
172+
isRunning: false,
173+
triggerType: triggerType,
174+
response: response,
120175
})
121176
}
122-
} finally {
123-
this._isRunning = false
124-
this._onSuggestionActionEvent.fire({
125-
editor: editor,
126-
isRunning: false,
127-
triggerType: triggerType,
128-
response: response,
129-
})
130-
}
131-
} else if (isInlineCompletionEnabled()) {
132-
if (triggerType === 'OnDemand') {
133-
ClassifierTrigger.instance.recordClassifierResultForManualTrigger(editor)
134177
}
135-
136-
this._isRunning = true
137-
let response: GetRecommendationsResponse | undefined = undefined
138-
139-
try {
140-
this._onSuggestionActionEvent.fire({
141-
editor: editor,
142-
isRunning: true,
143-
triggerType: triggerType,
144-
response: undefined,
145-
})
146-
147-
response = await InlineCompletionService.instance.getPaginatedRecommendation(
148-
client,
149-
editor,
150-
triggerType,
151-
config,
152-
autoTriggerType,
153-
event
154-
)
155-
} finally {
156-
this._isRunning = false
157-
this._onSuggestionActionEvent.fire({
158-
editor: editor,
159-
isRunning: false,
160-
triggerType: triggerType,
161-
response: response,
162-
})
163-
}
164-
}
178+
}, traceId)
165179
}
166180
}

packages/core/src/codewhisperer/util/telemetryHelper.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export class TelemetryHelper {
4646
private timeToFirstRecommendation = 0
4747
private classifierResult?: number = undefined
4848
private classifierThreshold?: number = undefined
49+
// variables for tracking end to end sessions
50+
public traceId: string = 'notSet'
4951

5052
// use this to distinguish DocumentChangeEvent from CWSPR or from other sources
5153
public lastSuggestionInDisplay = ''
@@ -90,6 +92,7 @@ export class TelemetryHelper {
9092
codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength,
9193
codewhispererUserGroup: CodeWhispererUserGroupSettings.getUserGroup().toString(),
9294
codewhispererCustomizationArn: getSelectedCustomization().arn,
95+
traceId: this.traceId,
9396
}
9497
telemetry.codewhisperer_serviceInvocation.emit(event)
9598
}
@@ -118,6 +121,7 @@ export class TelemetryHelper {
118121
codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout,
119122
codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg,
120123
codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength,
124+
traceId: this.traceId,
121125
})
122126
}
123127

@@ -171,6 +175,7 @@ export class TelemetryHelper {
171175
codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout,
172176
codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg,
173177
codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength,
178+
traceId: this.traceId,
174179
}
175180
telemetry.codewhisperer_userDecision.emit(event)
176181
events.push(event)
@@ -234,6 +239,7 @@ export class TelemetryHelper {
234239
codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout,
235240
codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg,
236241
codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength,
242+
traceId: this.traceId,
237243
}
238244
return aggregated
239245
}
@@ -299,6 +305,7 @@ export class TelemetryHelper {
299305
codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy,
300306
codewhispererCharactersAccepted: acceptedRecommendationContent.length,
301307
codewhispererFeatureEvaluations: FeatureConfigProvider.instance.getFeatureConfigsTelemetry(),
308+
traceId: this.traceId,
302309
}
303310
telemetry.codewhisperer_userTriggerDecision.emit(aggregated)
304311
this.prevTriggerDecision = this.getAggregatedSuggestionState(this.sessionDecisions)
@@ -386,6 +393,10 @@ export class TelemetryHelper {
386393
}
387394
}
388395

396+
public setTraceId(traceId: string) {
397+
this.traceId = traceId
398+
}
399+
389400
private resetUserTriggerDecisionTelemetry() {
390401
this.sessionDecisions = []
391402
this.triggerChar = ''

packages/core/src/codewhisperer/views/activeStateController.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { subscribeOnce } from '../../shared/utilities/vsCodeUtils'
1111
import { Container } from '../service/serviceContainer'
1212
import { RecommendationHandler } from '../service/recommendationHandler'
1313
import { cancellableDebounce } from '../../shared/utilities/functionUtils'
14+
import { telemetry } from '../../shared/telemetry'
15+
import { TelemetryHelper } from '../util/telemetryHelper'
1416

1517
export class ActiveStateController implements vscode.Disposable {
1618
private readonly _disposable: vscode.Disposable
@@ -33,7 +35,9 @@ export class ActiveStateController implements vscode.Disposable {
3335
constructor(private readonly container: Container) {
3436
this._disposable = vscode.Disposable.from(
3537
RecommendationService.instance.suggestionActionEvent(async (e) => {
36-
await this.onSuggestionActionEvent(e)
38+
await telemetry.withTraceId(async () => {
39+
await this.onSuggestionActionEvent(e)
40+
}, TelemetryHelper.instance.traceId)
3741
}),
3842
RecommendationHandler.instance.onDidReceiveRecommendation(async (_) => {
3943
await this.onDidReceiveRecommendation()

packages/core/src/codewhisperer/views/lineAnnotationController.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { session } from '../util/codeWhispererSession'
2020
import { RecommendationHandler } from '../service/recommendationHandler'
2121
import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
2222
import { setContext } from '../../shared'
23+
import { TelemetryHelper } from '../util/telemetryHelper'
2324

2425
const case3TimeWindow = 30000 // 30 seconds
2526

@@ -246,24 +247,26 @@ export class LineAnnotationController implements vscode.Disposable {
246247
await this.onReady()
247248
}),
248249
RecommendationService.instance.suggestionActionEvent(async (e) => {
249-
if (!this._isReady) {
250-
return
251-
}
252-
253-
if (this._currentState instanceof ManualtriggerState) {
254-
if (e.triggerType === 'OnDemand' && this._currentState.hasManualTrigger === false) {
255-
this._currentState.hasManualTrigger = true
250+
await telemetry.withTraceId(async () => {
251+
if (!this._isReady) {
252+
return
256253
}
257-
if (
258-
e.response?.recommendationCount !== undefined &&
259-
e.response?.recommendationCount > 0 &&
260-
this._currentState.hasValidResponse === false
261-
) {
262-
this._currentState.hasValidResponse = true
254+
255+
if (this._currentState instanceof ManualtriggerState) {
256+
if (e.triggerType === 'OnDemand' && this._currentState.hasManualTrigger === false) {
257+
this._currentState.hasManualTrigger = true
258+
}
259+
if (
260+
e.response?.recommendationCount !== undefined &&
261+
e.response?.recommendationCount > 0 &&
262+
this._currentState.hasValidResponse === false
263+
) {
264+
this._currentState.hasValidResponse = true
265+
}
263266
}
264-
}
265267

266-
await this.refresh(e.editor, 'codewhisperer')
268+
await this.refresh(e.editor, 'codewhisperer')
269+
}, TelemetryHelper.instance.traceId)
267270
}),
268271
this.container.lineTracker.onDidChangeActiveLines(async (e) => {
269272
await this.onActiveLinesChanged(e)

0 commit comments

Comments
 (0)