Fix "Class path contains multiple SLF4J bindings" during runtime atta… (#380)
* Fix "Class path contains multiple SLF4J bindings" during runtime attachment * Update runtime-attach/build.gradle.kts Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> * Renaming Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
48db05aa9f
commit
4645beca88
|
|
@ -0,0 +1,5 @@
|
|||
# Runtime attachment core
|
||||
|
||||
This project is dedicated to providers of the [OpenTelemetry Java agent](https://github.com/open-telemetry/opentelemetry-java-instrumentation). It helps them to build a dependency to runtime attach their agent jar file.
|
||||
|
||||
You can find [here](../runtime-attach/README.md) the project of the distribution published by OpenTelemetry.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
plugins {
|
||||
id("otel.java-conventions")
|
||||
id("otel.publish-conventions")
|
||||
}
|
||||
|
||||
description = "To help in create an OpenTelemetry distro able to runtime attach an OpenTelemetry Java Instrumentation agent"
|
||||
|
||||
dependencies {
|
||||
implementation("net.bytebuddy:byte-buddy-agent:1.11.18")
|
||||
|
||||
// Used by byte-buddy but not brought in as a transitive dependency.
|
||||
compileOnly("com.google.code.findbugs:annotations")
|
||||
}
|
||||
|
|
@ -5,24 +5,25 @@
|
|||
|
||||
package io.opentelemetry.contrib.attach;
|
||||
|
||||
import io.opentelemetry.javaagent.OpenTelemetryAgent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.CodeSource;
|
||||
|
||||
final class AgentFileProvider {
|
||||
|
||||
static File getAgentFile() {
|
||||
private final String agentJarResourceName;
|
||||
|
||||
verifyExistenceOfAgentJarFile();
|
||||
AgentFileProvider(String agentJarResourceName) {
|
||||
this.agentJarResourceName = agentJarResourceName;
|
||||
}
|
||||
|
||||
File getAgentFile() {
|
||||
|
||||
Path tempDirPath = createTempDir();
|
||||
|
||||
Path tempAgentJarPath = createTempAgentJarFile(tempDirPath);
|
||||
Path tempAgentJarPath = createTempAgentJarFileIn(tempDirPath);
|
||||
|
||||
deleteTempDirOnJvmExit(tempDirPath, tempAgentJarPath);
|
||||
|
||||
|
|
@ -34,13 +35,6 @@ final class AgentFileProvider {
|
|||
tempDirPath.toFile().deleteOnExit();
|
||||
}
|
||||
|
||||
private static void verifyExistenceOfAgentJarFile() {
|
||||
CodeSource codeSource = OpenTelemetryAgent.class.getProtectionDomain().getCodeSource();
|
||||
if (codeSource == null) {
|
||||
throw new IllegalStateException("could not get agent jar location");
|
||||
}
|
||||
}
|
||||
|
||||
private static Path createTempDir() {
|
||||
Path tempDir;
|
||||
try {
|
||||
|
|
@ -51,23 +45,18 @@ final class AgentFileProvider {
|
|||
return tempDir;
|
||||
}
|
||||
|
||||
private static Path createTempAgentJarFile(Path tempDir) {
|
||||
URL url = OpenTelemetryAgent.class.getProtectionDomain().getCodeSource().getLocation();
|
||||
try {
|
||||
return copyTo(url, tempDir, "agent.jar");
|
||||
private Path createTempAgentJarFileIn(Path tempDir) {
|
||||
Path agentJarPath = tempDir.resolve("agent.jar");
|
||||
try (InputStream jarAsInputStream =
|
||||
AgentFileProvider.class.getResourceAsStream(this.agentJarResourceName)) {
|
||||
if (jarAsInputStream == null) {
|
||||
throw new IllegalStateException(this.agentJarResourceName + " resource can't be found");
|
||||
}
|
||||
Files.copy(jarAsInputStream, agentJarPath);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(
|
||||
"Runtime attachment can't create agent jar file in temp directory", e);
|
||||
}
|
||||
return agentJarPath;
|
||||
}
|
||||
|
||||
private static Path copyTo(URL url, Path tempDir, String fileName) throws IOException {
|
||||
Path tempFile = tempDir.resolve(fileName);
|
||||
try (InputStream in = url.openStream()) {
|
||||
Files.copy(in, tempFile);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
private AgentFileProvider() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.contrib.attach;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
|
||||
/** This class allows you to attach the OpenTelemetry Java agent at runtime. */
|
||||
public final class CoreRuntimeAttach {
|
||||
|
||||
private static final String AGENT_ENABLED_PROPERTY = "otel.javaagent.enabled";
|
||||
private static final String AGENT_ENABLED_ENV_VAR = "OTEL_JAVAAGENT_ENABLED";
|
||||
static final String MAIN_METHOD_CHECK_PROP =
|
||||
"otel.javaagent.testing.runtime-attach.main-method-check";
|
||||
|
||||
private final String agentJarResourceName;
|
||||
|
||||
/**
|
||||
* Creates a new {@code DistroRuntimeAttach} from the resource name of the agent jar.
|
||||
*
|
||||
* @param agentJarResourceName Resource name of the agent jar.
|
||||
*/
|
||||
public CoreRuntimeAttach(String agentJarResourceName) {
|
||||
this.agentJarResourceName = agentJarResourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the OpenTelemetry Java agent to the current JVM. The attachment must be requested at the
|
||||
* beginning of the main method.
|
||||
*/
|
||||
public void attachJavaagentToCurrentJVM() {
|
||||
if (!shouldAttach()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AgentFileProvider agentFileProvider = new AgentFileProvider(agentJarResourceName);
|
||||
|
||||
File javaagentFile = agentFileProvider.getAgentFile();
|
||||
ByteBuddyAgent.attach(javaagentFile, getPid());
|
||||
|
||||
if (!agentIsAttached()) {
|
||||
printError("Agent was not attached. An unexpected issue has happened.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SystemOut")
|
||||
private static void printError(String message) {
|
||||
// not using java.util.logging in order to avoid initializing the global LogManager
|
||||
// too early (and incompatibly with the user's app),
|
||||
// and because this is too early to use the Javaagent's PatchLogger
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
private static boolean shouldAttach() {
|
||||
if (agentIsDisabledWithProp()) {
|
||||
return false;
|
||||
}
|
||||
if (agentIsDisabledWithEnvVar()) {
|
||||
return false;
|
||||
}
|
||||
if (agentIsAttached()) {
|
||||
return false;
|
||||
}
|
||||
if (mainMethodCheckIsEnabled() && !isMainThread()) {
|
||||
printError(
|
||||
"Agent is not attached because runtime attachment was not requested from main thread.");
|
||||
return false;
|
||||
}
|
||||
if (mainMethodCheckIsEnabled() && !isMainMethod()) {
|
||||
printError(
|
||||
"Agent is not attached because runtime attachment was not requested from main method.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean agentIsDisabledWithProp() {
|
||||
String agentEnabledPropValue = System.getProperty(AGENT_ENABLED_PROPERTY);
|
||||
return "false".equalsIgnoreCase(agentEnabledPropValue);
|
||||
}
|
||||
|
||||
private static boolean agentIsDisabledWithEnvVar() {
|
||||
String agentEnabledEnvVarValue = System.getenv(AGENT_ENABLED_ENV_VAR);
|
||||
return "false".equals(agentEnabledEnvVarValue);
|
||||
}
|
||||
|
||||
private static boolean agentIsAttached() {
|
||||
try {
|
||||
Class.forName("io.opentelemetry.javaagent.OpenTelemetryAgent", false, null);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean mainMethodCheckIsEnabled() {
|
||||
String mainThreadCheck = System.getProperty(MAIN_METHOD_CHECK_PROP);
|
||||
return !"false".equals(mainThreadCheck);
|
||||
}
|
||||
|
||||
private static boolean isMainThread() {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
return "main".equals(currentThread.getName());
|
||||
}
|
||||
|
||||
static boolean isMainMethod() {
|
||||
StackTraceElement bottomOfStack = findBottomOfStack(Thread.currentThread());
|
||||
String methodName = bottomOfStack.getMethodName();
|
||||
return "main".equals(methodName);
|
||||
}
|
||||
|
||||
private static StackTraceElement findBottomOfStack(Thread thread) {
|
||||
StackTraceElement[] stackTrace = thread.getStackTrace();
|
||||
return stackTrace[stackTrace.length - 1];
|
||||
}
|
||||
|
||||
private static String getPid() {
|
||||
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@ plugins {
|
|||
id("otel.publish-conventions")
|
||||
}
|
||||
|
||||
description = "Utility to attach OpenTelemetry Java Instrumentation agent from classpath"
|
||||
description = "To runtime attach the OpenTelemetry Java Instrumentation agent"
|
||||
|
||||
val agent: Configuration by configurations.creating
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent:1.6.0")
|
||||
implementation("net.bytebuddy:byte-buddy-agent:1.11.18")
|
||||
implementation(project(":runtime-attach-core"))
|
||||
agent("io.opentelemetry.javaagent:opentelemetry-javaagent:1.15.0")
|
||||
|
||||
// Used by byte-buddy but not brought in as a transitive dependency.
|
||||
compileOnly("com.google.code.findbugs:annotations")
|
||||
|
|
@ -23,3 +25,13 @@ tasks.test {
|
|||
useJUnitPlatform()
|
||||
setForkEvery(1) // One JVM by test class to avoid a test class launching a runtime attachment influences the behavior of another test class
|
||||
}
|
||||
|
||||
tasks {
|
||||
jar {
|
||||
inputs.files(agent)
|
||||
from({
|
||||
agent.singleFile
|
||||
})
|
||||
rename { "otel-agent.jar" }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,108 +5,18 @@
|
|||
|
||||
package io.opentelemetry.contrib.attach;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
|
||||
/** This class allows you to attach the OpenTelemetry Java agent at runtime. */
|
||||
public final class RuntimeAttach {
|
||||
|
||||
private static final String AGENT_ENABLED_PROPERTY = "otel.javaagent.enabled";
|
||||
private static final String AGENT_ENABLED_ENV_VAR = "OTEL_JAVAAGENT_ENABLED";
|
||||
static final String MAIN_METHOD_CHECK_PROP =
|
||||
"otel.javaagent.testing.runtime-attach.main-method-check";
|
||||
|
||||
/**
|
||||
* Attach the OpenTelemetry Java agent to the current JVM. The attachment must be requested at the
|
||||
* beginning of the main method.
|
||||
*/
|
||||
public static void attachJavaagentToCurrentJVM() {
|
||||
if (!shouldAttach()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File javaagentFile = AgentFileProvider.getAgentFile();
|
||||
ByteBuddyAgent.attach(javaagentFile, getPid());
|
||||
CoreRuntimeAttach distroRuntimeAttach = new CoreRuntimeAttach("/otel-agent.jar");
|
||||
|
||||
if (!agentIsAttached()) {
|
||||
printError("Agent was not attached. An unexpected issue has happened.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SystemOut")
|
||||
private static void printError(String message) {
|
||||
// not using java.util.logging in order to avoid initializing the global LogManager
|
||||
// too early (and incompatibly with the user's app),
|
||||
// and because this is too early to use the Javaagent's PatchLogger
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
private static boolean shouldAttach() {
|
||||
if (agentIsDisabledWithProp()) {
|
||||
return false;
|
||||
}
|
||||
if (agentIsDisabledWithEnvVar()) {
|
||||
return false;
|
||||
}
|
||||
if (agentIsAttached()) {
|
||||
return false;
|
||||
}
|
||||
if (mainMethodCheckIsEnabled() && !isMainThread()) {
|
||||
printError(
|
||||
"Agent is not attached because runtime attachment was not requested from main thread.");
|
||||
return false;
|
||||
}
|
||||
if (mainMethodCheckIsEnabled() && !isMainMethod()) {
|
||||
printError(
|
||||
"Agent is not attached because runtime attachment was not requested from main method.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean agentIsDisabledWithProp() {
|
||||
String agentEnabledPropValue = System.getProperty(AGENT_ENABLED_PROPERTY);
|
||||
return "false".equalsIgnoreCase(agentEnabledPropValue);
|
||||
}
|
||||
|
||||
private static boolean agentIsDisabledWithEnvVar() {
|
||||
String agentEnabledEnvVarValue = System.getenv(AGENT_ENABLED_ENV_VAR);
|
||||
return "false".equals(agentEnabledEnvVarValue);
|
||||
}
|
||||
|
||||
private static boolean agentIsAttached() {
|
||||
try {
|
||||
Class.forName("io.opentelemetry.javaagent.OpenTelemetryAgent", false, null);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean mainMethodCheckIsEnabled() {
|
||||
String mainThreadCheck = System.getProperty(MAIN_METHOD_CHECK_PROP);
|
||||
return !"false".equals(mainThreadCheck);
|
||||
}
|
||||
|
||||
private static boolean isMainThread() {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
return "main".equals(currentThread.getName());
|
||||
}
|
||||
|
||||
static boolean isMainMethod() {
|
||||
StackTraceElement bottomOfStack = findBottomOfStack(Thread.currentThread());
|
||||
String methodName = bottomOfStack.getMethodName();
|
||||
return "main".equals(methodName);
|
||||
}
|
||||
|
||||
private static StackTraceElement findBottomOfStack(Thread thread) {
|
||||
StackTraceElement[] stackTrace = thread.getStackTrace();
|
||||
return stackTrace[stackTrace.length - 1];
|
||||
}
|
||||
|
||||
private static String getPid() {
|
||||
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||
distroRuntimeAttach.attachJavaagentToCurrentJVM();
|
||||
}
|
||||
|
||||
private RuntimeAttach() {}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public class AbstractAttachmentTest {
|
|||
|
||||
@BeforeAll
|
||||
static void disableMainThreadCheck() {
|
||||
System.setProperty(RuntimeAttach.MAIN_METHOD_CHECK_PROP, "false");
|
||||
System.setProperty(CoreRuntimeAttach.MAIN_METHOD_CHECK_PROP, "false");
|
||||
}
|
||||
|
||||
boolean isAttached() {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ include(":micrometer-meter-provider")
|
|||
include(":jmx-metrics")
|
||||
include(":maven-extension")
|
||||
include(":runtime-attach")
|
||||
include(":runtime-attach-core")
|
||||
include(":samplers")
|
||||
include(":static-instrumenter:agent-instrumenter")
|
||||
include(":static-instrumenter:gradle-plugin")
|
||||
|
|
|
|||
Loading…
Reference in New Issue