Add doc about calling default methods from advice
MODIFY
docs/contributing/writing-instrumentation.md
docs/contributing/writing-instrumentation.md
@@ -6,7 +6,7 @@ Any time we want to add OpenTelemetry support for a new Java library, e.g., so u
of that library has tracing, we must write new instrumentation for that library. Let's
go over some terms first.
-**Manual Instrumentation**: This is logic that creates spans and enriches them with data
+**Library instrumentation**: This is logic that creates spans and enriches them with data
using library-specific monitoring APIs. For example, when instrumenting an RPC library,
the instrumentation will use some library-specific functionality to listen to events such
as the start and end of a request and will execute code to start and end spans in these
@@ -14,23 +14,23 @@ listeners. Many of these libraries will provide interception type APIs such as t
`ClientInterceptor` or servlet's `Filter`. Others will provide a Java interface whose methods
correspond to a request, and instrumentation can define an implementation which delegates
to the standard, wrapping methods with the logic to manage spans. Users will add code to their
-apps that initialize the classes provided by manual instrumentation libraries and the libraries
+apps that initialize the classes provided by library instrumentation, and the library instrumentation
can be found inside the user's app itself.
Some libraries will have no way of intercepting requests because they only expose static APIs
-and no interception hooks. For these libraries it is not possible to create manual
+and no interception hooks. For these libraries it is not possible to create library
instrumentation.
-**Auto Instrumentation**: This is logic that is similar to manual instrumentation, but instead
+**Java agent instrumentation**: This is logic that is similar to library instrumentation, but instead
of a user initializing classes themselves, a Java agent automatically initializes them during
class loading by manipulating byte code. This allows a user to develop their apps without thinking
-about instrumentation and get it "for free". Often, the auto instrumentation will generate bytecode
-that is more or less identical to what a user would have written themselves in their app.
+about instrumentation and get it "for free". Often, the agent instrumentation will generate
+bytecode that is more or less identical to what a user would have written themselves in their app.
-In addition to automatically initializing manual instrumentation, auto instrumentation can be used
-for libraries where manual instrumentation is not possible, such as `URLConnection`, because it can
-intercept even the JDK's classes. Such libraries will not have manual instrumentation but will have
-auto instrumentation.
+In addition to automatically initializing library instrumentation, agent instrumentation can be used
+for libraries where library instrumentation is not possible, such as `URLConnection`, because it can
+intercept even the JDK's classes. Such libraries will not have library instrumentation but will have
+agent instrumentation.
## Folder Structure
@@ -42,8 +42,8 @@ instrumented library and the oldest version being targeted. Ideally an old versi
targeted in a way that the instrumentation applies to a large range of versions, but this may be
restricted by the interception APIs provided by the library.
-Within the subfolder, create three folders `library` (skip if manual instrumentation is not possible),
-`auto`, and `testing`.
+Within the subfolder, create three folders `library` (skip if library instrumentation is not possible),
+`javaagent`, and `testing`.
For example, if we are targeting an RPC framework `yarpc` at version `1.0` we would have a tree like
@@ -68,7 +68,7 @@ include 'instrumentation:yarpc-1.0:library'
include 'instrumentation:yarpc-1.0:testing'
```
-## Writing manual instrumentation
+## Writing library instrumentation
Begin by writing the instrumentation for the library in `library`. This generally involves defining a
`Tracer` and using the typed tracers in our `instrumentation-common` library to create and annotate
@@ -80,9 +80,9 @@ configure build tooling for the library.
## Writing unit tests
Once the instrumentation is completed, we add unit tests to the `testing` module. Tests will
-generally apply to both manual and auto instrumentation, with the only difference being how a client
-or server is initialized. In a manual test, there will be code calling into the instrumentation API
-while in an auto test, it will generally just use the library's API as is. Create unit tests in an
+generally apply to both library and agent instrumentation, with the only difference being how a client
+or server is initialized. In a library test, there will be code calling into the instrumentation API,
+while in an agent test, it will generally just use the underlying library's API as is. Create unit tests in an
abstract class with an abstract method that returns an instrumented object like a client. The class
should itself extend from `InstrumentationSpecification` to be recognized by Spock and include helper
methods for assertions.
@@ -92,23 +92,35 @@ After writing a test or two, go back to the `library` package, make sure it has
the method to initialize the client using the library's mechanism to register interceptors, perhaps
a method like `registerInterceptor` or wrapping the result of a library factory when delegating. The
test should implement the `InstrumentationTestRunner` trait for common setup logic. If the tests
-pass, manual instrumentation is working OK.
+pass, library instrumentation is working OK.
-## Writing auto instrumentation
+## Writing Java agent instrumentation
-Now that we have working instrumentation, we can implement auto instrumentation so users of the agent
+Now that we have working instrumentation, we can implement agent instrumentation so users of the agent
do not have to modify their apps to use it. Make sure the `javaagent` submodule has a dependency on the
-`library` submodule and a test dependency on the `testing` submodule. Auto instrumentation defines
+`library` submodule and a test dependency on the `testing` submodule. Agent instrumentation defines
classes to match against to generate bytecode for. You will often match against the class you used
-in the unit test for manual instrumentation, for example the builder of a client. And then you could
-match against the method that creates the builder, for example its constructor. Auto instrumentation
+in the unit test for library instrumentation, for example the builder of a client. And then you could
+match against the method that creates the builder, for example its constructor. Agent instrumentation
can inject byte code to be run after the constructor returns, which would invoke e.g.,
`registerInterceptor` and initialize the instrumentation. Often, the code inside the byte code
decorator will be identical to the one in the unit test you wrote above - the agent does the work for
initializing the instrumentation library, so a user doesn't have to.
-With that written, let's add tests for the auto instrumentation. We basically want to ensure that
+With that written, let's add tests for the agent instrumentation. We basically want to ensure that
the instrumentation works without the user knowing about the instrumentation. Add a test that extends
the base class you wrote earlier, but in this, create a client using none of the APIs in our project,
only the ones offered by the library. Implement the `AgentTestRunner` trait for common setup logic,
-and try running. All of the tests should pass for auto instrumentation too.
+and try running. All the tests should pass for agent instrumentation too.
+
+### Java agent instrumentation gotchas
+
+#### Calling Java 8 default methods from advice
+
+If you are instrumenting a pre-Java 8 library, then inlining Java 8 default method calls into that
+library will result in a `java.lang.VerifyError` at runtime, since Java 8 default method invocations
+are not legal in Java 7 (and prior) bytecode.
+
+Because OpenTelemetry API has many common default methods (e.g. `Span.current()`),
+the `javaagent-api` artifact has a class `Java8BytecodeBridge` which provides static methods
+for accessing these default methods from advice.

Add doc about calling default methods from advice (#1737)