diff --git a/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerResponse.java b/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerResponse.java index 4b3b21676eec..7355a1152e80 100644 --- a/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerResponse.java +++ b/plugins/maven-server-api/src/main/java/org/jetbrains/idea/maven/server/MavenServerResponse.java @@ -25,4 +25,8 @@ public class MavenServerResponse implements Serializable public LongRunningTaskStatus getStatus() { return status; } + + public byte[] getTelemetryTrace() { + return telemetryTrace; + } } diff --git a/plugins/maven/intellij.maven.iml b/plugins/maven/intellij.maven.iml index 3e6a8a3de941..b0e09865215c 100644 --- a/plugins/maven/intellij.maven.iml +++ b/plugins/maven/intellij.maven.iml @@ -106,6 +106,8 @@ + + diff --git a/plugins/maven/maven-server-telemetry/intellij.maven.server.telemetry.iml b/plugins/maven/maven-server-telemetry/intellij.maven.server.telemetry.iml index 0e09c2d02f98..4d07e3e987e8 100644 --- a/plugins/maven/maven-server-telemetry/intellij.maven.server.telemetry.iml +++ b/plugins/maven/maven-server-telemetry/intellij.maven.server.telemetry.iml @@ -8,8 +8,10 @@ + - + + \ No newline at end of file diff --git a/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerOpenTelemetry.java b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerOpenTelemetry.java index 3aa143a9c32a..cd174259bd61 100644 --- a/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerOpenTelemetry.java +++ b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerOpenTelemetry.java @@ -125,9 +125,7 @@ final class MavenServerOpenTelemetryImpl implements MavenServerOpenTelemetry { @Override public byte[] shutdown() { try { - if (null != rootSpan) { - rootSpan.end(); - } + rootSpan.end(); if (myScope != null) { myScope.close(); } @@ -135,15 +133,12 @@ final class MavenServerOpenTelemetryImpl implements MavenServerOpenTelemetry { ((Closeable)myOpenTelemetry).close(); } // the data should be exported only after OpenTelemetry was closed to prevent data loss - if (mySpanDataCollector != null) { - Collection collectedSpans = mySpanDataCollector.getCollectedSpans(); - return MavenSpanDataSerializer.serialize(collectedSpans); - } + Collection collectedSpans = mySpanDataCollector.getCollectedSpans(); + return MavenSpanDataSerializer.serialize(collectedSpans); } catch (Exception e) { - // ignore + throw new RuntimeException(e); } - return ArrayUtilRt.EMPTY_BYTE_ARRAY; } private static @NotNull Scope injectTracingContext(@NotNull OpenTelemetry telemetry, @NotNull MavenTracingContext context) { diff --git a/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerTelemetryClasspathUtil.java b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerTelemetryClasspathUtil.java new file mode 100644 index 000000000000..a1d62309dc15 --- /dev/null +++ b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenServerTelemetryClasspathUtil.java @@ -0,0 +1,31 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.maven.server.telemetry; + +import com.fasterxml.jackson.core.JsonGenerator; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; +import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.common.Clock; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +import java.util.Arrays; +import java.util.Collection; + +public final class MavenServerTelemetryClasspathUtil { + public static final Collection> TELEMETRY_CLASSES = Arrays.asList( + SpanExporter.class, + TextMapPropagator.class, + OpenTelemetry.class, + OpenTelemetrySdk.class, + Clock.class, + SdkMeterProvider.class, + SdkLoggerProvider.class, + TraceRequestMarshaler.class, + MarshalerWithSize.class, + JsonGenerator.class + ); +} diff --git a/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenSpanDataSerializer.java b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenSpanDataSerializer.java index 3e1a9713fd51..434c8a108a07 100644 --- a/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenSpanDataSerializer.java +++ b/plugins/maven/maven-server-telemetry/src/com/intellij/maven/server/telemetry/MavenSpanDataSerializer.java @@ -1,7 +1,6 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.maven.server.telemetry; -import com.intellij.util.ArrayUtilRt; import io.opentelemetry.sdk.trace.data.SpanData; import org.jetbrains.annotations.NotNull; @@ -16,7 +15,7 @@ public class MavenSpanDataSerializer { return outputStream.toByteArray(); } catch (Exception e) { - return ArrayUtilRt.EMPTY_BYTE_ARRAY; + throw new RuntimeException(e); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java index 07c4d53070bc..3b30f4d3d243 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java @@ -1,6 +1,7 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.maven.maven3; +import com.intellij.maven.server.telemetry.MavenServerTelemetryClasspathUtil; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtil; @@ -94,14 +95,7 @@ final class Maven3Support implements MavenVersionAwareSupportExtension { classpath.add(new File(root, "intellij.maven.server")); classpath.add(new File(root, "intellij.maven.server.telemetry")); - try { - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.sdk.trace.export.SpanExporter")))); - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.context.propagation.TextMapPropagator")))); - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.api.OpenTelemetry")))); - } - catch (ClassNotFoundException e) { - MavenLog.LOG.error(e); - } + classpath.addAll(MavenUtil.collectClasspath(MavenServerTelemetryClasspathUtil.TELEMETRY_CLASSES)); File parentFile = MavenUtil.getMavenPluginParentFile(); classpath.add(new File(root, "intellij.maven.server.m3.common")); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java index 8d5b25e83a79..3d17fd23447f 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java @@ -1,6 +1,7 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.maven.maven4; +import com.intellij.maven.server.telemetry.MavenServerTelemetryClasspathUtil; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtil; @@ -91,14 +92,7 @@ final class Maven4Support implements MavenVersionAwareSupportExtension { classpath.add(new File(root, "intellij.maven.server")); classpath.add(new File(root, "intellij.maven.server.telemetry")); - try { - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.sdk.trace.export.SpanExporter")))); - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.context.propagation.TextMapPropagator")))); - classpath.add(new File(PathUtil.getJarPathForClass(Class.forName("io.opentelemetry.api.OpenTelemetry")))); - } - catch (ClassNotFoundException e) { - MavenLog.LOG.error(e); - } + classpath.addAll(MavenUtil.collectClasspath(MavenServerTelemetryClasspathUtil.TELEMETRY_CLASSES)); File parentFile = MavenUtil.getMavenPluginParentFile(); addDir(classpath, new File(parentFile, "maven40-server-impl/lib"), f -> true); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt index 852e300557fb..f745f336f7f0 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt @@ -26,6 +26,7 @@ import org.jetbrains.idea.maven.model.* import org.jetbrains.idea.maven.project.MavenResolveResultProblemProcessor.BLOCKED_MIRROR_FOR_REPOSITORIES import org.jetbrains.idea.maven.project.MavenResolveResultProblemProcessor.MavenResolveProblemHolder import org.jetbrains.idea.maven.server.* +import org.jetbrains.idea.maven.telemetry.tracer import org.jetbrains.idea.maven.utils.MavenLog import org.jetbrains.idea.maven.utils.MavenUtil import java.lang.reflect.InvocationTargetException diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerEx.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerEx.kt index 02447893fb36..636d90fbd612 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerEx.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerEx.kt @@ -20,8 +20,6 @@ import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.platform.backend.observation.trackActivity -import com.intellij.platform.diagnostic.telemetry.Scope -import com.intellij.platform.diagnostic.telemetry.TelemetryManager import com.intellij.platform.ide.progress.runWithModalProgressBlocking import com.intellij.platform.ide.progress.withBackgroundProgress import com.intellij.platform.util.progress.RawProgressReporter @@ -45,6 +43,7 @@ import org.jetbrains.idea.maven.model.MavenExplicitProfiles import org.jetbrains.idea.maven.project.preimport.MavenProjectStaticImporter import org.jetbrains.idea.maven.server.MavenWrapperDownloader import org.jetbrains.idea.maven.server.showUntrustedProjectNotification +import org.jetbrains.idea.maven.telemetry.tracer import org.jetbrains.idea.maven.utils.MavenActivityKey import org.jetbrains.idea.maven.utils.MavenLog import org.jetbrains.idea.maven.utils.MavenUtil @@ -91,8 +90,6 @@ interface MavenAsyncProjectsManager { previewModule: Module?): List } -internal val tracer by lazy { TelemetryManager.getSimpleTracer(Scope("maven")) } - open class MavenProjectsManagerEx(project: Project, private val cs: CoroutineScope) : MavenProjectsManager(project) { override suspend fun addManagedFilesWithProfilesAndUpdate(files: List, profiles: MavenExplicitProfiles, diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenEmbedderWrapper.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenEmbedderWrapper.kt index ff345524f3e2..953fcd70c99a 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenEmbedderWrapper.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenEmbedderWrapper.kt @@ -17,6 +17,9 @@ import org.jetbrains.idea.maven.buildtool.MavenSyncConsole import org.jetbrains.idea.maven.model.* import org.jetbrains.idea.maven.project.MavenConsole import org.jetbrains.idea.maven.server.MavenEmbedderWrapper.LongRunningEmbedderTask +import org.jetbrains.idea.maven.telemetry.getCurrentTelemetryIds +import org.jetbrains.idea.maven.telemetry.scheduleExportTelemetryTrace +import org.jetbrains.idea.maven.telemetry.tracer import org.jetbrains.idea.maven.utils.MavenLog import org.jetbrains.idea.maven.utils.MavenProcessCanceledException import java.io.File @@ -268,13 +271,15 @@ abstract class MavenEmbedderWrapper internal constructor(private val project: Pr } try { - withContext(Dispatchers.IO) { + withContext(Dispatchers.IO + tracer.span("runLongRunningTask")) { + val telemetryIds = getCurrentTelemetryIds() blockingContext { - val longRunningTaskInput = LongRunningTaskInput(longRunningTaskId, null, null) + val longRunningTaskInput = LongRunningTaskInput(longRunningTaskId, telemetryIds.traceId, telemetryIds.spanId) val response = task.run(embedder, longRunningTaskInput) val status = response.status eventHandler.handleConsoleEvents(status.consoleEvents()) eventHandler.handleDownloadEvents(status.downloadEvents()) + scheduleExportTelemetryTrace(project, response.telemetryTrace) response.result } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/telemetry/telemetry.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/telemetry/telemetry.kt new file mode 100644 index 000000000000..b2f87bb26f86 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/telemetry/telemetry.kt @@ -0,0 +1,83 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.maven.telemetry + +import com.intellij.diagnostic.ActivityImpl +import com.intellij.diagnostic.CoroutineTracerShim +import com.intellij.openapi.project.Project +import com.intellij.platform.diagnostic.telemetry.Scope +import com.intellij.platform.diagnostic.telemetry.TelemetryManager +import com.intellij.platform.diagnostic.telemetry.impl.computeSpanId +import com.intellij.platform.diagnostic.telemetry.impl.computeTraceId +import kotlinx.coroutines.launch +import org.jetbrains.idea.maven.utils.MavenCoroutineScopeProvider +import org.jetbrains.idea.maven.utils.MavenLog +import org.jetbrains.idea.maven.utils.MavenUtil +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +internal val tracer by lazy { TelemetryManager.getSimpleTracer(Scope(MavenUtil.MAVEN_NAME)) } + +data class TelemetryIds(val traceId: String?, val spanId: String?) + +private val emptyIds = TelemetryIds(null, null) + +suspend fun getCurrentTelemetryIds(): TelemetryIds { + val activity = CoroutineTracerShim.coroutineTracer.getTraceActivity() + if (null == activity) return emptyIds + if (activity !is ActivityImpl) { + MavenLog.LOG.error("ActivityImpl expected") + return emptyIds + } + val traceId = bytesToHex(computeTraceId(activity)) + val spanId = bytesToHex(computeSpanId(activity)) + + return TelemetryIds(traceId, spanId) +} + +private fun bytesToHex(bytes: ByteArray): String { + val hexString = StringBuilder() + for (b in bytes) { + val hex = Integer.toHexString(0xff and b.toInt()) + if (hex.length == 1) { + hexString.append('0') + } + hexString.append(hex) + } + return hexString.toString() +} + +private fun getOpenTelemetryAddress(): URI? { + val property = System.getProperty("idea.diagnostic.opentelemetry.otlp") + if (property == null) { + return null + } + if (property.endsWith("/")) { + return URI.create(property + "v1/traces") + } + return URI.create("$property/v1/traces") +} + +fun scheduleExportTelemetryTrace(project: Project, binaryTrace: ByteArray) { + if (binaryTrace.isEmpty()) return + + val telemetryHost = getOpenTelemetryAddress() + if (null == telemetryHost) return + + val cs = MavenCoroutineScopeProvider.getCoroutineScope(project) + cs.launch { + try { + HttpClient.newHttpClient() + .send(HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofByteArray(binaryTrace)) + .uri(telemetryHost) + .header("Content-Type", "application/x-protobuf") + .build(), + HttpResponse.BodyHandlers.discarding()) + } + catch (e: java.lang.Exception) { + MavenLog.LOG.error("Unable to upload performance traces to the OTLP server", e) + } + } +} \ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index ee2289e2d03f..af94627815ef 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -1747,4 +1747,12 @@ public class MavenUtil { public static MavenProjectModelReadHelper createModelReadHelper(Project project) { return new MavenProjectModelServerModelReadHelper(project); } + + public static Collection collectClasspath(Collection> classes) { + var result = new ArrayList(); + for (Class c : classes) { + result.add(new File(PathUtil.getJarPathForClass(c))); + } + return result; + } }