com.catchpoint.trace.instrument.integrations.spring.web.advice.SpringRestTemplateAdviceInstrumenter Maven / Gradle / Ivy
package com.catchpoint.trace.instrument.integrations.spring.web.advice;
import com.catchpoint.trace.instrument.core.advice.AbstractAdviceInstrumenter;
import com.catchpoint.trace.instrument.core.advice.AdviceInstrumenter;
import com.catchpoint.trace.core.scope.CatchpointScope;
import com.catchpoint.trace.core.span.CatchpointSpan;
import com.catchpoint.trace.core.CatchpointTracer;
import com.catchpoint.trace.core.TraceSupport;
import com.catchpoint.trace.instrument.core.advice.matcher.ClassLoaderMatchers;
import com.catchpoint.trace.instrument.integrations.spring.web.SpringWebHelper;
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.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static net.bytebuddy.matcher.ElementMatchers.*;
/**
*
* {@link AdviceInstrumenter} implementation which instruments
* Spring RestTemplate versions 3.x, 4.x and 5.x to monitor HTTP requests.
*
*
* Enabled by default and disabled by catchpoint.trace.instrument.integrations.spring.disable
* system property.
*
*
* @author serkan
*/
public class SpringRestTemplateAdviceInstrumenter extends AbstractAdviceInstrumenter {
@Override
public boolean isEnabled() {
return SpringWebHelper.isEnabled();
}
@Override
protected ElementMatcher super TypeDescription> typeMatcher() {
return named("org.springframework.web.client.RestTemplate");
}
@Override
protected Collection helperClassNames() {
return Arrays.asList(
"com.catchpoint.trace.instrument.integrations.spring.web.advice.SpringHttpClientAdviceHelper",
"com.catchpoint.trace.instrument.integrations.spring.web.advice.SpringRestTemplateAdviceHelper",
SpringRestTemplateAdviceInstrumenter.class.getName() + "$TraceAwareRequestCallback",
SpringRestTemplateAdviceInstrumenter.class.getName() + "$TraceAwareResponseExtractor");
}
@Override
protected ElementMatcher classLoaderMatcher() {
return ClassLoaderMatchers.hasClassesNamed(new String[]{
"org.springframework.web.client.RestTemplate"});
}
@Override
protected Map extends ElementMatcher super MethodDescription>, String> transformers() {
Map, String> transformers = new HashMap();
transformers.put(
isMethod().
and(isProtected()).
and(named("doExecute")).
and(takesArgument(0, URI.class)).
and(takesArgument(1, named("org.springframework.http.HttpMethod"))).
and(takesArgument(2, named("org.springframework.web.client.RequestCallback"))).
and(takesArgument(3, named("org.springframework.web.client.ResponseExtractor"))),
SpringRestTemplateAdviceInstrumenter.class.getName() + "$RestTemplateDoExecuteAdvice");
transformers.put(
isMethod().
and(isProtected()).
and(named("handleResponse")).
and(takesArgument(0, URI.class)).
and(takesArgument(1, named("org.springframework.http.HttpMethod"))).
and(takesArgument(2, named("org.springframework.http.client.ClientHttpResponse"))),
SpringRestTemplateAdviceInstrumenter.class.getName() + "$RestTemplateHandleResponseAdvice");
return transformers;
}
public static class RestTemplateDoExecuteAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static CatchpointScope enter(@Advice.Argument(0) URI uri,
@Advice.Argument(1) HttpMethod httpMethod,
@Advice.Argument(value = 2, readOnly = false) RequestCallback requestCallback,
@Advice.Argument(value = 3, readOnly = false) ResponseExtractor responseExtractor) {
CatchpointTracer tracer = TraceSupport.getTracer();
CatchpointScope scope = SpringRestTemplateAdviceHelper.createAndStartSpan(tracer, uri, httpMethod);
CatchpointSpan span = scope.span();
requestCallback = new TraceAwareRequestCallback(requestCallback, span);
responseExtractor = new TraceAwareResponseExtractor(responseExtractor, span);
return scope;
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void exit(@Advice.Enter CatchpointScope scope,
@Advice.Thrown Throwable error) {
SpringRestTemplateAdviceHelper.finishScope(scope, error);
}
}
public static class RestTemplateHandleResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void enter(@Advice.Argument(value = 2, readOnly = false) ClientHttpResponse response)
throws IOException {
CatchpointTracer tracer = TraceSupport.getTracer();
CatchpointSpan span = tracer.activeSpan();
if (span != null) {
response = SpringRestTemplateAdviceHelper.onResponse(span, response);
}
}
}
public static class TraceAwareRequestCallback implements RequestCallback {
private final RequestCallback requestCallback;
private final CatchpointSpan span;
public TraceAwareRequestCallback(RequestCallback requestCallback, CatchpointSpan span) {
this.requestCallback = requestCallback;
this.span = span;
}
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
SpringRestTemplateAdviceHelper.onRequest(span, request);
}
}
public static class TraceAwareResponseExtractor implements ResponseExtractor {
private final ResponseExtractor responseExtractor;
private final CatchpointSpan span;
public TraceAwareResponseExtractor(ResponseExtractor responseExtractor, CatchpointSpan span) {
this.responseExtractor = responseExtractor;
this.span = span;
}
@Override
public Object extractData(ClientHttpResponse response) throws IOException {
response = SpringRestTemplateAdviceHelper.onResponse(span, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
} else {
return null;
}
}
}
}