Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument spring-web 6 & spring-webmvc 6 #7366

Merged
merged 3 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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