Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 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
11 changes: 9 additions & 2 deletions plugins/core/jetbrains-community/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@ buildscript {
}
}

private val generatedSrcDir = project.layout.buildDirectory.dir("generated-src")
sourceSets {
main {
java.srcDir(project.layout.buildDirectory.dir("generated-src"))
java.srcDir(generatedSrcDir)
}
}

idea {
module {
generatedSourceDirs = generatedSourceDirs.toMutableSet() + generatedSrcDir.get().asFile
}
}

val generateTelemetry = tasks.register<GenerateTelemetry>("generateTelemetry") {
inputFiles = listOf(file("${project.projectDir}/resources/telemetryOverride.json"))
outputDirectory = project.layout.buildDirectory.dir("generated-src").get().asFile
outputDirectory = generatedSrcDir.get().asFile

doFirst {
outputDirectory.deleteRecursively()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.intellij.platform.diagnostic.telemetry.helpers

import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.StatusCode
import kotlin.coroutines.cancellation.CancellationException

val EXCEPTION_ESCAPED = AttributeKey.booleanKey("exception.escaped")

inline fun <T> Span.useWithoutActiveScope(operation: (Span) -> T): T {
try {
return operation(this)
} catch (e: CancellationException) {
recordException(e, Attributes.of(EXCEPTION_ESCAPED, true))
throw e
} catch (e: Throwable) {
recordException(e, Attributes.of(EXCEPTION_ESCAPED, true))
setStatus(StatusCode.ERROR)
throw e
} finally {
end()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
@file:Suppress("UnusedPrivateClass")

package software.aws.toolkits.jetbrains.services.telemetry.otel

import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.platform.util.http.ContentType
import com.intellij.platform.util.http.httpPost
import com.intellij.serviceContainer.NonInjectable
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator
import io.opentelemetry.context.Context
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler
import io.opentelemetry.sdk.OpenTelemetrySdk
import io.opentelemetry.sdk.resources.Resource
import io.opentelemetry.sdk.trace.ReadWriteSpan
import io.opentelemetry.sdk.trace.ReadableSpan
import io.opentelemetry.sdk.trace.SdkTracerProvider
import io.opentelemetry.sdk.trace.SpanProcessor
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.amazon.awssdk.http.ContentStreamProvider
import software.amazon.awssdk.http.HttpExecuteRequest
import software.amazon.awssdk.http.SdkHttpMethod
import software.amazon.awssdk.http.SdkHttpRequest
import software.amazon.awssdk.http.apache.ApacheHttpClient
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner
import java.io.ByteArrayOutputStream
import java.net.ConnectException

private class BasicOtlpSpanProcessor(

Check warning on line 40 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Class "BasicOtlpSpanProcessor" is never used

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Class "BasicOtlpSpanProcessor" is never used
private val coroutineScope: CoroutineScope,
private val traceUrl: String = "http://127.0.0.1:4318/v1/traces",
) : SpanProcessor {
override fun onStart(parentContext: Context, span: ReadWriteSpan) {}
override fun isStartRequired() = false
override fun isEndRequired() = true

override fun onEnd(span: ReadableSpan) {
val data = span.toSpanData()
coroutineScope.launch {
try {
val item = TraceRequestMarshaler.create(listOf(data))

httpPost(traceUrl, contentLength = item.binarySerializedSize.toLong(), contentType = ContentType.XProtobuf) {

Check warning on line 54 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'httpPost(java.lang.String, long, com.intellij.platform.util.http.ContentType, kotlin.jvm.functions.Function2,? extends java.lang.Object\>, kotlin.coroutines.Continuation)' is marked unstable with @ApiStatus.Internal

Check warning

Code scanning / QDJVMC

Unstable API Usage Warning

'httpPost(java.lang.String, long, com.intellij.platform.util.http.ContentType, kotlin.jvm.functions.Function2,? extends java.lang.Object>, kotlin.coroutines.Continuation)' is marked unstable with @ApiStatus.Internal
item.writeBinaryTo(this)
}
} catch (e: CancellationException) {
throw e
} catch (e: ConnectException) {
thisLogger().warn("Cannot export (url=$traceUrl): ${e.message}")

Check notice on line 60 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Non-distinguishable logging calls

Similar log messages

Check notice

Code scanning / QDJVMC

Non-distinguishable logging calls Note

Similar log messages
} catch (e: Throwable) {
thisLogger().error("Cannot export (url=$traceUrl)", e)
}
}
}
}

private class SigV4OtlpSpanProcessor(

Check warning on line 68 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Class "SigV4OtlpSpanProcessor" is never used

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Class "SigV4OtlpSpanProcessor" is never used
private val coroutineScope: CoroutineScope,
private val traceUrl: String,
private val creds: AwsCredentialsProvider,
) : SpanProcessor {
override fun onStart(parentContext: Context, span: ReadWriteSpan) {}
override fun isStartRequired() = false
override fun isEndRequired() = true

private val client = ApacheHttpClient.create()

override fun onEnd(span: ReadableSpan) {
coroutineScope.launch {
val data = span.toSpanData()
try {
val item = TraceRequestMarshaler.create(listOf(data))
// calculate the sigv4 header
val signer = AwsV4HttpSigner.create()
val httpRequest =
SdkHttpRequest.builder()
.uri(traceUrl)
.method(SdkHttpMethod.POST)
.putHeader("Content-Type", "application/x-protobuf")
.build()

val baos = ByteArrayOutputStream()
item.writeBinaryTo(baos)
val payload = ContentStreamProvider.fromByteArray(baos.toByteArray())
val signedRequest = signer.sign {
it.identity(creds.resolveIdentity().get())
it.request(httpRequest)
it.payload(payload)
it.putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, "osis")
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this property name stand for

Copy link
Contributor Author

Choose a reason for hiding this comment

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

open search ingestion service

it.putProperty(AwsV4HttpSigner.REGION_NAME, "us-west-2")
}

// Create and HTTP client and send the request. ApacheHttpClient requires the 'apache-client' module.
client.prepareRequest(
HttpExecuteRequest.builder()
.request(signedRequest.request())
.contentStreamProvider(signedRequest.payload().orElse(null))
.build()
).call()
} catch (e: CancellationException) {
throw e
} catch (e: ConnectException) {
thisLogger().warn("Cannot export (url=$traceUrl): ${e.message}")

Check notice on line 114 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Non-distinguishable logging calls

Similar log messages

Check notice

Code scanning / QDJVMC

Non-distinguishable logging calls Note

Similar log messages
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: should this also be log.error?

} catch (e: Throwable) {
thisLogger().error("Cannot export (url=$traceUrl)", e)
}
}
}
}

private object StdoutSpanProcessor : SpanProcessor {
override fun onStart(parentContext: Context, span: ReadWriteSpan) {}
override fun isStartRequired() = false
override fun isEndRequired() = true

override fun onEnd(span: ReadableSpan) {
println(span.toSpanData())
}
}

@Service
class OTelService @NonInjectable internal constructor(spanProcessors: List<SpanProcessor>) : Disposable {
@Suppress("unused")
constructor() : this(listOf(StdoutSpanProcessor))

private val sdkDelegate = lazy {
OpenTelemetrySdk.builder()
.setTracerProvider(
SdkTracerProvider.builder()
.apply {
spanProcessors.forEach {
addSpanProcessor(it)
}
}
.setResource(
Resource.create(
Attributes.builder()
.put(AttributeKey.stringKey("os.type"), SystemInfoRt.OS_NAME)
Copy link
Contributor

Choose a reason for hiding this comment

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

Where would we use this recorded information other than telemetry?
Should this have all the fields that we record for the telemetry publisher?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

these are common fields understood by OTLP processors

.put(AttributeKey.stringKey("os.version"), SystemInfoRt.OS_VERSION)
.put(AttributeKey.stringKey("host.arch"), System.getProperty("os.arch"))
.build()
)
)
.build()
)
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
.build()
}
internal val sdk: OpenTelemetrySdk by sdkDelegate

override fun dispose() {
if (sdkDelegate.isInitialized()) {
sdk.close()
}
}

companion object {
fun getSdk() = service<OTelService>().sdk

Check warning on line 169 in plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OTelService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "getSdk" is never used

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Function "getSdk" is never used
}
}
Loading
Loading