diff --git a/WORKSPACE b/WORKSPACE index 9f578f1a0f8..c7314c54370 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,6 +4,28 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_maven_import_external") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +RULES_JVM_EXTERNAL_TAG = "4.2" +RULES_JVM_EXTERNAL_SHA = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca" + +http_archive( + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + sha256 = RULES_JVM_EXTERNAL_SHA, + url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, +) + +load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps") + +rules_jvm_external_deps() + +load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup") + +rules_jvm_external_setup() + +load("@rules_jvm_external//:defs.bzl", "maven_install") + # Long-lived download links available at: https://www.jetbrains.com/intellij-repository/releases # The plugin api for IntelliJ 2021.2. This is required to build IJwB, @@ -508,3 +530,20 @@ jvm_maven_import_external( licenses = ["notice"], # Apache 2.0 server_urls = ["https://repo1.maven.org/maven2"], ) + +maven_install( + artifacts = [ + "junit:junit:4.12", # removing this breaks rules_jvm_external at the time of commit + "io.opentelemetry:opentelemetry-bom:1.14.0", + "io.opentelemetry:opentelemetry-api:1.14.0", + "io.opentelemetry:opentelemetry-sdk:1.14.0", + "io.opentelemetry:opentelemetry-sdk-common:1.14.0", + "io.opentelemetry:opentelemetry-sdk-trace:1.14.0", + "io.opentelemetry:opentelemetry-exporter-otlp:1.14.0", + "io.opentelemetry:opentelemetry-exporter-otlp-trace:1.14.0", + "io.opentelemetry:opentelemetry-semconv:1.14.0-alpha", + ], + repositories = [ + "https://repo1.maven.org/maven2", + ], +) \ No newline at end of file diff --git a/base/BUILD b/base/BUILD index 5c002cfb741..5e199f57ca1 100644 --- a/base/BUILD +++ b/base/BUILD @@ -38,6 +38,14 @@ java_library( "//sdkcompat", "//third_party/auto_value", "@error_prone_annotations//jar", + "@maven//:io_opentelemetry_opentelemetry_bom", + "@maven//:io_opentelemetry_opentelemetry_api", + "@maven//:io_opentelemetry_opentelemetry_sdk", + "@maven//:io_opentelemetry_opentelemetry_sdk_common", + "@maven//:io_opentelemetry_opentelemetry_sdk_trace", + "@maven//:io_opentelemetry_opentelemetry_exporter_otlp", + "@maven//:io_opentelemetry_opentelemetry_exporter_otlp_trace", + "@maven//:io_opentelemetry_opentelemetry_semconv", ], ) diff --git a/base/src/com/google/idea/blaze/base/analytics/OtlpTraceSpan.java b/base/src/com/google/idea/blaze/base/analytics/OtlpTraceSpan.java new file mode 100644 index 00000000000..b309b865779 --- /dev/null +++ b/base/src/com/google/idea/blaze/base/analytics/OtlpTraceSpan.java @@ -0,0 +1,30 @@ +package com.google.idea.blaze.base.analytics; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; + +import java.util.Objects; + +public final class OtlpTraceSpan implements TraceSpan { + + private static final String SCOPE_NAME = "sync"; + private final Span span; + + private OtlpTraceSpan(Span span) { + Objects.requireNonNull(span); + this.span = span; + } + + public static TraceSpan create(String name) { + Tracer tracer = OtlpTracer.create(SCOPE_NAME); + Span span = tracer + .spanBuilder(name) + .startSpan(); + return new OtlpTraceSpan(span); + } + + @Override + public void close() { + span.end(); + } +} diff --git a/base/src/com/google/idea/blaze/base/analytics/OtlpTracer.java b/base/src/com/google/idea/blaze/base/analytics/OtlpTracer.java new file mode 100644 index 00000000000..cb455819a7a --- /dev/null +++ b/base/src/com/google/idea/blaze/base/analytics/OtlpTracer.java @@ -0,0 +1,68 @@ +package com.google.idea.blaze.base.analytics; + +import java.io.IOException; +import java.net.Socket; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +public final class OtlpTracer { + + private static final String OTLP_HOST = "localhost"; + private static final int OTLP_PORT = 4317; + private static final String SERVICE_NAME = "canva.devenv.bazelbuild-intellij"; + + // If we cannot open the specified port, we assume that + // opentelemetry-collector is not running and no-op the implementaton. + private static OpenTelemetry openTelemetry = isSocketListening(OTLP_HOST, OTLP_PORT) + ? initializeOpenTelemetrySdk(SERVICE_NAME, OTLP_HOST, OTLP_PORT) + : OpenTelemetry.noop(); + + public static Tracer create(String scopeName) { + return openTelemetry.getTracer(scopeName); + } + + private static OpenTelemetry initializeOpenTelemetrySdk(String serviceName, String host, int port) { + Attributes attributes = Attributes.builder() + .put(ResourceAttributes.SERVICE_NAME, serviceName) + .build(); + + Resource serviceResource = Resource + .create(attributes); + + OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder() + .setEndpoint(endpointAsString(host, port)) + .build(); + + SpanProcessor processor = SimpleSpanProcessor.create(exporter); + + SdkTracerProvider tracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(processor) + .setResource(serviceResource) + .build(); + + return OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .buildAndRegisterGlobal(); + } + + private static boolean isSocketListening(String host, int port) { + try (Socket ignored = new Socket(host, port)) { + return true; + } catch (IOException ignored) { + return false; + } + } + + private static String endpointAsString(String host, int port) { + return "http://" + host + ":" + port; + } +} \ No newline at end of file diff --git a/base/src/com/google/idea/blaze/base/analytics/TraceSpan.java b/base/src/com/google/idea/blaze/base/analytics/TraceSpan.java new file mode 100644 index 00000000000..9f55d7a81ea --- /dev/null +++ b/base/src/com/google/idea/blaze/base/analytics/TraceSpan.java @@ -0,0 +1,6 @@ +package com.google.idea.blaze.base.analytics; + +/** + * close() exports the span. + */ +public interface TraceSpan extends AutoCloseable { } diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java index 6f65c24a6fe..ea4cdb127f6 100644 --- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java +++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncManager.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; +import com.google.idea.blaze.base.analytics.TraceSpan; +import com.google.idea.blaze.base.analytics.OtlpTraceSpan; import com.google.idea.blaze.base.async.executor.ProgressiveTaskWithProgressIndicator; import com.google.idea.blaze.base.command.BlazeInvocationContext.ContextType; import com.google.idea.blaze.base.ideinfo.TargetKey; @@ -197,7 +199,7 @@ boolean shouldForceFullSync( } private static void executeTask(Project project, BlazeSyncParams params, BlazeContext context) { - try { + try (TraceSpan traceSpan = OtlpTraceSpan.create(params.title())) { SyncPhaseCoordinator.getInstance(project).syncProject(params, context).get(); } catch (InterruptedException e) { context.output(new PrintOutput("Sync interrupted: " + e.getMessage())); @@ -208,6 +210,9 @@ private static void executeTask(Project project, BlazeSyncParams params, BlazeCo } catch (CancellationException e) { context.output(new PrintOutput("Sync cancelled")); context.setCancelled(); + } catch (Exception e) { + context.output(new PrintOutput("Unexpected exception: " + e.getMessage())); + context.setHasError(); } }