Browse Source

Instrumentation-specific bootstrap classes (#3495)

* Instrumentation-specific bootstrap classes

* try to move bootstrap modules dependency to javaagent
Mateusz Rzeszutek 3 years ago
parent
commit
3d1e782fc5

+ 1 - 1
buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts

@@ -261,7 +261,7 @@ idea {
 }
 
 when (projectDir.name) {
-  "javaagent", "library", "testing" -> {
+  "bootstrap", "javaagent", "library", "testing" -> {
     // We don't use this group anywhere in our config, but we need to make sure it is unique per
     // instrumentation so Gradle doesn't merge projects with same name due to a bug in Gradle.
     // https://github.com/gradle/gradle/issues/847

+ 8 - 0
instrumentation/build.gradle.kts

@@ -6,6 +6,8 @@ plugins {
   id("otel.java-conventions")
 }
 
+val bootstrap by configurations.creating
+
 val instrumentationProjectTest = tasks.named("test")
 val instrumentationProjectDependencies = dependencies
 
@@ -15,6 +17,12 @@ subprojects {
     instrumentationProjectTest.configure {
       dependsOn(subProj.tasks.named("test"))
     }
+
+    if (subProj.name == "bootstrap") {
+      instrumentationProjectDependencies.run {
+        add(bootstrap.name, project(subProj.path))
+      }
+    }
   }
 
   plugins.withId("otel.javaagent-instrumentation") {

+ 7 - 0
instrumentation/undertow-1.4/bootstrap/build.gradle.kts

@@ -0,0 +1,7 @@
+plugins {
+  id("otel.java-conventions")
+}
+
+dependencies {
+  compileOnly("io.opentelemetry:opentelemetry-api")
+}

+ 37 - 0
instrumentation/undertow-1.4/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/undertow/KeyHolder.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.bootstrap.undertow;
+
+import java.util.IdentityHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Undertow's {@code io.undertow.server.HttpServerExchange} uses {@code
+ * io.undertow.util.AttachmentKey} as a key for storing arbitrary data. It uses {@link
+ * IdentityHashMap} and thus all keys are compared for equality by object reference. This means that
+ * we cannot hold an instance of {@code io.undertow.util.AttachmentKey} in a static field of the
+ * corresponding Tracer, as we usually do. Tracers are loaded into user's classloaders and thus it
+ * is totally possible to have several instances of tracers. Each of those instances will have a
+ * separate value in a static field and {@code io.undertow.server.HttpServerExchange} will treat
+ * them as different keys then.
+ *
+ * <p>That is why this class exists and resides in a separate package. This package is treated in a
+ * special way and is always loaded by bootstrap classloader. This makes sure that this class is
+ * available to all tracers from every classloader.
+ *
+ * <p>But at the same time, being loaded by bootstrap classloader, this class itself cannot initiate
+ * the loading of {@code io.undertow.util.AttachmentKey} class. Class has to be loaded by <i>any</i>
+ * classloader that has it, e.g. by the classloader of a Tracer that uses this key holder. After
+ * that, <i>all</i> Tracers, loaded by all classloaders, will be able to use exactly the same sole
+ * instance of the key.
+ */
+// TODO allow instrumentation to have their own classes that should go to bootstrap classloader
+public final class KeyHolder {
+  public static final ConcurrentMap<Class<?>, Object> contextKeys = new ConcurrentHashMap<>();
+
+  private KeyHolder() {}
+}

+ 48 - 0
instrumentation/undertow-1.4/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/undertow/UndertowActiveHandlers.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.bootstrap.undertow;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.ContextKey;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Helper container for keeping track of request processing state in undertow. */
+public final class UndertowActiveHandlers {
+  private static final ContextKey<AtomicInteger> CONTEXT_KEY =
+      ContextKey.named("opentelemetry-undertow-active-handlers");
+
+  private UndertowActiveHandlers() {}
+
+  /**
+   * Attach to context.
+   *
+   * @param context server context
+   * @param initialValue initial value for counter
+   * @return new context
+   */
+  public static Context init(Context context, int initialValue) {
+    return context.with(CONTEXT_KEY, new AtomicInteger(initialValue));
+  }
+
+  /**
+   * Increment counter.
+   *
+   * @param context server context
+   */
+  public static void increment(Context context) {
+    context.get(CONTEXT_KEY).incrementAndGet();
+  }
+
+  /**
+   * Decrement counter.
+   *
+   * @param context server context
+   * @return value of counter after decrementing it
+   */
+  public static int decrementAndGet(Context context) {
+    return context.get(CONTEXT_KEY).decrementAndGet();
+  }
+}

+ 2 - 0
instrumentation/undertow-1.4/javaagent/build.gradle.kts

@@ -13,4 +13,6 @@ muzzle {
 
 dependencies {
   library("io.undertow:undertow-core:2.0.0.Final")
+
+  compileOnly(project(":instrumentation:undertow-1.4:bootstrap"))
 }

+ 2 - 2
instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpServerTracer.java

@@ -12,8 +12,8 @@ import io.opentelemetry.context.propagation.TextMapGetter;
 import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
 import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
 import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
-import io.opentelemetry.javaagent.instrumentation.api.undertow.KeyHolder;
-import io.opentelemetry.javaagent.instrumentation.api.undertow.UndertowActiveHandlers;
+import io.opentelemetry.javaagent.bootstrap.undertow.KeyHolder;
+import io.opentelemetry.javaagent.bootstrap.undertow.UndertowActiveHandlers;
 import io.undertow.server.DefaultResponseListener;
 import io.undertow.server.HttpServerExchange;
 import io.undertow.util.AttachmentKey;

+ 4 - 3
javaagent/build.gradle.kts

@@ -115,7 +115,9 @@ tasks {
   }
 }
 
-val licenseReportDependencies by configurations.creating
+val licenseReportDependencies by configurations.creating {
+  extendsFrom(shadowInclude)
+}
 
 dependencies {
   testCompileOnly(project(":javaagent-bootstrap"))
@@ -126,6 +128,7 @@ dependencies {
   testImplementation("io.opentracing.contrib.dropwizard:dropwizard-opentracing:0.2.2")
 
   shadowInclude(project(":javaagent-bootstrap"))
+  shadowInclude(project(":instrumentation", configuration = "bootstrap"))
 
   // We only have compileOnly dependencies on these to make sure they don"t leak into POMs.
   licenseReportDependencies("com.github.ben-manes.caffeine:caffeine") {
@@ -137,10 +140,8 @@ dependencies {
   //  but I couldn"t get that to work
   licenseReportDependencies(project(":javaagent-tooling"))
   licenseReportDependencies(project(":javaagent-extension-api"))
-  licenseReportDependencies(project(":javaagent-bootstrap"))
 }
 
-
 licenseReport {
   outputDir = rootProject.file("licenses").absolutePath
 

+ 1 - 0
settings.gradle.kts

@@ -308,6 +308,7 @@ include(":instrumentation:tomcat:tomcat-7.0:javaagent")
 include(":instrumentation:tomcat:tomcat-10.0:javaagent")
 include(":instrumentation:tomcat:tomcat-common:javaagent")
 include(":instrumentation:twilio-6.6:javaagent")
+include(":instrumentation:undertow-1.4:bootstrap")
 include(":instrumentation:undertow-1.4:javaagent")
 include(":instrumentation:vaadin-14.2:javaagent")
 include(":instrumentation:vaadin-14.2:testing")

+ 1 - 0
testing/agent-for-testing/build.gradle.kts

@@ -68,6 +68,7 @@ tasks {
 dependencies {
   // Dependencies to include without obfuscation.
   shadowInclude(project(":javaagent-bootstrap"))
+  shadowInclude(project(":instrumentation", configuration = "bootstrap"))
 
   testImplementation(project(":testing-common"))
   testImplementation("io.opentelemetry:opentelemetry-api")