Instrumentation-specific bootstrap classes (#3495)

* Instrumentation-specific bootstrap classes

* try to move bootstrap modules dependency to javaagent
This commit is contained in:
Mateusz Rzeszutek 2021-07-13 09:34:02 +02:00 committed by GitHub
parent e5bbc392c6
commit 3d1e782fc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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