Skip to content

Conversation

@tylerbenson
Copy link
Member

Includes handling for attaching all the async stuff, but can't support everything the java agent does.

I could use help figuring out the right way to shadow all the necessary dependencies. I'm a bit rusty with our shadow usage. Depending on how aggressive we want to be, the dependency could be reversed so that :servlet-3.0:javaagent depends on this new module, but that's more aggressive. Perhaps shadow could prune unused classes (with minimize()) instead.

Once we get the modules figured out, I can work on adding some unit tests.

If we like this approach, it could probably be copied and modified pretty easily to support servlet 5, but I'm not familiar with that instrumentation.

Includes handling for attaching all the async stuff, but can't support everything the java agent does.

I could use help figuring out the right way to shadow all the necessary dependencies.  I'm a bit rusty with our shadow usage.
Depending on how aggressive we want to be, the dependency could be reversed so that `:servlet-3.0:javaagent` depends on this new module, but that's more aggressive.  Perhaps shadow could prune unused classes (with `minimize()`) instead.

Once we get the modules figured out, I can work on adding some unit tests.

If we like this approach, it could probably be copied and modified pretty easily to support servlet 5, but I'm not familiar with that instrumentation.
@laurit
Copy link
Contributor

laurit commented Oct 31, 2025

I could use help figuring out the right way to shadow all the necessary dependencies.

You shouldn't shadow anything. If you need to share code you can extract common code between the javaagent and library instrumentation into separate module or make javaagent module depend on the library module if that make sense. In your place I would focus on building a functioning instrumentation along with tests and explore options for sharing code later. It is quite possible that the agent code does things that don't make sense for the library instrumentation which could limit the amount of code that can be shared between them.

@@ -0,0 +1,43 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.helper;
Copy link
Member Author

@tylerbenson tylerbenson Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@laurit I'm not sure how to handle the dependency on classes like io.opentelemetry.javaagent.bootstrap.http.HttpServerResponseMutator from Servlet3Accessor referenced by Servlet3Singletons that seem to expect to be on the bootstrap classpath and are part of the javaagent-extension-api.

More and more it seems like there will be relatively little code that can be shared between the javaagent and library instrumentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That one shouldn't be too hard to handle. You can easily remove that interface from Servlet3Accessor and have the usage in advice create a HttpServerResponseMutator that calls the appendHeader method in Servlet3Accessor.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I ended up just copying over all the classes that were referenced. Let me know what/if you want me to prune anything significant.

Copied over from :servlet3.0:javaagent.
Had to disable/remove some test cases/scenarios that didn't make sense or just weren't working.
Still not using proper gradle project structure though.
@Override
public void run() {
try {
try (Scope scope = context.makeCurrent()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like an oversight that this isn't already here.


public static final VirtualField<Filter, MappingResolver.Factory>
FILTER_MAPPING_RESOLVER_FACTORY =
VirtualField.find(Filter.class, MappingResolver.Factory.class);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change isn't necessary, but it seems odd that these are duplicated. I combined them, but if it's intentional I can revert this change.

Throwable throwable = null;
Servlet3RequestAdviceScope adviceScope =
new Servlet3RequestAdviceScope(
CallDepth.forClass(OpenTelemetryServletFilter.class), httpRequest, httpResponse, this);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to keep the instrumentation as similar as possible to the javaagent instrumentation.

@tylerbenson tylerbenson force-pushed the tyler/servlet-filter-inst-lib branch from 4efcd9e to 2e0d691 Compare November 6, 2025 18:27

dependencies {
library("javax.servlet:javax.servlet-api:3.0.1")
library("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing some latestDepTest failures on this. How should I be declaring this dependency properly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use compileOnly for servlet-api and implementation for opentelemetry-semconv-incubating (actually you shouldn't add that dependency at all, but more on that later). library dependencies have their version bumped to latest available version when running latest dep tests, you don't want that for either of these dependencies.
In library instrumentations we don't depend on incubating semconv but copy the keys we need from there. For example

// copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_SYSTEM = AttributeKey.stringKey("db.system");
private static final AttributeKey<String> DB_STATEMENT = AttributeKey.stringKey("db.statement");
We don't use incubating semconv dependency in library instrumentations (tests are fine) because the incubating semconv are not guaranteed to be backwards compatible. Given multiple library instrumentations that depend on different versions of incubating semconv it could be that no version of incubating semconv works for all the libraries.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the help. Build passes now!

@tylerbenson tylerbenson marked this pull request as ready for review November 6, 2025 19:23
@tylerbenson tylerbenson requested a review from a team as a code owner November 6, 2025 19:23
tylerbenson and others added 3 commits November 7, 2025 10:50
Incubating keys are internalized to avoid a dependency on a moving target.
@otelbot-java-instrumentation
Copy link
Contributor

🔧 The result from spotlessApply was committed to the PR branch.

@tylerbenson tylerbenson force-pushed the tyler/servlet-filter-inst-lib branch from c822c44 to 6ce59fa Compare November 7, 2025 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants