Skip to content

Commit

Permalink
Add span for DispatcherServlet.render (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
kanagat kananinja nugusbayev authored and felixbarny committed Jan 8, 2019
1 parent 1f267c6 commit 95b0572
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

## Bug Fixes

# 1.2.1

## Features
* Added span for `DispatcherServlet#render`.

# 1.2.0

## Features
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 - 2019 Elastic and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package co.elastic.apm.agent.spring.webmvc;

import co.elastic.apm.agent.bci.ElasticApmInstrumentation;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
import co.elastic.apm.agent.impl.transaction.Span;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.web.servlet.ModelAndView;

import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;

public class DispatcherServletRenderInstrumentation extends ElasticApmInstrumentation {

private static final String SPAN_TYPE_DISPATCHER_SERVLET_RENDER = "template.dispatcher-servlet.render";
private static final String DISPATCHER_SERVLET_RENDER_METHOD = "DispatcherServlet#render";

@Advice.OnMethodEnter(suppress = Throwable.class)
private static void beforeExecute(@Advice.Argument(0) @Nullable ModelAndView modelAndView,
@Advice.Local("span") Span span) {
if (tracer == null || tracer.activeSpan() == null) {
return;
}
final AbstractSpan<?> parent = tracer.activeSpan();
span = parent.createSpan().withType(SPAN_TYPE_DISPATCHER_SERVLET_RENDER).withName(DISPATCHER_SERVLET_RENDER_METHOD);
if (modelAndView != null && modelAndView.getViewName() != null) {
span.appendToName(" ").appendToName(modelAndView.getViewName());
}
span.activate();
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
private static void afterExecute(@Advice.Local("span") @Nullable Span span,
@Advice.Thrown @Nullable Throwable t) {
if (span != null) {
span.captureException(t)
.deactivate()
.end();
}
}

@Override
public ElementMatcher<? super TypeDescription> getTypeMatcher() {
return named("org.springframework.web.servlet.DispatcherServlet");
}

@Override
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
return named("render")
.and(takesArgument(0, named("org.springframework.web.servlet.ModelAndView")))
.and(takesArgument(1, named("javax.servlet.http.HttpServletRequest")))
.and(takesArgument(2, named("javax.servlet.http.HttpServletResponse")));
}

@Override
public Collection<String> getInstrumentationGroupNames() {
return Arrays.asList("dispatcher-servlet", "render");
}
}

Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
co.elastic.apm.agent.spring.webmvc.SpringTransactionNameInstrumentation
co.elastic.apm.agent.spring.webmvc.DispatcherServletRenderInstrumentation
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 - 2019 Elastic and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package co.elastic.apm.agent.spring.webmvc;

import co.elastic.apm.agent.MockReporter;
import co.elastic.apm.agent.bci.ElasticApmAgent;
import co.elastic.apm.agent.configuration.SpyConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.ElasticApmTracerBuilder;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.servlet.ServletInstrumentation;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.junit.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

public class DispatcherServletRenderInstrumentationTest {

private static MockReporter reporter;
private static ElasticApmTracer tracer;
private MockMvc mockMvc;

@BeforeClass
@BeforeAll
public static void setUpAll() {
reporter = new MockReporter();
tracer = new ElasticApmTracerBuilder()
.configurationRegistry(SpyConfiguration.createSpyConfig())
.reporter(reporter)
.build();
ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(),
Arrays.asList(new ServletInstrumentation(), new DispatcherServletRenderInstrumentation()));
}

@AfterClass
@AfterAll
public static void afterAll() {
ElasticApmAgent.reset();
}

@Before
@BeforeEach
public void setup() {
this.mockMvc = MockMvcBuilders
.standaloneSetup(new MessageController())
.setViewResolvers(jspViewResolver())
.build();
}

private ViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/static/");
bean.setSuffix(".jsp");
return bean;
}

@Controller
public static class MessageController {
@GetMapping("/test")
public ModelAndView test() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("message-view");
return modelAndView;
}
}

@Test
public void testCallOfDispatcherServletWithNonNullModelAndView() throws Exception {
reporter.reset();
this.mockMvc.perform(get("/test"));
assertEquals(1, reporter.getTransactions().size());
assertEquals(1, reporter.getSpans().size());
assertEquals("DispatcherServlet#render message-view", reporter.getSpans().get(0).getName().toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
<h2>Hello From Message-view.jsp!</h2>
</body>
</html>
4 changes: 2 additions & 2 deletions docs/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ you should add an additional entry to this list (make sure to also include the d
==== `disable_instrumentations`

A list of instrumentations which should be disabled.
Valid options are `annotations`, `apache-httpclient`, `elasticsearch-restclient`, `http-client`, `incubating`, `jax-rs`, `jax-rs-annotations`, `jdbc`, `opentracing`, `public-api`, `servlet-api`, `servlet-api-async`, `spring-mvc`, `spring-resttemplate`.
Valid options are `annotations`, `apache-httpclient`, `dispatcher-servlet`, `elasticsearch-restclient`, `http-client`, `incubating`, `jax-rs`, `jax-rs-annotations`, `jdbc`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `spring-mvc`, `spring-resttemplate`.
If you want to try out incubating features,
set the value to an empty string.

Expand Down Expand Up @@ -1019,7 +1019,7 @@ The default unit for this option is `ms`
# sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie
# A list of instrumentations which should be disabled.
# Valid options are `annotations`, `apache-httpclient`, `elasticsearch-restclient`, `http-client`, `incubating`, `jax-rs`, `jax-rs-annotations`, `jdbc`, `opentracing`, `public-api`, `servlet-api`, `servlet-api-async`, `spring-mvc`, `spring-resttemplate`.
# Valid options are `annotations`, `apache-httpclient`, `dispatcher-servlet`, `elasticsearch-restclient`, `http-client`, `incubating`, `jax-rs`, `jax-rs-annotations`, `jdbc`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `spring-mvc`, `spring-resttemplate`.
# If you want to try out incubating features,
# set the value to an empty string.
#
Expand Down

0 comments on commit 95b0572

Please sign in to comment.