opentelemetry-java-instrumentation icon indicating copy to clipboard operation
opentelemetry-java-instrumentation copied to clipboard

Unable to launch OTel Java Agent from an uber JAR

Open rvesse opened this issue 1 year ago • 8 comments

Describe the bug

In a similar vein to #8227 I've been experimenting with whether you can package the Otel Agent JAR into an uber JAR and have that work. I'm pretty sure the answer is no this won't work, so it would be nice if this were explicitly stated that this isn't supported somewhere in the documentation

Firstly if you're attempting to package into an uber jar you need to set both the Premain-Class and Launcher-Agent-Class attributes in manifest.mf. It's Launcher-Agent-Class that actually starts the agent in an uber JAR deployment BUT if you don't have Premain-Class as well OTel baulks with the following error:

ERROR io.opentelemetry.javaagent.OpenTelemetryAgent java.lang.IllegalStateException: The agent was not installed, because the agent was found in 'example.jar', which doesn't contain a Premain-Class manifest attribute. Make sure that you haven't included the agent jar file inside of an application uber jar.

Clearly implying that the authors didn't expect this to be supported anyway.

If you do specify the Premain-Class attribute to force your way past this error you'll get the following error on startup:

OpenTelemetry Javaagent failed to start java.lang.IllegalStateException: Could not install class file transformer at net.bytebuddy.agent.builder.AgentBuilder$Default.doInstall(AgentBuilder.java:11253) at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:11155) at net.bytebuddy.agent.builder.AgentBuilder$Default$Delegator.installOn(AgentBuilder.java:12927) at io.opentelemetry.javaagent.tooling.AgentInstaller.installBytebuddyAgent(AgentInstaller.java:181) at io.opentelemetry.javaagent.tooling.AgentInstaller.installBytebuddyAgent(AgentInstaller.java:94) at io.opentelemetry.javaagent.tooling.AgentStarterImpl.start(AgentStarterImpl.java:78) at io.opentelemetry.javaagent.bootstrap.AgentInitializer.initialize(AgentInitializer.java:35) at io.opentelemetry.javaagent.OpenTelemetryAgent.startAgent(OpenTelemetryAgent.java:57) at io.opentelemetry.javaagent.OpenTelemetryAgent.agentmain(OpenTelemetryAgent.java:49) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:513) at java.instrument/sun.instrument.InstrumentationImpl.loadAgent0(Native Method) at java.instrument/sun.instrument.InstrumentationImpl.loadAgent(InstrumentationImpl.java:556) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.base/sun.launcher.LauncherHelper.lambda$getMainClassFromJar$0(LauncherHelper.java:576) at java.base/java.util.Optional.ifPresent(Optional.java:178) at java.base/sun.launcher.LauncherHelper.getMainClassFromJar(LauncherHelper.java:571) at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:778) at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:686) Caused by: java.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment at java.instrument/sun.instrument.InstrumentationImpl.addTransformer(InstrumentationImpl.java:96) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at net.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source) at net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:1032) at net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1162) at net.bytebuddy.agent.builder.$Proxy5.addTransformer(Unknown Source) at net.bytebuddy.agent.builder.AgentBuilder$Default.doInstall(AgentBuilder.java:11230) ... 25 more

Which likely is the same underlying bug raphw/byte-buddy/issues/374 mentioned in the comments thread on #8227

Steps to reproduce

Generate an über JAR with the OTel Java Agent using the toolchain of your choice, e.g. Maven Shade plugin:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <configuration>
          <shadedArtifactAttached>false</shadedArtifactAttached>
          <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
              <mainClass>my.package.MainClass</mainClass>
              <!-- https://issues.apache.org/jira/browse/LOG4J2-2537 -->
              <manifestEntries>
                <Multi-Release>true</Multi-Release>
                <Premain-Class>io.opentelemetry.javaagent.OpenTelemetryAgent</Premain-Class>
                <Launcher-Agent-Class>io.opentelemetry.javaagent.OpenTelemetryAgent</Launcher-Agent-Class>
              </manifestEntries>
            </transformer>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
          </transformers>
          <filters>
            <filter>
              <artifact>*:*</artifact>
              <excludes>
                <!-- 
                     Some jars are signed but shading breaks that. 
                     Don't include signing files. 
                -->
                <exclude>META-INF/*.SF</exclude>
                <exclude>META-INF/*.DSA</exclude>
                <exclude>META-INF/*.RSA</exclude>
              </excludes>
            </filter>
          </filters>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

Expected behavior

N/A, seems this isn't intended to work/can't work so documentation merely needs to be clearer around this

Actual behavior

Errors thrown as shown earlier in description

Javaagent or library instrumentation version

1.24.0

Environment

JDK: Temurin 17 and OpenJDK 17 OS: Mac OX X Ventura, Linux (5.15.40-linuxkit)

Additional context

No response

rvesse avatar Oct 17 '23 13:10 rvesse

hi @rvesse! is this feature what you're looking for? https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/runtime-attach

trask avatar Oct 17 '23 14:10 trask

hi @rvesse! is this feature what you're looking for? https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/runtime-attach

No, I don't think that solves the problem for Uber JARs anyway, I get a slightly different error condition but my application still crashes:

[otel.javaagent 2023-10-17 15:34:29:703 +0100] [Attach Listener] INFO io.opentelemetry.javaagent.tooling.VersionLogger - opentelemetry-javaagent - version: 0.20.1-SNAPSHOT ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...

Personally I really don't want to put the agent into an uber JAR because that's not how agents are supposed to be used. But I'd ideally like some documentation saying Don't do that because X (or equivalent) in your official documentation so I point to that to justify why we aren't doing that.

rvesse avatar Oct 17 '23 14:10 rvesse

No, I don't think that solves the problem for Uber JARs anyway

can you describe a bit more why not? runtime attach module should allows you to embed the agent in an uber jar (e.g. spring boot)

trask avatar Oct 17 '23 14:10 trask

You probably get the UnsupportedOperationException because you didn't add Can-Retransform-Classes: true manifest attribute, you should also add Can-Redefine-Classes: true. Packaging the agent into an uberjar like that will take some effort to make it work, I think it would be easier to get it running by packaging the agent as a jar file into your uberjar. In the Launcher-Agent-Class unpack that jar and use an URLClassLoader and a bit of reflection to call premain in OpenTelemetryAgent. I haven't tried it myself so can't say for sure whether this is all that is needed. If you want to package the agent into the uberjar then you have to be aware that in https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/60b65488cbad7d3c620e74c7a83fa044e77f310f/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/OpenTelemetryAgent.java#L96 the agent adds the jar it is loaded from to boot class path, this may break your application.

laurit avatar Oct 17 '23 16:10 laurit

To summarise:

  • Packaging the agent into an uber JAR is theoretically possible (if appropriate manifest.mf attributes are set) but likely a very bad idea per @laurit's comment as your entire application ends up on the bootstrap classpath which can lead to all sorts of subtle and interesting class loading problems
  • Runtime Attach is an option (but at least for my use case/deployment environment doesn't work)

Note that I'm quite happy for you guys to close this as Won't Fix or your equivalent.

My main goal in filing this issue was to have some more easily discoverable discussion of this topic to help future engineers who get asked to try and do this and start Googling and don't waste too much time.

rvesse avatar Oct 19 '23 09:10 rvesse

@trask we are trying to use the agent with quarkus Uber jar and we end up getting the same exception as stated by @rvesse. Is this the limitation of otel agent with uber jars?

alagusundarams avatar Mar 27 '24 14:03 alagusundarams

hi @alagusundarams! if Runtime Attach isn't working for you, can you open an issue in https://github.com/open-telemetry/opentelemetry-java-contrib?

trask avatar Mar 27 '24 15:03 trask

Hi @alagusundarams and @rvesse -- did you open an issue in the contrib repo? If so, can you please mention it here? Thanks!

breedx-splk avatar Jul 23 '24 23:07 breedx-splk

This has been automatically marked as stale because it has been marked as needing author feedback and has not had any activity for 7 days. It will be closed automatically if there is no response from the author within 7 additional days from this comment.

github-actions[bot] avatar Jul 30 '24 23:07 github-actions[bot]