Skip to content

Commit 61a832f

Browse files
author
Mateusz Rzeszutek
committed
Instrument spring-web 6 & spring-webmvc 6
1 parent e87f7e3 commit 61a832f

File tree

35 files changed

+1018
-244
lines changed

35 files changed

+1018
-244
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.core;
7+
8+
import com.google.auto.service.AutoService;
9+
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
10+
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
11+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
12+
13+
@AutoService(IgnoredTypesConfigurer.class)
14+
public class SpringCoreIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
15+
16+
@Override
17+
public void configure(IgnoredTypesBuilder builder, ConfigProperties config) {
18+
// a Runnable task class that we don't need to touch
19+
builder
20+
.ignoreClass("org.springframework.util.ConcurrentLruCache$AddTask")
21+
.ignoreTaskClass("org.springframework.util.ConcurrentLruCache$AddTask");
22+
}
23+
}

instrumentation/spring/spring-web/spring-web-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/springweb/v3_1/WebApplicationContextInstrumentation.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory b
7575
Class<?> clazz =
7676
dispatcherServletClass
7777
.getClassLoader()
78-
.loadClass("org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
78+
.loadClass(
79+
"org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter");
7980
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
8081
beanDefinition.setScope(SCOPE_SINGLETON);
8182
beanDefinition.setBeanClass(clazz);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.springframework")
8+
module.set("spring-web")
9+
versions.set("[6.0.0)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
dependencies {
15+
compileOnly("org.springframework:spring-web:6.0.0")
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static java.util.Collections.singletonList;
10+
11+
import com.google.auto.service.AutoService;
12+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
14+
import java.util.List;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
17+
@AutoService(InstrumentationModule.class)
18+
public class SpringWebInstrumentationModule extends InstrumentationModule {
19+
20+
public SpringWebInstrumentationModule() {
21+
super("spring-web", "spring-web-6.0");
22+
}
23+
24+
@Override
25+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
26+
// class added in 6.0
27+
return hasClassesNamed("org.springframework.web.ErrorResponse");
28+
}
29+
30+
@Override
31+
public List<TypeInstrumentation> typeInstrumentations() {
32+
return singletonList(new WebApplicationContextInstrumentation());
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
9+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
10+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
11+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
12+
import static net.bytebuddy.matcher.ElementMatchers.named;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
14+
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SINGLETON;
15+
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
17+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
18+
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.description.type.TypeDescription;
20+
import net.bytebuddy.matcher.ElementMatcher;
21+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.beans.factory.support.GenericBeanDefinition;
24+
25+
/**
26+
* This instrumentation adds the OpenTelemetryHandlerMappingFilter definition to the spring context
27+
* When the context is created, the filter will be added to the beginning of the filter chain.
28+
*/
29+
public class WebApplicationContextInstrumentation implements TypeInstrumentation {
30+
31+
@Override
32+
public ElementMatcher<ClassLoader> classLoaderOptimization() {
33+
return hasClassesNamed(
34+
"org.springframework.context.support.AbstractApplicationContext",
35+
"org.springframework.web.context.WebApplicationContext");
36+
}
37+
38+
@Override
39+
public ElementMatcher<TypeDescription> typeMatcher() {
40+
return extendsClass(named("org.springframework.context.support.AbstractApplicationContext"))
41+
.and(implementsInterface(named("org.springframework.web.context.WebApplicationContext")));
42+
}
43+
44+
@Override
45+
public void transform(TypeTransformer transformer) {
46+
transformer.applyAdviceToMethod(
47+
isMethod()
48+
.and(named("postProcessBeanFactory"))
49+
.and(
50+
takesArgument(
51+
0,
52+
named(
53+
"org.springframework.beans.factory.config.ConfigurableListableBeanFactory"))),
54+
WebApplicationContextInstrumentation.class.getName() + "$FilterInjectingAdvice");
55+
}
56+
57+
@SuppressWarnings("unused")
58+
public static class FilterInjectingAdvice {
59+
60+
@Advice.OnMethodEnter(suppress = Throwable.class)
61+
public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory beanFactory) {
62+
if (beanFactory instanceof BeanDefinitionRegistry
63+
&& !beanFactory.containsBean("otelAutoDispatcherFilter")) {
64+
try {
65+
// Firstly check whether DispatcherServlet is present. We need to load an instrumented
66+
// class from spring-webmvc to trigger injection that makes
67+
// OpenTelemetryHandlerMappingFilter available.
68+
Class<?> dispatcherServletClass =
69+
beanFactory
70+
.getBeanClassLoader()
71+
.loadClass("org.springframework.web.servlet.DispatcherServlet");
72+
73+
// Now attempt to load our injected instrumentation class from the same class loader as
74+
// DispatcherServlet
75+
Class<?> clazz =
76+
dispatcherServletClass
77+
.getClassLoader()
78+
.loadClass(
79+
"org.springframework.web.servlet.v6_0.OpenTelemetryHandlerMappingFilter");
80+
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
81+
beanDefinition.setScope(SCOPE_SINGLETON);
82+
beanDefinition.setBeanClass(clazz);
83+
84+
((BeanDefinitionRegistry) beanFactory)
85+
.registerBeanDefinition("otelAutoDispatcherFilter", beanDefinition);
86+
} catch (ClassNotFoundException ignored) {
87+
// Ignore
88+
}
89+
}
90+
}
91+
}
92+
}

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/build.gradle.kts

+3-12
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ muzzle {
2020
dependencies {
2121
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
2222

23+
implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-common:javaagent"))
24+
2325
compileOnly("org.springframework:spring-webmvc:3.1.0.RELEASE")
2426
compileOnly("javax.servlet:javax.servlet-api:3.1.0")
25-
// compileOnly("org.springframework:spring-webmvc:2.5.6")
26-
// compileOnly("javax.servlet:servlet-api:2.4")
2727

2828
// Include servlet instrumentation for verifying the tomcat requests
2929
testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
3030
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent"))
3131
testInstrumentation(project(":instrumentation:tomcat:tomcat-7.0:javaagent"))
3232
testInstrumentation(project(":instrumentation:spring:spring-web:spring-web-3.1:javaagent"))
3333

34-
testImplementation("javax.validation:validation-api:1.1.0.Final")
35-
testImplementation("org.hibernate:hibernate-validator:5.4.2.Final")
36-
37-
testImplementation("org.spockframework:spock-spring")
34+
testImplementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-common:testing"))
3835

3936
testLibrary("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE")
4037
testLibrary("org.springframework.boot:spring-boot-starter-web:1.5.17.RELEASE")
@@ -43,12 +40,6 @@ dependencies {
4340
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:2.+")
4441
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-web:2.+")
4542
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-security:2.+")
46-
47-
testImplementation("org.springframework.security.oauth:spring-security-oauth2:2.0.16.RELEASE")
48-
49-
// For spring security
50-
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api:2.3.2")
51-
testImplementation("org.glassfish.jaxb:jaxb-runtime:2.3.2")
5243
}
5344

5445
tasks.withType<Test>().configureEach {

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/DispatcherServletInstrumentation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.springframework.context.ApplicationContext;
2525
import org.springframework.web.servlet.HandlerMapping;
2626
import org.springframework.web.servlet.ModelAndView;
27-
import org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter;
27+
import org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter;
2828

2929
public class DispatcherServletInstrumentation implements TypeInstrumentation {
3030

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/HandlerAdapterInstrumentation.java

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
2323
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2424
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
25+
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler;
2526
import javax.servlet.http.HttpServletRequest;
2627
import net.bytebuddy.asm.Advice;
2728
import net.bytebuddy.description.type.TypeDescription;

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/SpringWebMvcInstrumentationModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public SpringWebMvcInstrumentationModule() {
2121
@Override
2222
public boolean isHelperClass(String className) {
2323
return className.startsWith(
24-
"org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
24+
"org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter");
2525
}
2626

2727
@Override

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/SpringWebMvcSingletons.java

+5-16
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1;
77

8-
import io.opentelemetry.api.GlobalOpenTelemetry;
98
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10-
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
9+
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.SpringWebMvcInstrumenterFactory;
1110
import org.springframework.web.servlet.ModelAndView;
1211

1312
public final class SpringWebMvcSingletons {
@@ -18,20 +17,10 @@ public final class SpringWebMvcSingletons {
1817
private static final Instrumenter<ModelAndView, Void> MODEL_AND_VIEW_INSTRUMENTER;
1918

2019
static {
21-
HANDLER_INSTRUMENTER =
22-
Instrumenter.<Object, Void>builder(
23-
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, new HandlerSpanNameExtractor())
24-
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
25-
.buildInstrumenter();
26-
27-
MODEL_AND_VIEW_INSTRUMENTER =
28-
Instrumenter.<ModelAndView, Void>builder(
29-
GlobalOpenTelemetry.get(),
30-
INSTRUMENTATION_NAME,
31-
new ModelAndViewSpanNameExtractor())
32-
.addAttributesExtractor(new ModelAndViewAttributesExtractor())
33-
.setEnabled(ExperimentalConfig.get().viewTelemetryEnabled())
34-
.buildInstrumenter();
20+
SpringWebMvcInstrumenterFactory factory =
21+
new SpringWebMvcInstrumenterFactory(INSTRUMENTATION_NAME);
22+
HANDLER_INSTRUMENTER = factory.createHandlerInstrumenter();
23+
MODEL_AND_VIEW_INSTRUMENTER = factory.createModelAndViewInstrumenter();
3524
}
3625

3726
public static Instrumenter<Object, Void> handlerInstrumenter() {
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package org.springframework.web.servlet;
6+
package org.springframework.web.servlet.v3_1;
77

88
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource.CONTROLLER;
99

@@ -28,6 +28,8 @@
2828
import javax.servlet.http.HttpServletRequest;
2929
import javax.servlet.http.HttpServletResponse;
3030
import org.springframework.core.Ordered;
31+
import org.springframework.web.servlet.HandlerExecutionChain;
32+
import org.springframework.web.servlet.HandlerMapping;
3133
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
3234

3335
public class OpenTelemetryHandlerMappingFilter implements Filter, Ordered {

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/AuthServerConfig.groovy

-15
This file was deleted.

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SecurityConfig.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package test.boot
77

8+
import boot.SavingAuthenticationProvider
89
import org.springframework.context.annotation.Bean
910
import org.springframework.context.annotation.Configuration
1011
import org.springframework.core.annotation.Order

0 commit comments

Comments
 (0)