The javaagent can be logically divided into several parts, based on the class loader that contains particular classes (and resources) in the runtime:
The only class that is loaded by the system class loader is the
io.opentelemetry.javaagent.OpenTelemetryAgent
class. This is the main class of the javaagent, it
implements the
Java instrumentation agent specification.
This class is loaded during application startup by the system class loader. Its sole
responsibility is to push the agent's classes into JVM's bootstrap class loader and immediately
delegate to the io.opentelemetry.javaagent.bootstrap.AgentInitializer
class, living in the
bootstrap class loader.
Inside the javaagent jar, this class is located in the io/opentelemetry/javaagent/
directory.
The bootstrap class loader contains several modules:
javaagent-bootstrap
module:
it contains classes that continue the initialization work started by OpenTelemetryAgent
, as well
as some internal javaagent classes and interfaces that must be globally available to the whole
application. This module is internal and its APIs are considered unstable.instrumentation-api
and instrumentation-api-semconv
modules:
these modules contain the Instrumenter API and other related
utilities. Because they are used by almost all instrumentations, they must be globally available
to all classloaders running within the instrumented application. The classes located in these
modules are used by both javaagent and library instrumentations - they all must be usable even
without the javaagent present.instrumentation-annotations-support
module:
it contains classes that provide support for annotation-based auto-instrumentation, e.g.
the @WithSpan
annotation. This module is internal and its APIs are considered unstable.io.opentelemetry.javaagent.bootstrap
package from the javaagent-extension-api
module:
this package contains several instrumentation utilities that are only usable when an application
is instrumented with the javaagent; for example, the Java8BytecodeBridge
that should be used
inside advice classes.otel.javaagent-bootstrap
Gradle plugin:
these modules contain instrumentation-specific classes that must be globally available in the
bootstrap class loader. For example, classes that are used to coordinate
different InstrumentationModule
s, like the common utilities for storing Servlet context path, or
the thread local switch used to coordinate different Kafka consumer instrumentations. By
convention, all these modules are named according to this
pattern: :instrumentation:...:bootstrap
.Inside the javaagent jar, these classes are all located under the io/opentelemetry/javaagent/
directory. Aside from the javaagent-specific javaagent-bootstrap
and javaagent-extension-api
modules, all other modules are relocated and placed under the io/opentelemetry/javaagent/shaded/
directory. This is done to avoid conflicts with the application code, which may contain different
versions of some of our APIs (opentelemetry-api
, instrumentation-api
).
The agent class loader contains almost everything else not mentioned before, including:
javaagent-tooling
module:
this module picks up the initialization process started by OpenTelemetryAgent
and javaagent-bootstrap
and actually finishes the work, starting up the OpenTelemetry SDK and
building and installing the ClassFileTransformer
in the JVM. The javaagent
uses ByteBuddy to configure and construct the ClassFileTransformer
.
This module is internal and its APIs are considered unstable.muzzle
module:
it contains classes that are internally used by muzzle, our safety net feature. This
module is internal and its APIs are considered unstable.io.opentelemetry.javaagent.extension
package from the javaagent-extension-api
module:
this package contains common extension points and SPIs that can be used to customize the agent
behavior.otel.javaagent-instrumentation
Gradle plugin:
these modules contain actual javaagent instrumentations. Almost all of them implement
the InstrumentationModule
, some of them include a library instrumentation as an implementation
dependency. You can read more about writing instrumentations here.
By convention, all these modules are named according to this
pattern: :instrumentation:...:javaagent
.Inside the javaagent jar, all classes and resources that are meant to be loaded by
the AgentClassLoader
are placed inside the inst/
directory. All Java class files have
the .classdata
extension (instead of just .class
) - this ensures that they will not be loaded by
general class loaders included with the application, making the javaagent internals completely
isolated from the application code.
If a javaagent instrumentation includes a library instrumentation as an implementation
dependency,
that dependency is shaded to prevent conflicts with application code (which may or may not include
the same library classes in different version).
The extension class loader(s) is used to load custom extensions, if they're used. Extensions can be
external jars (provided by the otel.javaagent.extensions
configuration property), or can be
embedded into an OpenTelemetry javaagent distribution (by adding the extension jars into
the extensions/
directory inside the javaagent jar). Each extension is loaded in isolation, in a
separate class loader - this is intended to reduce the possibility of conflicts between different
extensions. Extension jars can be compiled against unshaded versions of the OpenTelemetry APIs,
the javaagent will apply shading dynamically in the runtime, when the extension is loaded.