Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fossa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,9 @@ targets:
- type: gradle
path: ./
target: ':instrumentation:servlet:servlet-3.0:javaagent'
- type: gradle
path: ./
target: ':instrumentation:servlet:servlet-3.0:library'
- type: gradle
path: ./
target: ':instrumentation:servlet:servlet-5.0:javaagent'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.FILTER_MAPPING_RESOLVER_FACTORY;
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.FILTER_MAPPING_RESOLVER;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
Expand All @@ -20,7 +20,6 @@ public static void filterInit(
if (filterConfig == null) {
return;
}
FILTER_MAPPING_RESOLVER_FACTORY.set(
filter, new Servlet3FilterMappingResolverFactory(filterConfig));
FILTER_MAPPING_RESOLVER.set(filter, new Servlet3FilterMappingResolverFactory(filterConfig));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.SERVLET_MAPPING_RESOLVER_FACTORY;
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.SERVLET_MAPPING_RESOLVER;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
Expand All @@ -20,7 +20,6 @@ public static void servletInit(
if (servletConfig == null) {
return;
}
SERVLET_MAPPING_RESOLVER_FACTORY.set(
servlet, new Servlet3MappingResolverFactory(servletConfig));
SERVLET_MAPPING_RESOLVER.set(servlet, new Servlet3MappingResolverFactory(servletConfig));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@
public final class Servlet3Singletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.servlet-3.0";

public static final VirtualField<Servlet, MappingResolver.Factory>
SERVLET_MAPPING_RESOLVER_FACTORY =
VirtualField.find(Servlet.class, MappingResolver.Factory.class);

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.


private static final Instrumenter<
ServletRequestContext<HttpServletRequest>, ServletResponseContext<HttpServletResponse>>
INSTRUMENTER =
Expand All @@ -41,9 +33,9 @@ public final class Servlet3Singletons {
private static final ServletHelper<HttpServletRequest, HttpServletResponse> HELPER =
new ServletHelper<>(INSTRUMENTER, Servlet3Accessor.INSTANCE);

private static final VirtualField<Servlet, MappingResolver.Factory> SERVLET_MAPPING_RESOLVER =
public static final VirtualField<Servlet, MappingResolver.Factory> SERVLET_MAPPING_RESOLVER =
VirtualField.find(Servlet.class, MappingResolver.Factory.class);
private static final VirtualField<Filter, MappingResolver.Factory> FILTER_MAPPING_RESOLVER =
public static final VirtualField<Filter, MappingResolver.Factory> FILTER_MAPPING_RESOLVER =
VirtualField.find(Filter.class, MappingResolver.Factory.class);

private static final Instrumenter<ClassAndMethod, Void> RESPONSE_INSTRUMENTER =
Expand Down
21 changes: 21 additions & 0 deletions instrumentation/servlet/servlet-3.0/library/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
id("otel.library-instrumentation")
}

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!


testLibrary("org.eclipse.jetty:jetty-server:8.0.0.v20110901")
testLibrary("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901")
testLibrary("org.apache.tomcat.embed:tomcat-embed-core:8.0.41")
testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.41")
}

tasks {
withType<Test>().configureEach {
// required on jdk17+ to allow tomcat to shutdown properly.
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.servlet.v3_0;

import static io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3Singletons.FILTER_MAPPING_RESOLVER;

import io.opentelemetry.instrumentation.servlet.v3_0.copied.CallDepth;
import io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3FilterMappingResolverFactory;
import io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3RequestAdviceScope;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* OpenTelemetry Library instrumentation for Java Servlet based applications that can't use a Java
* Agent. Due to inherit limitations in the servlet filter API, instrumenting at the filter level
* will miss anything that happens earlier in the filter stack or problems handled directly by the
* app server. For this reason, Java Agent instrumentation is preferred when possible.
*/
@WebFilter("/*")
public class OpenTelemetryServletFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) {
FILTER_MAPPING_RESOLVER.set(this, new Servlet3FilterMappingResolverFactory(filterConfig));
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Only HttpServlets are supported.
if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) {
chain.doFilter(request, response);
return;
}

HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

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.

try {
chain.doFilter(
new OtelHttpServletRequest(httpRequest), new OtelHttpServletResponse(httpResponse));
} catch (Throwable e) {
throwable = e;
throw e;
} finally {
adviceScope.exit(throwable, httpRequest, httpResponse);
}
}

@Override
public void destroy() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.servlet.v3_0;

import static io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3Singletons.helper;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/// Delegates all methods except [#start(Runnable) which wraps the [Runnable].
public class OtelAsyncContext implements AsyncContext {
private final AsyncContext delegate;

public OtelAsyncContext(AsyncContext delegate) {
this.delegate = delegate;
}

@Override
public ServletRequest getRequest() {
return delegate.getRequest();
}

@Override
public ServletResponse getResponse() {
return delegate.getResponse();
}

@Override
public boolean hasOriginalRequestAndResponse() {
return delegate.hasOriginalRequestAndResponse();
}

@Override
public void dispatch() {
delegate.dispatch();
}

@Override
public void dispatch(String path) {
delegate.dispatch(path);
}

@Override
public void dispatch(ServletContext context, String path) {
delegate.dispatch(context, path);
}

@Override
public void complete() {
delegate.complete();
}

@Override
public void start(Runnable run) {
delegate.start(helper().wrapAsyncRunnable(run));
}

@Override
public void addListener(AsyncListener listener) {
delegate.addListener(listener);
}

@Override
public void addListener(
AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) {
delegate.addListener(listener, servletRequest, servletResponse);
}

@Override
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException {
return delegate.createListener(clazz);
}

@Override
public void setTimeout(long timeout) {
delegate.setTimeout(timeout);
}

@Override
public long getTimeout() {
return delegate.getTimeout();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.servlet.v3_0;

import static io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3Singletons.helper;

import io.opentelemetry.context.Context;
import javax.servlet.AsyncContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/// Wrapper around [HttpServletRequest] that attaches an async listener if [#startAsync()] is
/// invoked and a wrapper around [#getAsyncContext()] to capture exceptions from async [Runnable]s.
public class OtelHttpServletRequest extends HttpServletRequestWrapper {

public OtelHttpServletRequest(HttpServletRequest request) {
super(request);
}

@Override
public AsyncContext getAsyncContext() {
return new OtelAsyncContext(super.getAsyncContext());
}

@Override
public AsyncContext startAsync() {
try {
return new OtelAsyncContext(super.startAsync());
} finally {
helper().attachAsyncListener(this, Context.current());
}
}

@Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) {
try {
return new OtelAsyncContext(super.startAsync(servletRequest, servletResponse));
} finally {
helper().attachAsyncListener(this, Context.current());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.servlet.v3_0;

import io.opentelemetry.instrumentation.servlet.v3_0.copied.CallDepth;
import io.opentelemetry.instrumentation.servlet.v3_0.copied.Servlet3ResponseAdviceScope;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/// Wrapper around [HttpServletResponse].
public class OtelHttpServletResponse extends HttpServletResponseWrapper {

public OtelHttpServletResponse(HttpServletResponse response) {
super(response);
}

@Override
public void sendError(int sc, String msg) throws IOException {
Servlet3ResponseAdviceScope scope =
new Servlet3ResponseAdviceScope(
CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendError");
Throwable throwable = null;
try {
super.sendError(sc, msg);
} catch (Throwable ex) {
throwable = ex;
throw ex;
} finally {
scope.exit(throwable);
}
}

@Override
public void sendError(int sc) throws IOException {
Servlet3ResponseAdviceScope scope =
new Servlet3ResponseAdviceScope(
CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendError");
Throwable throwable = null;
try {
super.sendError(sc);
} catch (Throwable ex) {
throwable = ex;
throw ex;
} finally {
scope.exit(throwable);
}
}

@Override
public void sendRedirect(String location) throws IOException {
Servlet3ResponseAdviceScope scope =
new Servlet3ResponseAdviceScope(
CallDepth.forClass(HttpServletResponse.class), this.getClass(), "sendRedirect");
Throwable throwable = null;
try {
super.sendRedirect(location);
} catch (Throwable ex) {
throwable = ex;
throw ex;
} finally {
scope.exit(throwable);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.servlet.v3_0.copied;

import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class AgentCommonConfig {
private AgentCommonConfig() {}

private static final CommonConfig instance = new CommonConfig(AgentInstrumentationConfig.get());

public static CommonConfig get() {
return instance;
}
}
Loading
Loading