-
Notifications
You must be signed in to change notification settings - Fork 870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First POC for external extension loading #2774
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any thoughts on class loader isolation, so extensions can bring their own version of grpc/netty and not cause conflicts? or should we table that aspect for now
* This classloader is used to load arbitrary extensions for Otel Java instrumentation agent. Such | ||
* extensions may include SDK components (exporters or propagators) and additional instrumentations. | ||
* They have to be isolated and shaded to reduce interference with the user application and to make | ||
* it compatible with shaded SDK used by the agent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* it compatible with shaded SDK used by the agent. | |
* it compatible with shaded API used by the agent. |
rule( | ||
"#io.opentelemetry.extension.aws", | ||
"#io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forget why this needs to be shaded..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the x-ray propagator - I think it's to be able to inject into user classloader for use from instrumentation, even if they have their own (potentially incompatible) one. Since extension instrumentation may use the propagator it seems appropriate to shade here too.
// TODO add support for system properties | ||
URL extension = parseLocation(System.getenv("OTEL_JAVAAGENT_EXTENSIONS")); | ||
if (extension != null) { | ||
return new ExtensionClassLoader(extension, parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we only handle OTEL_JAVAAGENT_EXTENSIONS pointing to a directory, and use all *.jar
in that directory? we will want that option I think for easy dropping in of multiple extensions, and may be reasonable to only support that option (simpler doc story not to have multiple meanings of OTEL_JAVAAGENT_EXTENSIONS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would support both single file and a directory.
|
||
public ExtensionClassLoader(URL url, ClassLoader parent) { | ||
super(new URL[] {url}, parent); | ||
this.manifest = getManifest(url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I remember this, I'm not sure how to generalize this to more than one jar file under the constraint of using URLClassLoader
maybe we can pass URLs to URLClassLoader that have custom InputStreams that do the shading? If that can work somehow (big if) then URLClassLoader will take care of the class/package definitions for us and we don't need the manifest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... in fact we can do this, I think... I will give it a try.
What isolation of what from what are you thinking about? :) One extension jar from another? Extensions from AgentClassLoader? |
these are good questions 😁, ideally all of the above(?), but don't want to over-engineer it either |
Extensions are already loaded from a separate classloader, which is a child of AgentClassLoader. Meaning nobody should see their classes. Extensions themselves should have access to agent classes, thus AgentClassLoader as a parent. This current state + possible separating extensions from each other - is it Ok or do we want more? |
9104930
to
27f17cd
Compare
inputs.files(layout.files(shadowTask)) | ||
|
||
doFirst { | ||
jvmArgs("-Dio.opentelemetry.smoketest.agent.shadowJar.path=/Users/nsalnikovtarnovski/Documents/workspace/opentelemetry-java-instrumentation/javaagent/build/libs/opentelemetry-javaagent-1.2.0-SNAPSHOT-all.jar") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably need to pass some other javaagent path here 😄
@@ -34,21 +34,27 @@ private static synchronized void startAgent(Instrumentation inst, URL bootstrapU | |||
throws Exception { | |||
if (AGENT_CLASSLOADER == null) { | |||
ClassLoader agentClassLoader = createAgentClassLoader("inst", bootstrapUrl); | |||
AGENT_CLASSLOADER = agentClassLoader; | |||
|
|||
Class<?> agentInstallerClass = | |||
agentClassLoader.loadClass("io.opentelemetry.javaagent.tooling.AgentInstaller"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If agentClassLoader
is an ExtensionClassLoader
then won't we put AgentInstaller
in the same CL as extensions? And won't it apply shading transformations on all agent classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because ExtensionClassLoader
first delegates to parent, as usual. And its parent is the old AgentClassLoader
|
||
@Override | ||
public InputStream getResourceAsStream(String name) { | ||
InputStream originalStream = super.getResourceAsStream(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing if (originalStream == null)
Please start reading from
ExtensionClassLoader
.This is the first attempt to address #2442. Manual testing confirmed that this works.