Skip to content

Commit

Permalink
Instrument spring-web 6 & spring-webmvc 6
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Rzeszutek committed Dec 8, 2022
1 parent 17b3fdb commit aeeb340
Show file tree
Hide file tree
Showing 35 changed files with 1,018 additions and 244 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.core;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;

@AutoService(IgnoredTypesConfigurer.class)
public class SpringCoreIgnoredTypesConfigurer implements IgnoredTypesConfigurer {

@Override
public void configure(IgnoredTypesBuilder builder, ConfigProperties config) {
// a Runnable task class that we don't need to touch
builder
.ignoreClass("org.springframework.util.ConcurrentLruCache$AddTask")
.ignoreTaskClass("org.springframework.util.ConcurrentLruCache$AddTask");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory b
Class<?> clazz =
dispatcherServletClass
.getClassLoader()
.loadClass("org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
.loadClass(
"org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter");
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setScope(SCOPE_SINGLETON);
beanDefinition.setBeanClass(clazz);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.springframework")
module.set("spring-web")
versions.set("[6.0.0)")
assertInverse.set(true)
}
}

dependencies {
compileOnly("org.springframework:spring-web:6.0.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(InstrumentationModule.class)
public class SpringWebInstrumentationModule extends InstrumentationModule {

public SpringWebInstrumentationModule() {
super("spring-web", "spring-web-6.0");
}

@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
// class added in 6.0
return hasClassesNamed("org.springframework.web.ErrorResponse");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new WebApplicationContextInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.web.v6_0;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SINGLETON;

import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;

/**
* This instrumentation adds the OpenTelemetryHandlerMappingFilter definition to the spring context
* When the context is created, the filter will be added to the beginning of the filter chain.
*/
public class WebApplicationContextInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed(
"org.springframework.context.support.AbstractApplicationContext",
"org.springframework.web.context.WebApplicationContext");
}

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(named("org.springframework.context.support.AbstractApplicationContext"))
.and(implementsInterface(named("org.springframework.web.context.WebApplicationContext")));
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("postProcessBeanFactory"))
.and(
takesArgument(
0,
named(
"org.springframework.beans.factory.config.ConfigurableListableBeanFactory"))),
WebApplicationContextInstrumentation.class.getName() + "$FilterInjectingAdvice");
}

@SuppressWarnings("unused")
public static class FilterInjectingAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry
&& !beanFactory.containsBean("otelAutoDispatcherFilter")) {
try {
// Firstly check whether DispatcherServlet is present. We need to load an instrumented
// class from spring-webmvc to trigger injection that makes
// OpenTelemetryHandlerMappingFilter available.
Class<?> dispatcherServletClass =
beanFactory
.getBeanClassLoader()
.loadClass("org.springframework.web.servlet.DispatcherServlet");

// Now attempt to load our injected instrumentation class from the same class loader as
// DispatcherServlet
Class<?> clazz =
dispatcherServletClass
.getClassLoader()
.loadClass(
"org.springframework.web.servlet.v6_0.OpenTelemetryHandlerMappingFilter");
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setScope(SCOPE_SINGLETON);
beanDefinition.setBeanClass(clazz);

((BeanDefinitionRegistry) beanFactory)
.registerBeanDefinition("otelAutoDispatcherFilter", beanDefinition);
} catch (ClassNotFoundException ignored) {
// Ignore
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,18 @@ muzzle {
dependencies {
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))

implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-common:javaagent"))

compileOnly("org.springframework:spring-webmvc:3.1.0.RELEASE")
compileOnly("javax.servlet:javax.servlet-api:3.1.0")
// compileOnly("org.springframework:spring-webmvc:2.5.6")
// compileOnly("javax.servlet:servlet-api:2.4")

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

testImplementation("javax.validation:validation-api:1.1.0.Final")
testImplementation("org.hibernate:hibernate-validator:5.4.2.Final")

testImplementation("org.spockframework:spock-spring")
testImplementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-common:testing"))

testLibrary("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE")
testLibrary("org.springframework.boot:spring-boot-starter-web:1.5.17.RELEASE")
Expand All @@ -43,12 +40,6 @@ dependencies {
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:2.+")
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-web:2.+")
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-security:2.+")

testImplementation("org.springframework.security.oauth:spring-security-oauth2:2.0.16.RELEASE")

// For spring security
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api:2.3.2")
testImplementation("org.glassfish.jaxb:jaxb-runtime:2.3.2")
}

tasks.withType<Test>().configureEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.springframework.context.ApplicationContext;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter;
import org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter;

public class DispatcherServletInstrumentation implements TypeInstrumentation {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler;
import javax.servlet.http.HttpServletRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public SpringWebMvcInstrumentationModule() {
@Override
public boolean isHelperClass(String className) {
return className.startsWith(
"org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
"org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

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

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.SpringWebMvcInstrumenterFactory;
import org.springframework.web.servlet.ModelAndView;

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

static {
HANDLER_INSTRUMENTER =
Instrumenter.<Object, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, new HandlerSpanNameExtractor())
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
.buildInstrumenter();

MODEL_AND_VIEW_INSTRUMENTER =
Instrumenter.<ModelAndView, Void>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
new ModelAndViewSpanNameExtractor())
.addAttributesExtractor(new ModelAndViewAttributesExtractor())
.setEnabled(ExperimentalConfig.get().viewTelemetryEnabled())
.buildInstrumenter();
SpringWebMvcInstrumenterFactory factory =
new SpringWebMvcInstrumenterFactory(INSTRUMENTATION_NAME);
HANDLER_INSTRUMENTER = factory.createHandlerInstrumenter();
MODEL_AND_VIEW_INSTRUMENTER = factory.createModelAndViewInstrumenter();
}

public static Instrumenter<Object, Void> handlerInstrumenter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package org.springframework.web.servlet;
package org.springframework.web.servlet.v3_1;

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

Expand All @@ -28,6 +28,8 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

public class OpenTelemetryHandlerMappingFilter implements Filter, Ordered {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package test.boot

import boot.SavingAuthenticationProvider
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
Expand Down
Loading

0 comments on commit aeeb340

Please sign in to comment.