[maven] IDEA-340498 trace maven server calls

GitOrigin-RevId: 014b3b0549e7e5a7ac74f219d94d38b235bd24a4
This commit is contained in:
Dmitry Kichinsky
2024-02-03 20:36:57 +01:00
committed by intellij-monorepo-bot
parent 48f1e64951
commit bdf57a7c4b
13 changed files with 149 additions and 34 deletions

View File

@@ -25,4 +25,8 @@ public class MavenServerResponse<T extends Serializable> implements Serializable
public LongRunningTaskStatus getStatus() {
return status;
}
public byte[] getTelemetryTrace() {
return telemetryTrace;
}
}

View File

@@ -106,6 +106,8 @@
<orderEntry type="module" module-name="intellij.platform.backend.observation" />
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
<orderEntry type="module" module-name="intellij.platform.diagnostic.telemetry" />
<orderEntry type="module" module-name="intellij.platform.diagnostic.telemetry.impl" />
<orderEntry type="module" module-name="intellij.maven.server.telemetry" />
</component>
<component name="copyright">
<Base>

View File

@@ -8,8 +8,10 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.util.rt" />
<orderEntry type="module" module-name="intellij.maven.server" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
<orderEntry type="library" name="opentelemetry" level="project" />
<orderEntry type="module" module-name="intellij.maven.server" />
<orderEntry type="library" name="opentelemetry-exporter-otlp-common" level="project" />
<orderEntry type="library" name="jackson" level="project" />
</component>
</module>

View File

@@ -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<SpanData> collectedSpans = mySpanDataCollector.getCollectedSpans();
return MavenSpanDataSerializer.serialize(collectedSpans);
}
Collection<SpanData> 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) {

View File

@@ -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<Class<?>> TELEMETRY_CLASSES = Arrays.asList(
SpanExporter.class,
TextMapPropagator.class,
OpenTelemetry.class,
OpenTelemetrySdk.class,
Clock.class,
SdkMeterProvider.class,
SdkLoggerProvider.class,
TraceRequestMarshaler.class,
MarshalerWithSize.class,
JsonGenerator.class
);
}

View File

@@ -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);
}
}

View File

@@ -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"));

View File

@@ -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);

View File

@@ -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

View File

@@ -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<Module>
}
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<VirtualFile>,
profiles: MavenExplicitProfiles,

View File

@@ -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
}
}

View File

@@ -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)
}
}
}

View File

@@ -1747,4 +1747,12 @@ public class MavenUtil {
public static MavenProjectModelReadHelper createModelReadHelper(Project project) {
return new MavenProjectModelServerModelReadHelper(project);
}
public static Collection<File> collectClasspath(Collection<Class<?>> classes) {
var result = new ArrayList<File>();
for (Class<?> c : classes) {
result.add(new File(PathUtil.getJarPathForClass(c)));
}
return result;
}
}