packages/core/src/codewhisperer/util/telemetryHelper.ts (818 lines of code) (raw):

/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import globals from '../../shared/extensionGlobals' import { runtimeLanguageContext } from './runtimeLanguageContext' import { codeWhispererClient as client, RecommendationsList } from '../client/codewhisperer' import { LicenseUtil } from './licenseUtil' import { CodewhispererGettingStartedTask, CodewhispererLanguage, CodewhispererPreviousSuggestionState, CodewhispererUserDecision, CodewhispererUserTriggerDecision, Status, telemetry, } from '../../shared/telemetry/telemetry' import { CodewhispererCompletionType, CodewhispererSuggestionState } from '../../shared/telemetry/telemetry' import { getImportCount } from './importAdderUtil' import { CodeWhispererSettings } from './codewhispererSettings' import { getSelectedCustomization } from './customizationUtil' import { AuthUtil } from './authUtil' import { isAwsError } from '../../shared/errors' import { getLogger } from '../../shared/logger/logger' import { session } from './codeWhispererSession' import { CodeWhispererSupplementalContext } from '../models/model' import { FeatureConfigProvider } from '../../shared/featureConfig' import { CodeScanRemediationsEventType } from '../client/codewhispereruserclient' import { CodeAnalysisScope as CodeAnalysisScopeClientSide } from '../models/constants' import { Session } from '../../amazonqTest/chat/session/session' export class TelemetryHelper { // Some variables for client component latency private _sdkApiCallEndTime = 0 get sdkApiCallEndTime(): number { return this._sdkApiCallEndTime } private _allPaginationEndTime = 0 get allPaginationEndTime(): number { return this._allPaginationEndTime } private _firstResponseRequestId = '' get firstResponseRequestId(): string { return this._firstResponseRequestId } // variables for user trigger decision // these will be cleared after a invocation session private sessionDecisions: CodewhispererUserTriggerDecision[] = [] private triggerChar?: string = undefined private prevTriggerDecision?: CodewhispererPreviousSuggestionState private typeAheadLength = 0 private timeSinceLastModification = 0 private lastTriggerDecisionTime = 0 private classifierResult?: number = undefined private classifierThreshold?: number = undefined // variables for tracking end to end sessions public traceId: string = 'notSet' // use this to distinguish DocumentChangeEvent from CWSPR or from other sources public lastSuggestionInDisplay = '' constructor() {} static #instance: TelemetryHelper public static get instance() { return (this.#instance ??= new this()) } public sendTestGenerationToolkitEvent( session: Session, isSupportedLanguage: boolean, isFileInWorkspace: boolean, result: 'Succeeded' | 'Failed' | 'Cancelled', requestId?: string, perfClientLatency?: number, reasonDesc?: string, isCodeBlockSelected?: boolean, artifactsUploadDuration?: number, buildPayloadBytes?: number, buildZipFileBytes?: number, acceptedCharactersCount?: number, acceptedCount?: number, acceptedLinesCount?: number, generatedCharactersCount?: number, generatedCount?: number, generatedLinesCount?: number, reason?: string, status?: Status ) { telemetry.amazonq_utgGenerateTests.emit({ cwsprChatProgrammingLanguage: session.fileLanguage ?? 'plaintext', hasUserPromptSupplied: session.hasUserPromptSupplied, isSupportedLanguage: session.isSupportedLanguage, isFileInWorkspace: isFileInWorkspace, result: result, artifactsUploadDuration: artifactsUploadDuration, buildPayloadBytes: buildPayloadBytes, buildZipFileBytes: buildZipFileBytes, credentialStartUrl: AuthUtil.instance.startUrl, acceptedCharactersCount: acceptedCharactersCount, acceptedCount: acceptedCount, acceptedLinesCount: acceptedLinesCount, generatedCharactersCount: generatedCharactersCount, generatedCount: generatedCount, generatedLinesCount: generatedLinesCount, isCodeBlockSelected: isCodeBlockSelected, jobGroup: session.testGenerationJobGroupName, jobId: session.listOfTestGenerationJobId[0], perfClientLatency: perfClientLatency, requestId: requestId, reasonDesc: reasonDesc, reason: reason, status: status, }) } public recordServiceInvocationTelemetry( requestId: string, sessionId: string, lastSuggestionIndex: number, result: 'Succeeded' | 'Failed', duration: number | undefined, language: CodewhispererLanguage, taskType: CodewhispererGettingStartedTask | undefined, reason: string, supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined ) { const event = { codewhispererAutomatedTriggerType: session.autoTriggerType, codewhispererCursorOffset: session.startCursorOffset, codewhispererCustomizationArn: getSelectedCustomization().arn, CodewhispererGettingStartedTask: taskType, codewhispererImportRecommendationEnabled: CodeWhispererSettings.instance.isImportRecommendationEnabled(), codewhispererLastSuggestionIndex: lastSuggestionIndex, codewhispererLanguage: language, codewhispererLineNumber: session.startPos.line, codewhispererRequestId: requestId ? requestId : undefined, codewhispererSessionId: sessionId ? sessionId : undefined, codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, codewhispererSupplementalContextLatency: supplementalContextMetadata?.latency, codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, codewhispererTriggerType: session.triggerType, credentialStartUrl: AuthUtil.instance.startUrl, duration: duration || 0, reason: reason ? reason.substring(0, 200) : undefined, result, traceId: this.traceId, } telemetry.codewhisperer_serviceInvocation.emit(event) } public recordUserDecisionTelemetryForEmptyList( requestIdList: string[], sessionId: string, paginationIndex: number, language: CodewhispererLanguage, supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined ) { const selectedCustomization = getSelectedCustomization() const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile telemetry.codewhisperer_userTriggerDecision.emit({ codewhispererAutomatedTriggerType: session.autoTriggerType, codewhispererClassifierResult: this.classifierResult, codewhispererClassifierThreshold: this.classifierThreshold, codewhispererCompletionType: 'Line', codewhispererCursorOffset: session.startCursorOffset, codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, codewhispererFeatureEvaluations: FeatureConfigProvider.instance.getFeatureConfigsTelemetry(), codewhispererFirstRequestId: requestIdList[0], codewhispererGettingStartedTask: session.taskType, codewhispererLanguage: language, codewhispererLineNumber: session.startPos.line, codewhispererPreviousSuggestionState: this.prevTriggerDecision, codewhispererSessionId: sessionId, codewhispererSuggestionCount: 0, codewhispererSuggestionImportCount: 0, codewhispererSuggestionState: 'Empty', codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, // eslint-disable-next-line id-length codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy, codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, codewhispererTimeSinceLastDocumentChange: this.timeSinceLastModification ? this.timeSinceLastModification : undefined, codewhispererTimeSinceLastUserDecision: this.lastTriggerDecisionTime ? performance.now() - this.lastTriggerDecisionTime : undefined, codewhispererTimeToFirstRecommendation: session.timeToFirstRecommendation, codewhispererTriggerType: session.triggerType, codewhispererTypeaheadLength: this.typeAheadLength, credentialStartUrl: AuthUtil.instance.startUrl, traceId: this.traceId, }) client .sendTelemetryEvent({ telemetryEvent: { userTriggerDecisionEvent: { sessionId: sessionId, requestId: requestIdList[0], customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language), }, completionType: 'LINE', suggestionState: 'EMPTY', recommendationLatencyMilliseconds: 0, triggerToResponseLatencyMilliseconds: session.timeToFirstRecommendation, perceivedLatencyMilliseconds: session.perceivedLatency, timestamp: new Date(Date.now()), suggestionReferenceCount: 0, generatedLine: 0, numberOfRecommendations: 0, acceptedCharacterCount: 0, }, }, profileArn: profile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().error(`Failed to invoke sendTelemetryEvent, requestId: ${requestId ?? ''}`) }) } /** * This function is to record the user decision on each of the suggestion in the list of CodeWhisperer recommendations. * @param recommendations the recommendations * @param acceptIndex the index of the accepted suggestion in the corresponding list of CodeWhisperer response. * If this function is not called on acceptance, then acceptIndex == -1 * @param languageId the language ID of the current document in current active editor * @param paginationIndex the index of pagination calls * @param recommendationSuggestionState the key-value mapping from index to suggestion state */ public recordUserDecisionTelemetry( requestIdList: string[], sessionId: string, recommendations: RecommendationsList, acceptIndex: number, paginationIndex: number, completionTypes: Map<number, CodewhispererCompletionType>, recommendationSuggestionState?: Map<number, string>, supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined ) { const events: CodewhispererUserDecision[] = [] // emit user decision telemetry for (const [i, _elem] of recommendations.entries()) { let uniqueSuggestionReferences: string | undefined = undefined const uniqueLicenseSet = LicenseUtil.getUniqueLicenseNames(_elem.references) if (uniqueLicenseSet.size > 0) { uniqueSuggestionReferences = JSON.stringify(Array.from(uniqueLicenseSet)) } if (_elem.content.length === 0) { recommendationSuggestionState?.set(i, 'Empty') } const event: CodewhispererUserDecision = { // TODO: maintain a list of RecommendationContexts with both recommendation and requestId in it, instead of two separate list items. codewhispererCompletionType: this.getCompletionType(i, completionTypes), codewhispererGettingStartedTask: session.taskType, codewhispererLanguage: session.language, codewhispererPaginationProgress: paginationIndex, codewhispererRequestId: requestIdList[i], codewhispererSessionId: sessionId ? sessionId : undefined, codewhispererSuggestionImportCount: getImportCount(_elem), codewhispererSuggestionIndex: i, codewhispererSuggestionState: this.getSuggestionState(i, acceptIndex, recommendationSuggestionState), codewhispererSuggestionReferenceCount: _elem.references ? _elem.references.length : 0, codewhispererSuggestionReferences: uniqueSuggestionReferences, codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, codewhispererTriggerType: session.triggerType, credentialStartUrl: AuthUtil.instance.startUrl, traceId: this.traceId, } events.push(event) } // aggregate suggestion references count const referenceCount = this.getAggregatedSuggestionReferenceCount(events) // aggregate user decision events at requestId level const aggregatedEvent = this.aggregateUserDecisionByRequest(events, requestIdList[0], sessionId) if (aggregatedEvent) { this.sessionDecisions.push(aggregatedEvent) } // TODO: use a ternary for this let acceptedRecommendationContent if (acceptIndex !== -1 && recommendations[acceptIndex] !== undefined) { acceptedRecommendationContent = recommendations[acceptIndex].content } else { acceptedRecommendationContent = '' } // after we have all request level user decisions, aggregate them at session level and send this.sendUserTriggerDecisionTelemetry( sessionId, acceptedRecommendationContent, referenceCount, supplementalContextMetadata ) } public aggregateUserDecisionByRequest( events: CodewhispererUserDecision[], requestId: string, sessionId: string, supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined ) { // the request level user decision will contain information from both the service_invocation event // and the user_decision events for recommendations within that request if (!events.length) { return } const aggregated: CodewhispererUserTriggerDecision = { codewhispererAutomatedTriggerType: session.autoTriggerType, codewhispererCompletionType: events[0].codewhispererCompletionType, codewhispererCursorOffset: session.startCursorOffset, codewhispererFirstRequestId: requestId, codewhispererGettingStartedTask: session.taskType, codewhispererLanguage: events[0].codewhispererLanguage, codewhispererLineNumber: session.startPos.line, codewhispererSessionId: sessionId, codewhispererSuggestionCount: events.length, codewhispererSuggestionImportCount: events .map((e) => e.codewhispererSuggestionImportCount || 0) .reduce((a, b) => a + b, 0), codewhispererSuggestionState: this.getAggregatedSuggestionState(events), codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, codewhispererTriggerType: events[0].codewhispererTriggerType, codewhispererTypeaheadLength: 0, credentialStartUrl: events[0].credentialStartUrl, traceId: this.traceId, } return aggregated } public sendUserTriggerDecisionTelemetry( sessionId: string, acceptedRecommendationContent: string, referenceCount: number, supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined ) { // the user trigger decision will aggregate information from request level user decisions within one session // and add additional session level insights if (!this.sessionDecisions.length) { return } // TODO: add partial acceptance related metrics const autoTriggerType = this.sessionDecisions[0].codewhispererAutomatedTriggerType const language = this.sessionDecisions[0].codewhispererLanguage const aggregatedCompletionType = this.sessionDecisions[0].codewhispererCompletionType const aggregatedSuggestionState = this.getAggregatedSuggestionState(this.sessionDecisions) const selectedCustomization = getSelectedCustomization() const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile const generatedLines = acceptedRecommendationContent.trim() === '' ? 0 : acceptedRecommendationContent.split('\n').length const suggestionCount = this.sessionDecisions .map((e) => e.codewhispererSuggestionCount) .reduce((a, b) => a + b, 0) const aggregated: CodewhispererUserTriggerDecision = { codewhispererAutomatedTriggerType: autoTriggerType, codewhispererCharactersAccepted: acceptedRecommendationContent.length, codewhispererClassifierResult: this.classifierResult, codewhispererClassifierThreshold: this.classifierThreshold, codewhispererCompletionType: aggregatedCompletionType, codewhispererCursorOffset: this.sessionDecisions[0].codewhispererCursorOffset, codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, codewhispererFeatureEvaluations: FeatureConfigProvider.instance.getFeatureConfigsTelemetry(), codewhispererFirstRequestId: this.sessionDecisions[0].codewhispererFirstRequestId, codewhispererGettingStartedTask: session.taskType, codewhispererLanguage: language, codewhispererLineNumber: this.sessionDecisions[0].codewhispererLineNumber, codewhispererPreviousSuggestionState: this.prevTriggerDecision, codewhispererSessionId: this.sessionDecisions[0].codewhispererSessionId, codewhispererSuggestionCount: suggestionCount, codewhispererSuggestionImportCount: this.sessionDecisions .map((e) => e.codewhispererSuggestionImportCount || 0) .reduce((a, b) => a + b, 0), codewhispererSuggestionState: aggregatedSuggestionState, codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, // eslint-disable-next-line id-length codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy, codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, codewhispererTimeSinceLastDocumentChange: this.timeSinceLastModification ? this.timeSinceLastModification : undefined, codewhispererTimeSinceLastUserDecision: this.lastTriggerDecisionTime ? performance.now() - this.lastTriggerDecisionTime : undefined, codewhispererTimeToFirstRecommendation: session.timeToFirstRecommendation, codewhispererTriggerCharacter: autoTriggerType === 'SpecialCharacters' ? this.triggerChar : undefined, codewhispererTriggerType: this.sessionDecisions[0].codewhispererTriggerType, codewhispererTypeaheadLength: this.typeAheadLength, credentialStartUrl: this.sessionDecisions[0].credentialStartUrl, traceId: this.traceId, } telemetry.codewhisperer_userTriggerDecision.emit(aggregated) this.prevTriggerDecision = this.getAggregatedSuggestionState(this.sessionDecisions) this.lastTriggerDecisionTime = performance.now() // When we send a userTriggerDecision for neither Accept nor Reject, service side should not use this value // and client side will set this value to 0.0. let e2eLatency = session.firstSuggestionShowTime - session.invokeSuggestionStartTime if (aggregatedSuggestionState !== 'Reject' && aggregatedSuggestionState !== 'Accept') { e2eLatency = 0.0 } client .sendTelemetryEvent({ telemetryEvent: { userTriggerDecisionEvent: { sessionId: sessionId, requestId: this.sessionDecisions[0].codewhispererFirstRequestId, customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage( this.sessionDecisions[0].codewhispererLanguage ), }, completionType: this.getSendTelemetryCompletionType(aggregatedCompletionType), suggestionState: this.getSendTelemetrySuggestionState(aggregatedSuggestionState), recommendationLatencyMilliseconds: e2eLatency, triggerToResponseLatencyMilliseconds: session.timeToFirstRecommendation, perceivedLatencyMilliseconds: session.perceivedLatency, timestamp: new Date(Date.now()), suggestionReferenceCount: referenceCount, generatedLine: generatedLines, numberOfRecommendations: suggestionCount, acceptedCharacterCount: acceptedRecommendationContent.length, }, }, profileArn: profile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) this.resetUserTriggerDecisionTelemetry() } public getLastTriggerDecisionForClassifier() { if (this.lastTriggerDecisionTime && performance.now() - this.lastTriggerDecisionTime <= 2 * 60 * 1000) { return this.prevTriggerDecision } } public setClassifierResult(classifierResult: number) { this.classifierResult = classifierResult } public setClassifierThreshold(classifierThreshold: number) { this.classifierThreshold = classifierThreshold } public setTriggerCharForUserTriggerDecision(triggerChar: string) { this.triggerChar = triggerChar } public setTypeAheadLength(typeAheadLength: number) { this.typeAheadLength = typeAheadLength } public setTimeSinceLastModification(timeSinceLastModification: number) { this.timeSinceLastModification = timeSinceLastModification } public setTraceId(traceId: string) { this.traceId = traceId } private resetUserTriggerDecisionTelemetry() { this.sessionDecisions = [] this.triggerChar = '' this.typeAheadLength = 0 this.timeSinceLastModification = 0 session.timeToFirstRecommendation = 0 session.perceivedLatency = 0 this.classifierResult = undefined this.classifierThreshold = undefined } private getSendTelemetryCompletionType(completionType: CodewhispererCompletionType) { return completionType === 'Block' ? 'BLOCK' : 'LINE' } private getAggregatedSuggestionState( // if there is any Accept within the session, mark the session as Accept // if there is any Reject within the session, mark the session as Reject // if all recommendations within the session are empty, mark the session as Empty // otherwise mark the session as Discard events: CodewhispererUserDecision[] | CodewhispererUserTriggerDecision[] ): CodewhispererPreviousSuggestionState { let isEmpty = true for (const event of events) { if (event.codewhispererSuggestionState === 'Accept') { return 'Accept' } else if (event.codewhispererSuggestionState === 'Reject') { return 'Reject' } else if (event.codewhispererSuggestionState !== 'Empty') { isEmpty = false } } return isEmpty ? 'Empty' : 'Discard' } private getSendTelemetrySuggestionState(state: CodewhispererPreviousSuggestionState) { if (state === 'Accept') { return 'ACCEPT' } else if (state === 'Reject') { return 'REJECT' } else if (state === 'Discard') { return 'DISCARD' } return 'EMPTY' } private getAggregatedSuggestionReferenceCount( events: CodewhispererUserDecision[] // if there is reference for accepted recommendation within the session, mark the reference number // as 1, otherwise mark the session as 0 ) { for (const event of events) { if (event.codewhispererSuggestionState === 'Accept' && event.codewhispererSuggestionReferenceCount !== 0) { return 1 } } return 0 } public getSuggestionState( i: number, acceptIndex: number, recommendationSuggestionState?: Map<number, string> ): CodewhispererSuggestionState { const state = recommendationSuggestionState?.get(i) if (state && ['Empty', 'Filter', 'Discard'].includes(state)) { return state as CodewhispererSuggestionState } else if (recommendationSuggestionState !== undefined && recommendationSuggestionState.get(i) !== 'Showed') { return 'Unseen' } if (acceptIndex === -1) { return 'Reject' } return i === acceptIndex ? 'Accept' : 'Ignore' } public getCompletionType(i: number, completionTypes: Map<number, CodewhispererCompletionType>) { return completionTypes.get(i) || 'Line' } public isTelemetryEnabled(): boolean { return globals.telemetry.telemetryEnabled } public resetClientComponentLatencyTime() { session.invokeSuggestionStartTime = 0 session.preprocessEndTime = 0 session.sdkApiCallStartTime = 0 this._sdkApiCallEndTime = 0 session.fetchCredentialStartTime = 0 session.firstSuggestionShowTime = 0 this._allPaginationEndTime = 0 this._firstResponseRequestId = '' } public setPreprocessEndTime() { if (session.preprocessEndTime !== 0) { getLogger().warn(`inline completion preprocessEndTime has been set and not reset correctly`) } session.preprocessEndTime = performance.now() } /** This method is assumed to be invoked first at the start of execution **/ public setInvokeSuggestionStartTime() { this.resetClientComponentLatencyTime() session.invokeSuggestionStartTime = performance.now() } public setSdkApiCallEndTime() { if (this._sdkApiCallEndTime === 0 && session.sdkApiCallStartTime !== 0) { this._sdkApiCallEndTime = performance.now() } } public setAllPaginationEndTime() { if (this._allPaginationEndTime === 0 && this._sdkApiCallEndTime !== 0) { this._allPaginationEndTime = performance.now() } } public setFirstSuggestionShowTime() { if (session.firstSuggestionShowTime === 0 && this._sdkApiCallEndTime !== 0) { session.firstSuggestionShowTime = performance.now() } } public setFirstResponseRequestId(requestId: string) { if (this._firstResponseRequestId === '') { this._firstResponseRequestId = requestId } } // report client component latency after all pagination call finish // and at least one suggestion is shown to the user public tryRecordClientComponentLatency() { if (session.firstSuggestionShowTime === 0 || this._allPaginationEndTime === 0) { return } telemetry.codewhisperer_clientComponentLatency.emit({ codewhispererAllCompletionsLatency: this._allPaginationEndTime - session.sdkApiCallStartTime, codewhispererCompletionType: 'Line', codewhispererCredentialFetchingLatency: session.sdkApiCallStartTime - session.fetchCredentialStartTime, codewhispererCustomizationArn: getSelectedCustomization().arn, codewhispererEndToEndLatency: session.firstSuggestionShowTime - session.invokeSuggestionStartTime, codewhispererFirstCompletionLatency: this._sdkApiCallEndTime - session.sdkApiCallStartTime, codewhispererLanguage: session.language, codewhispererPostprocessingLatency: session.firstSuggestionShowTime - this._sdkApiCallEndTime, codewhispererPreprocessingLatency: session.preprocessEndTime - session.invokeSuggestionStartTime, codewhispererRequestId: this._firstResponseRequestId, codewhispererSessionId: session.sessionId, codewhispererTriggerType: session.triggerType, credentialStartUrl: AuthUtil.instance.startUrl, }) } public sendCodeScanEvent(languageId: string, jobId: string) { getLogger().debug(`start sendCodeScanEvent: jobId: "${jobId}", languageId: "${languageId}"`) client .sendTelemetryEvent({ telemetryEvent: { codeScanEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(languageId as CodewhispererLanguage), }, codeScanJobId: jobId, timestamp: new Date(Date.now()), }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendCodeScanEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendCodeScanSucceededEvent( language: string, jobId: string, numberOfFindings: number, scope: CodeAnalysisScopeClientSide ) { client .sendTelemetryEvent({ telemetryEvent: { codeScanSucceededEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), }, codeScanJobId: jobId, numberOfFindings: numberOfFindings, timestamp: new Date(Date.now()), codeAnalysisScope: scope === CodeAnalysisScopeClientSide.FILE_AUTO ? 'FILE' : 'PROJECT', }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent for code scan success, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendCodeScanFailedEvent(language: string, jobId: string, scope: CodeAnalysisScopeClientSide) { client .sendTelemetryEvent({ telemetryEvent: { codeScanFailedEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), }, codeScanJobId: jobId, codeAnalysisScope: scope === CodeAnalysisScopeClientSide.FILE_AUTO ? 'FILE' : 'PROJECT', timestamp: new Date(Date.now()), }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent for code scan failure, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendCodeFixGenerationEvent( jobId: string, language?: string, ruleId?: string, detectorId?: string, linesOfCodeGenerated?: number, charsOfCodeGenerated?: number ) { client .sendTelemetryEvent({ telemetryEvent: { codeFixGenerationEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), }, jobId, ruleId, detectorId, linesOfCodeGenerated, charsOfCodeGenerated, }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent for code fix generation, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendCodeFixAcceptanceEvent( jobId: string, language?: string, ruleId?: string, detectorId?: string, linesOfCodeAccepted?: number, charsOfCodeAccepted?: number ) { client .sendTelemetryEvent({ telemetryEvent: { codeFixAcceptanceEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), }, jobId, ruleId, detectorId, linesOfCodeAccepted, charsOfCodeAccepted, }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent for code fix acceptance, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendTestGenerationEvent( groupName: string, jobId: string, language?: string, numberOfUnitTestCasesGenerated?: number, numberOfUnitTestCasesAccepted?: number, linesOfCodeGenerated?: number, linesOfCodeAccepted?: number, charsOfCodeGenerated?: number, charsOfCodeAccepted?: number ) { client .sendTelemetryEvent({ telemetryEvent: { testGenerationEvent: { programmingLanguage: { languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), }, jobId, groupName, ideCategory: 'VSCODE', numberOfUnitTestCasesGenerated, numberOfUnitTestCasesAccepted, linesOfCodeGenerated, linesOfCodeAccepted, charsOfCodeGenerated, charsOfCodeAccepted, timestamp: new Date(Date.now()), }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendTelemetryEvent for test generation, requestId: ${requestId ?? ''}, message: ${ error.message }` ) }) } public sendCodeScanRemediationsEvent( languageId?: string, codeScanRemediationEventType?: CodeScanRemediationsEventType, detectorId?: string, findingId?: string, ruleId?: string, component?: string, reason?: string, result?: string, includesFix?: boolean ) { client .sendTelemetryEvent({ telemetryEvent: { codeScanRemediationsEvent: { programmingLanguage: languageId ? { languageName: runtimeLanguageContext.toRuntimeLanguage( languageId as CodewhispererLanguage ), } : undefined, CodeScanRemediationsEventType: codeScanRemediationEventType, detectorId: detectorId, findingId: findingId, ruleId: ruleId, component: component, reason: reason, result: result, includesFix: includesFix, timestamp: new Date(Date.now()), }, }, profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, }) .then() .catch((error) => { let requestId: string | undefined if (isAwsError(error)) { requestId = error.requestId } getLogger().debug( `Failed to sendCodeScanRemediationsEvent to CodeWhisperer, requestId: ${ requestId ?? '' }, message: ${error.message}` ) }) } }