What am I doing ?
trying to create a Otel Extension and everything is working fine until the moment I use external libraries.
Relevant context information:
I am using
Maven
instead ofGradle
.I don't have any
premain
method ormain
method. I am pretty newbie in Javaagent and extensions.I am using only
InstrumentationModule
andTypeInstrumentation
from Otel Java Instrumentation.I can inject new helper classes overriding
getAdditionalHelperClassNames
. It works fine.
Issue:
- Even being able to inject new helper classes I don't know how to inject external libraries like
com.jayway.jsonpath
. For that reason I am having a lot ofClassNotFoundException
.
Request:
- Can you help me inject an external library into classpath to use on application or show me how to use external libraries and don't have ClassNotFoundException?
- Muzzle does that but it only works with Gradle.
Error:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause
java.lang.ClassNotFoundException: com.fasterxml.jackson.dataformat.yaml.YAMLFactory
at java.base/java.URLClassLoader.findClass(URLClassLoader.java:445) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593) ~[na:na]
at .springframework.boot.loader.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104) ~[test-rest-api.jar:na]
at .springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[test-rest-api.jar:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
at .springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:92) ~[spring-web-6.1.3.jar!/:6.1.3]
at .apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.18.jar!/:na]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Code:
Usefull links:
- Otel Javaagent v1.33.6
- Instrument class example
- Module class example
- Instrumentation documentation(Most specifically where talk about Gradle and Muzzle
How to run the extension
After build the extension artifact:
- You need to download the Otel Javaagent version 1.33.6
- You need 1 application to inject the extension (I am using a simple springboot application with 2 endpoints)
- Run application with java command. Use the following flags: -javaagent -Dotel.javaagent.extensions
Example:
java -javaagent:/path/to/opentelemetry/javaagent/opentelemetry-javaagent.jar
-Dotel.javaagent.extensions=/path/to/your/extension/otel-extension-1.0.0-SNAPSHOT.jar
-jar test-rest-api.jar \
Maven:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="; xmlns=".0.0"
xsi:schemaLocation=".0.0 .0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.extension</groupId>
<artifactId>otel-extension</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>otel-extension</name>
<properties>
<java.version>17</java.version>
<!-- JSON VERSIONS -->
<jackson-dataformat-yaml.version>2.15.2</jackson-dataformat-yaml.version>
<jackson-datatype-jsr310.version>2.9.8</jackson-datatype-jsr310.version>
</properties>
<dependencies>
<!-- JAKARTA SERVLET -->
<dependency>
<groupId>.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>10.1.18</version>
<scope>compile</scope>
</dependency>
<!-- SPRINGFRAMEWORK -->
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.3</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- LOMBOK -->
<dependency>
<groupId>.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>provided</scope>
</dependency>
<!-- GOOGLE -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.1.1</version>
</dependency>
<!-- OPENTELEMETRY -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure-spi</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-javaagent-extension-api</artifactId>
<version>1.24.0-alpha</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>1.26.0-alpha</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-muzzle</artifactId>
<version>2.13.3-alpha</version>
<scope>runtime</scope>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson-datatype-jsr310.version}</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
InstrumentationModule:
package com.example;
import java.util.Collections;
import java.util.List;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public final class ServletInstrumentationModule extends InstrumentationModule {
public ServletInstrumentationModule() {
super("otel-extension");
}
@Override
public int order() {
return 1;
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return AgentElementMatchers.hasClassesNamed("jakarta.servlet.http.HttpServlet");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new HttpServletRequestInstrumentation());
}
@Override
public boolean isHelperClass(String className) {
return true;
}
@Override
public List<String> getAdditionalHelperClassNames() {
return List.of(
"com.example.HttpServletRequestWrapper",
"com.example.HttpServletRequestWrapper$CachedServletInputStream",
"com.example.OtelExtensionFilter",
"com.example.HttpMetricExtractorInjector",
"com.example.MetricMap",
"com.example.MetricMap$EnumConverter"
);
}
}
TypeInstrumentation
package com.example;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
public class HttpServletRequestInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return AgentElementMatchers.hasSuperType(
namedOneOf("jakarta.servlet.Filter"));
}
@Override
public void transform(TypeTransformer typeTransformer) {
System.out.println("Transform");
typeTransformer.applyAdviceToMethod(
namedOneOf("doFilter")
.and(ElementMatchers.takesArgument(
0, ElementMatchers.named("jakarta.servlet.ServletRequest")))
.and(ElementMatchers.takesArgument(
1, ElementMatchers.named("jakarta.servlet.ServletResponse")))
.and(ElementMatchers.takesArgument(
2, ElementMatchers.named("jakarta.servlet.FilterChain"))),
this.getClass().getName() + "$FilterAdvice");
}
@SuppressWarnings("unused")
public static class FilterAdvice {
@Advice.OnMethodEnter()
public static void onGetInputStream(
@Advice.Argument(value = 0, typing = Typing.DYNAMIC, readOnly = false) ServletRequest request,
@Advice.Argument(value = 1) ServletResponse response,
@Advice.Argument(value = 2) FilterChain filterChain
) throws IOException, ServletException {
// TODO: Add here any class that doesn't exist inside the target springboot application
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
request = new HttpServletRequestWrapper(httpServletRequest);
System.out.println(new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
}
}
}
}
}
What am I doing ?
trying to create a Otel Extension and everything is working fine until the moment I use external libraries.
Relevant context information:
I am using
Maven
instead ofGradle
.I don't have any
premain
method ormain
method. I am pretty newbie in Javaagent and extensions.I am using only
InstrumentationModule
andTypeInstrumentation
from Otel Java Instrumentation.I can inject new helper classes overriding
getAdditionalHelperClassNames
. It works fine.
Issue:
- Even being able to inject new helper classes I don't know how to inject external libraries like
com.jayway.jsonpath
. For that reason I am having a lot ofClassNotFoundException
.
Request:
- Can you help me inject an external library into classpath to use on application or show me how to use external libraries and don't have ClassNotFoundException?
- Muzzle does that but it only works with Gradle.
Error:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause
java.lang.ClassNotFoundException: com.fasterxml.jackson.dataformat.yaml.YAMLFactory
at java.base/java.URLClassLoader.findClass(URLClassLoader.java:445) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593) ~[na:na]
at .springframework.boot.loader.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104) ~[test-rest-api.jar:na]
at .springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[test-rest-api.jar:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
at .springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:92) ~[spring-web-6.1.3.jar!/:6.1.3]
at .apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.18.jar!/:na]
at .apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.18.jar!/:na]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Code:
Usefull links:
- Otel Javaagent v1.33.6
- Instrument class example
- Module class example
- Instrumentation documentation(Most specifically where talk about Gradle and Muzzle
How to run the extension
After build the extension artifact:
- You need to download the Otel Javaagent version 1.33.6
- You need 1 application to inject the extension (I am using a simple springboot application with 2 endpoints)
- Run application with java command. Use the following flags: -javaagent -Dotel.javaagent.extensions
Example:
java -javaagent:/path/to/opentelemetry/javaagent/opentelemetry-javaagent.jar
-Dotel.javaagent.extensions=/path/to/your/extension/otel-extension-1.0.0-SNAPSHOT.jar
-jar test-rest-api.jar \
Maven:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3./2001/XMLSchema-instance" xmlns="http://maven.apache./POM/4.0.0"
xsi:schemaLocation="http://maven.apache./POM/4.0.0 https://maven.apache./xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.extension</groupId>
<artifactId>otel-extension</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>otel-extension</name>
<properties>
<java.version>17</java.version>
<!-- JSON VERSIONS -->
<jackson-dataformat-yaml.version>2.15.2</jackson-dataformat-yaml.version>
<jackson-datatype-jsr310.version>2.9.8</jackson-datatype-jsr310.version>
</properties>
<dependencies>
<!-- JAKARTA SERVLET -->
<dependency>
<groupId>.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>10.1.18</version>
<scope>compile</scope>
</dependency>
<!-- SPRINGFRAMEWORK -->
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.3</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- LOMBOK -->
<dependency>
<groupId>.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>provided</scope>
</dependency>
<!-- GOOGLE -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.1.1</version>
</dependency>
<!-- OPENTELEMETRY -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure-spi</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-javaagent-extension-api</artifactId>
<version>1.24.0-alpha</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>1.26.0-alpha</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-muzzle</artifactId>
<version>2.13.3-alpha</version>
<scope>runtime</scope>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson-datatype-jsr310.version}</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
InstrumentationModule:
package com.example;
import java.util.Collections;
import java.util.List;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public final class ServletInstrumentationModule extends InstrumentationModule {
public ServletInstrumentationModule() {
super("otel-extension");
}
@Override
public int order() {
return 1;
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return AgentElementMatchers.hasClassesNamed("jakarta.servlet.http.HttpServlet");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new HttpServletRequestInstrumentation());
}
@Override
public boolean isHelperClass(String className) {
return true;
}
@Override
public List<String> getAdditionalHelperClassNames() {
return List.of(
"com.example.HttpServletRequestWrapper",
"com.example.HttpServletRequestWrapper$CachedServletInputStream",
"com.example.OtelExtensionFilter",
"com.example.HttpMetricExtractorInjector",
"com.example.MetricMap",
"com.example.MetricMap$EnumConverter"
);
}
}
TypeInstrumentation
package com.example;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
public class HttpServletRequestInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return AgentElementMatchers.hasSuperType(
namedOneOf("jakarta.servlet.Filter"));
}
@Override
public void transform(TypeTransformer typeTransformer) {
System.out.println("Transform");
typeTransformer.applyAdviceToMethod(
namedOneOf("doFilter")
.and(ElementMatchers.takesArgument(
0, ElementMatchers.named("jakarta.servlet.ServletRequest")))
.and(ElementMatchers.takesArgument(
1, ElementMatchers.named("jakarta.servlet.ServletResponse")))
.and(ElementMatchers.takesArgument(
2, ElementMatchers.named("jakarta.servlet.FilterChain"))),
this.getClass().getName() + "$FilterAdvice");
}
@SuppressWarnings("unused")
public static class FilterAdvice {
@Advice.OnMethodEnter()
public static void onGetInputStream(
@Advice.Argument(value = 0, typing = Typing.DYNAMIC, readOnly = false) ServletRequest request,
@Advice.Argument(value = 1) ServletResponse response,
@Advice.Argument(value = 2) FilterChain filterChain
) throws IOException, ServletException {
// TODO: Add here any class that doesn't exist inside the target springboot application
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
request = new HttpServletRequestWrapper(httpServletRequest);
System.out.println(new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
}
}
}
}
}
Share
Improve this question
edited Mar 13 at 4:55
kriegaex
67.8k15 gold badges120 silver badges224 bronze badges
asked Mar 11 at 14:38
UnovaUnova
112 bronze badges
4
|
1 Answer
Reset to default 0With a few modifications to your code (e.g. adding a missing import and actually using classes from json-path
in the extension code), I can reproduce the problem. The easiest way to fix it is to simply shade the necessary dependency classes into the extension, which solves the problem during build time:
<build>
<plugins>
<plugin>
<groupId>.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.2</version>
<executions>
<execution>
<id>shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>com.jayway.jsonpath:json-path</include>
</includes>
</artifactSet>
<!-- Make artifact as small as possible (optional) -->
<minimizeJar>true</minimizeJar>
<filters>
<filter>
<artifact>com.jayway.jsonpath:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/maven/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
This works for me, also testing with a Spring Boot app. If for some reason auto-detection of used classes does not work in any given situation, simply do not use minimizeJar
(it defaults to false
).
BTW, writing one or two Muzzle Maven Plugins mirroring the behaviour of the Gradle plugins and contributing them to the OpenTelemetry project would be a more generic and sustainable solution for the benefit of all users.
Update: I thought it would be a good idea to create OTel issue #13611 to improve the situation upstream.
@Advice
. Please also mnetion how you start the agent, i.e. via-javaagent
CLI argument or via hot-attachment, either manually or through some OTEL machanism? Sorry for the last question, but I never used OTEL before, I just know a few things about java agents. – kriegaex Commented Mar 12 at 2:48