Skip to content

Commit 6f6a0f0

Browse files
committed
telemetry(amazonq): createUpload metric for scans
1 parent 21aba02 commit 6f6a0f0

File tree

4 files changed

+127
-32
lines changed

4 files changed

+127
-32
lines changed

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanException.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ open class CodeWhispererCodeScanException(override val message: String?) : Runti
99

1010
open class CodeWhispererCodeFixException(override val message: String?) : RuntimeException()
1111

12-
open class CodeWhispererCodeScanServerException(override val message: String?) : RuntimeException()
12+
open class CodeWhispererCodeScanServerException(
13+
override val message: String?,
14+
val requestId: String?,
15+
val requestServiceType: String?,
16+
val httpStatusCode: String?,
17+
) : RuntimeException()
1318

1419
internal fun noFileOpenError(): Nothing =
1520
throw CodeWhispererCodeScanException(message("codewhisperer.codescan.no_file_open"))
@@ -29,8 +34,12 @@ internal fun fileFormatNotSupported(format: String): Nothing =
2934
internal fun fileTooLarge(): Nothing =
3035
throw CodeWhispererCodeScanException(message("codewhisperer.codescan.file_too_large"))
3136

32-
internal fun codeScanServerException(errorMessage: String): Nothing =
33-
throw CodeWhispererCodeScanServerException(errorMessage)
37+
internal fun codeScanServerException(
38+
errorMessage: String,
39+
requestId: String? = null,
40+
requestServiceType: String? = null,
41+
httpStatusCode: String? = null,
42+
): Nothing = throw CodeWhispererCodeScanServerException(errorMessage, requestId, requestServiceType, httpStatusCode)
3443

3544
internal fun invalidSourceZipError(): Nothing =
3645
throw CodeWhispererCodeScanException(message("codewhisperer.codescan.invalid_source_zip_telemetry"))

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ class CodeWhispererCodeScanSession(val sessionContext: CodeScanSessionContext) {
105105
val artifactsUploadStartTime = now()
106106
val codeScanName = UUID.randomUUID().toString()
107107

108-
val taskType = if (sessionContext.codeAnalysisScope == CodeWhispererConstants.CodeAnalysisScope.PROJECT) {
109-
CodeWhispererConstants.UploadTaskType.SCAN_PROJECT
110-
} else {
108+
val taskType = if (isAutoScan()) {
111109
CodeWhispererConstants.UploadTaskType.SCAN_FILE
110+
} else {
111+
CodeWhispererConstants.UploadTaskType.SCAN_PROJECT
112112
}
113113

114114
val sourceZipUploadResponse =

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/util/CodeWhispererZipUploadManager.kt

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.openapi.components.service
88
import com.intellij.openapi.project.Project
99
import com.intellij.util.io.HttpRequests
1010
import org.apache.commons.codec.digest.DigestUtils
11+
import software.amazon.awssdk.awscore.exception.AwsServiceException
1112
import software.amazon.awssdk.services.codewhispererruntime.model.CodeAnalysisUploadContext
1213
import software.amazon.awssdk.services.codewhispererruntime.model.CodeFixUploadContext
1314
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlRequest
@@ -21,6 +22,7 @@ import software.aws.toolkits.core.utils.debug
2122
import software.aws.toolkits.core.utils.getLogger
2223
import software.aws.toolkits.jetbrains.core.AwsClientManager
2324
import software.aws.toolkits.jetbrains.services.amazonq.RetryableOperation
25+
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanServerException
2426
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanSession.Companion.APPLICATION_ZIP
2527
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanSession.Companion.AWS_KMS
2628
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanSession.Companion.CONTENT_MD5
@@ -32,7 +34,11 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.codeScanS
3234
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.invalidSourceZipError
3335
import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.CodeTestException
3436
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
37+
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
3538
import software.aws.toolkits.resources.message
39+
import software.aws.toolkits.telemetry.AmazonqTelemetry
40+
import software.aws.toolkits.telemetry.AmazonqUploadIntent
41+
import software.aws.toolkits.telemetry.MetricResult
3642
import java.io.File
3743
import java.io.FileInputStream
3844
import java.io.IOException
@@ -50,29 +56,66 @@ class CodeWhispererZipUploadManager(private val project: Project) {
5056
taskName: String,
5157
featureUseCase: CodeWhispererConstants.FeatureName,
5258
): CreateUploadUrlResponse {
53-
// Throw error if zipFile is invalid.
54-
if (!zipFile.exists()) {
55-
when (featureUseCase) {
56-
CodeWhispererConstants.FeatureName.CODE_REVIEW -> invalidSourceZipError()
57-
CodeWhispererConstants.FeatureName.TEST_GENERATION -> testGenerationInvalidSourceZipError()
58-
else -> throw IllegalArgumentException("Unsupported feature case: $featureUseCase") // Adding else for safety check
59+
val startTime = System.currentTimeMillis()
60+
var result: MetricResult = MetricResult.Succeeded
61+
var failureReason: String? = null
62+
var failureReasonDesc: String? = null
63+
var requestId: String? = null
64+
var requestServiceType: String? = null
65+
var httpStatusCode: String? = null
66+
try {
67+
// Throw error if zipFile is invalid.
68+
if (!zipFile.exists()) {
69+
when (featureUseCase) {
70+
CodeWhispererConstants.FeatureName.CODE_REVIEW -> invalidSourceZipError()
71+
CodeWhispererConstants.FeatureName.TEST_GENERATION -> testGenerationInvalidSourceZipError()
72+
else -> throw IllegalArgumentException("Unsupported feature case: $featureUseCase") // Adding else for safety check
73+
}
5974
}
60-
}
61-
val fileMd5: String = Base64.getEncoder().encodeToString(DigestUtils.md5(FileInputStream(zipFile)))
62-
val createUploadUrlResponse = createUploadUrl(fileMd5, artifactType, taskType, taskName, featureUseCase)
63-
val url = createUploadUrlResponse.uploadUrl()
64-
LOG.debug { "$featureUseCase: Uploading $artifactType using the presigned URL." }
75+
val fileMd5: String = Base64.getEncoder().encodeToString(DigestUtils.md5(FileInputStream(zipFile)))
76+
val createUploadUrlResponse = createUploadUrl(fileMd5, artifactType, taskType, taskName, featureUseCase)
77+
val url = createUploadUrlResponse.uploadUrl()
78+
LOG.debug { "$featureUseCase: Uploading $artifactType using the presigned URL." }
6579

66-
uploadArtifactToS3(
67-
url,
68-
createUploadUrlResponse.uploadId(),
69-
zipFile,
70-
fileMd5,
71-
createUploadUrlResponse.kmsKeyArn(),
72-
createUploadUrlResponse.requestHeaders(),
73-
featureUseCase
74-
)
75-
return createUploadUrlResponse
80+
uploadArtifactToS3(
81+
url,
82+
createUploadUrlResponse.uploadId(),
83+
zipFile,
84+
fileMd5,
85+
createUploadUrlResponse.kmsKeyArn(),
86+
createUploadUrlResponse.requestHeaders(),
87+
featureUseCase
88+
)
89+
return createUploadUrlResponse
90+
} catch (e: Exception) {
91+
result = MetricResult.Failed
92+
failureReason = e.javaClass.simpleName
93+
failureReasonDesc = e.message
94+
if (e is CodeWhispererCodeScanServerException) {
95+
requestId = e.requestId
96+
requestServiceType = e.requestServiceType
97+
httpStatusCode = e.httpStatusCode
98+
}
99+
throw e
100+
} finally {
101+
if (featureUseCase == CodeWhispererConstants.FeatureName.CODE_REVIEW) {
102+
AmazonqTelemetry.createUpload(
103+
amazonqUploadIntent = if (taskType == CodeWhispererConstants.UploadTaskType.SCAN_PROJECT) {
104+
AmazonqUploadIntent.FULLPROJECTSECURITYSCAN
105+
} else {
106+
AmazonqUploadIntent.AUTOMATICFILESECURITYSCAN
107+
},
108+
result = result,
109+
reason = failureReason,
110+
reasonDesc = failureReasonDesc,
111+
duration = (System.currentTimeMillis() - startTime).toDouble(),
112+
credentialStartUrl = getStartUrl(project),
113+
requestId = requestId,
114+
requestServiceType = requestServiceType,
115+
httpStatusCode = httpStatusCode
116+
)
117+
}
118+
}
76119
}
77120

78121
@Throws(IOException::class)
@@ -85,6 +128,7 @@ class CodeWhispererZipUploadManager(private val project: Project) {
85128
requestHeaders: Map<String, String>?,
86129
featureUseCase: CodeWhispererConstants.FeatureName,
87130
) {
131+
var connection: HttpURLConnection? = null
88132
RetryableOperation<Unit>().execute(
89133
operation = {
90134
val uploadIdJson = """{"uploadId":"$uploadId"}"""
@@ -103,9 +147,9 @@ class CodeWhispererZipUploadManager(private val project: Project) {
103147
}
104148
}
105149
}.connect {
106-
val connection = it.connection as HttpURLConnection
107-
connection.setFixedLengthStreamingMode(fileToUpload.length())
108-
IoUtils.copy(fileToUpload.inputStream(), connection.outputStream)
150+
connection = it.connection as HttpURLConnection
151+
connection?.setFixedLengthStreamingMode(fileToUpload.length())
152+
IoUtils.copy(fileToUpload.inputStream(), connection?.outputStream)
109153
}
110154
},
111155
isRetryable = { e ->
@@ -118,7 +162,12 @@ class CodeWhispererZipUploadManager(private val project: Project) {
118162
val errorMessage = getTelemetryErrorMessage(e, featureUseCase)
119163
when (featureUseCase) {
120164
CodeWhispererConstants.FeatureName.CODE_REVIEW ->
121-
codeScanServerException("CreateUploadUrlException: $errorMessage")
165+
codeScanServerException(
166+
"CreateUploadUrlException: $errorMessage",
167+
connection?.getHeaderField("x-amz-request-id"),
168+
"s3",
169+
(e as? HttpRequests.HttpStatusException)?.statusCode.toString()
170+
)
122171
CodeWhispererConstants.FeatureName.TEST_GENERATION ->
123172
throw CodeTestException(
124173
"UploadTestArtifactToS3Error: $errorMessage",
@@ -162,7 +211,11 @@ class CodeWhispererZipUploadManager(private val project: Project) {
162211
val errorMessage = getTelemetryErrorMessage(e, featureUseCase)
163212
when (featureUseCase) {
164213
CodeWhispererConstants.FeatureName.CODE_REVIEW ->
165-
codeScanServerException("CreateUploadUrlException after $attempts attempts: $errorMessage")
214+
codeScanServerException(
215+
"CreateUploadUrlException after $attempts attempts: $errorMessage",
216+
requestId = (e as? AwsServiceException)?.requestId(),
217+
httpStatusCode = (e as? AwsServiceException)?.statusCode().toString()
218+
)
166219

167220
CodeWhispererConstants.FeatureName.TEST_GENERATION ->
168221
throw CodeTestException(

plugins/core/jetbrains-community/resources/telemetryOverride.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@
192192
"name": "relativePath",
193193
"type": "string",
194194
"description": "The relative path of the file"
195+
},
196+
{
197+
"name": "amazonqUploadIntent",
198+
"type": "string",
199+
"description": "The intent of the upload",
200+
"allowedValues": [
201+
"TRANSFORMATION",
202+
"TASK_ASSIST_PLANNING",
203+
"AUTOMATIC_FILE_SECURITY_SCAN",
204+
"FULL_PROJECT_SECURITY_SCAN"
205+
]
195206
}
196207
],
197208
"metrics": [
@@ -469,6 +480,28 @@
469480
}
470481
]
471482
},
483+
{
484+
"name": "amazonq_createUpload",
485+
"description": "Captures createUploadUrl invocation process",
486+
"metadata": [
487+
{
488+
"type": "amazonqConversationId",
489+
"required": false
490+
},
491+
{
492+
"type": "amazonqRepositorySize",
493+
"required": false
494+
},
495+
{
496+
"type": "amazonqUploadIntent",
497+
"required": false
498+
},
499+
{
500+
"type": "credentialStartUrl",
501+
"required": false
502+
}
503+
]
504+
},
472505
{
473506
"name": "auth_modifyConnection",
474507
"description": "An auth connection was modified in some way, e.g. deleted, updated",

0 commit comments

Comments
 (0)