Move more type instrumentations to top-level classes (#3118)

This commit is contained in:
Trask Stalnaker 2021-05-28 17:24:19 -07:00 committed by GitHub
parent ca5b792f95
commit c1c052318b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 2312 additions and 2000 deletions

View File

@ -1,158 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.scaladsl.HttpExt;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Collections;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import scala.concurrent.Future;
import scala.runtime.AbstractFunction1;
import scala.util.Try;
@AutoService(InstrumentationModule.class)
public class AkkaHttpClientInstrumentationModule extends InstrumentationModule {
public AkkaHttpClientInstrumentationModule() {
super("akka-http", "akka-http-client");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new HttpExtInstrumentation());
}
public static class HttpExtInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("akka.http.scaladsl.HttpExt");
}
@Override
public void transform(TypeTransformer transformer) {
// This is mainly for compatibility with 10.0
transformer.applyAdviceToMethod(
named("singleRequest")
.and(takesArgument(0, named("akka.http.scaladsl.model.HttpRequest"))),
AkkaHttpClientInstrumentationModule.class.getName() + "$SingleRequestAdvice");
// This is for 10.1+
transformer.applyAdviceToMethod(
named("singleRequestImpl")
.and(takesArgument(0, named("akka.http.scaladsl.model.HttpRequest"))),
AkkaHttpClientInstrumentationModule.class.getName() + "$SingleRequestAdvice");
}
}
public static class SingleRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0, readOnly = false) HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
/*
Versions 10.0 and 10.1 have slightly different structure that is hard to distinguish so here
we cast 'wider net' and avoid instrumenting twice.
In the future we may want to separate these, but since lots of code is reused we would need to come up
with way of continuing to reusing it.
*/
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
// Request is immutable, so we have to assign new value once we update headers
AkkaHttpHeaders headers = new AkkaHttpHeaders(request);
context = tracer().startSpan(parentContext, request, headers);
scope = context.makeCurrent();
request = headers.getRequest();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Argument(0) HttpRequest request,
@Advice.This HttpExt thiz,
@Advice.Return Future<HttpResponse> responseFuture,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable == null) {
responseFuture.onComplete(new OnCompleteHandler(context), thiz.system().dispatcher());
} else {
tracer().endExceptionally(context, throwable);
}
}
}
public static class OnCompleteHandler extends AbstractFunction1<Try<HttpResponse>, Void> {
private final Context context;
public OnCompleteHandler(Context context) {
this.context = context;
}
@Override
public Void apply(Try<HttpResponse> result) {
if (result.isSuccess()) {
tracer().end(context, result.get());
} else {
tracer().endExceptionally(context, result.failed().get());
}
return null;
}
}
public static class AkkaHttpHeaders {
private HttpRequest request;
public AkkaHttpHeaders(HttpRequest request) {
this.request = request;
}
public HttpRequest getRequest() {
return request;
}
public void setRequest(HttpRequest request) {
this.request = request;
}
}
public static class InjectAdapter implements TextMapSetter<AkkaHttpHeaders> {
public static final InjectAdapter SETTER = new InjectAdapter();
@Override
public void set(AkkaHttpHeaders carrier, String key, String value) {
HttpRequest request = carrier.getRequest();
if (request != null) {
// It looks like this cast is only needed in Java, Scala would have figured it out
carrier.setRequest(
(HttpRequest) request.removeHeader(key).addHeader(RawHeader.create(key, value)));
}
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
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;
@AutoService(InstrumentationModule.class)
public class AkkaHttpClientInstrumentationModule extends InstrumentationModule {
public AkkaHttpClientInstrumentationModule() {
super("akka-http", "akka-http-client");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpExtClientInstrumentation());
}
}

View File

@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp;
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientInstrumentationModule.InjectAdapter.SETTER;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.client.HttpHeaderSetter.SETTER;
import akka.http.javadsl.model.HttpHeader;
import akka.http.scaladsl.model.HttpRequest;
@ -13,7 +13,6 @@ import akka.http.scaladsl.model.HttpResponse;
import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpClientInstrumentationModule.AkkaHttpHeaders;
import java.net.URI;
import java.net.URISyntaxException;

View File

@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
import akka.http.scaladsl.model.HttpRequest;
public class AkkaHttpHeaders {
private HttpRequest request;
public AkkaHttpHeaders(HttpRequest request) {
this.request = request;
}
public HttpRequest getRequest() {
return request;
}
public void setRequest(HttpRequest request) {
this.request = request;
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.client.AkkaHttpClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import akka.http.scaladsl.HttpExt;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
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 scala.concurrent.Future;
public class HttpExtClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("akka.http.scaladsl.HttpExt");
}
@Override
public void transform(TypeTransformer transformer) {
// This is mainly for compatibility with 10.0
transformer.applyAdviceToMethod(
named("singleRequest").and(takesArgument(0, named("akka.http.scaladsl.model.HttpRequest"))),
this.getClass().getName() + "$SingleRequestAdvice");
// This is for 10.1+
transformer.applyAdviceToMethod(
named("singleRequestImpl")
.and(takesArgument(0, named("akka.http.scaladsl.model.HttpRequest"))),
this.getClass().getName() + "$SingleRequestAdvice");
}
public static class SingleRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0, readOnly = false) HttpRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
/*
Versions 10.0 and 10.1 have slightly different structure that is hard to distinguish so here
we cast 'wider net' and avoid instrumenting twice.
In the future we may want to separate these, but since lots of code is reused we would need to come up
with way of continuing to reusing it.
*/
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
// Request is immutable, so we have to assign new value once we update headers
AkkaHttpHeaders headers = new AkkaHttpHeaders(request);
context = tracer().startSpan(parentContext, request, headers);
scope = context.makeCurrent();
request = headers.getRequest();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Argument(0) HttpRequest request,
@Advice.This HttpExt thiz,
@Advice.Return Future<HttpResponse> responseFuture,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable == null) {
responseFuture.onComplete(new OnCompleteHandler(context), thiz.system().dispatcher());
} else {
tracer().endExceptionally(context, throwable);
}
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.scaladsl.model.HttpRequest;
import io.opentelemetry.context.propagation.TextMapSetter;
public class HttpHeaderSetter implements TextMapSetter<AkkaHttpHeaders> {
public static final HttpHeaderSetter SETTER = new HttpHeaderSetter();
@Override
public void set(AkkaHttpHeaders carrier, String key, String value) {
HttpRequest request = carrier.getRequest();
if (request != null) {
// It looks like this cast is only needed in Java, Scala would have figured it out
carrier.setRequest(
(HttpRequest) request.removeHeader(key).addHeader(RawHeader.create(key, value)));
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.client;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.client.AkkaHttpClientTracer.tracer;
import akka.http.scaladsl.model.HttpResponse;
import io.opentelemetry.context.Context;
import scala.runtime.AbstractFunction1;
import scala.util.Try;
public class OnCompleteHandler extends AbstractFunction1<Try<HttpResponse>, Void> {
private final Context context;
public OnCompleteHandler(Context context) {
this.context = context;
}
@Override
public Void apply(Try<HttpResponse> result) {
if (result.isSuccess()) {
tracer().end(context, result.get());
} else {
tracer().endExceptionally(context, result.failed().get());
}
return null;
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp;
package io.opentelemetry.javaagent.instrumentation.akkahttp.server;
import akka.http.javadsl.model.HttpHeader;
import akka.http.scaladsl.model.HttpRequest;

View File

@ -3,26 +3,19 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp;
package io.opentelemetry.javaagent.instrumentation.akkahttp.server;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpServerTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.akkahttp.server.AkkaHttpServerTracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import akka.stream.Materializer;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import scala.Function1;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
@ -36,49 +29,7 @@ public class AkkaHttpServerInstrumentationModule extends InstrumentationModule {
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpExtInstrumentation());
}
public static class HttpExtInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("akka.http.scaladsl.HttpExt");
}
@Override
public void transform(TypeTransformer transformer) {
// Instrumenting akka-streams bindAndHandle api was previously attempted.
// This proved difficult as there was no clean way to close the async scope
// in the graph logic after the user's request handler completes.
//
// Instead, we're instrumenting the bindAndHandle function helpers by
// wrapping the scala functions with our own handlers.
transformer.applyAdviceToMethod(
named("bindAndHandleSync").and(takesArgument(0, named("scala.Function1"))),
AkkaHttpServerInstrumentationModule.class.getName() + "$AkkaHttpSyncAdvice");
transformer.applyAdviceToMethod(
named("bindAndHandleAsync").and(takesArgument(0, named("scala.Function1"))),
AkkaHttpServerInstrumentationModule.class.getName() + "$AkkaHttpAsyncAdvice");
}
}
public static class AkkaHttpSyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapHandler(
@Advice.Argument(value = 0, readOnly = false)
Function1<HttpRequest, HttpResponse> handler) {
handler = new SyncWrapper(handler);
}
}
public static class AkkaHttpAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapHandler(
@Advice.Argument(value = 0, readOnly = false)
Function1<HttpRequest, Future<HttpResponse>> handler,
@Advice.Argument(7) Materializer materializer) {
handler = new AsyncWrapper(handler, materializer.executionContext());
}
return singletonList(new HttpExtServerInstrumentation());
}
public static class SyncWrapper extends AbstractFunction1<HttpRequest, HttpResponse> {

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp;
package io.opentelemetry.javaagent.instrumentation.akkahttp.server;
import akka.http.javadsl.model.HttpHeader;
import akka.http.scaladsl.model.HttpRequest;

View File

@ -0,0 +1,64 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.akkahttp.server;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import akka.stream.Materializer;
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 scala.Function1;
import scala.concurrent.Future;
public class HttpExtServerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("akka.http.scaladsl.HttpExt");
}
@Override
public void transform(TypeTransformer transformer) {
// Instrumenting akka-streams bindAndHandle api was previously attempted.
// This proved difficult as there was no clean way to close the async scope
// in the graph logic after the user's request handler completes.
//
// Instead, we're instrumenting the bindAndHandle function helpers by
// wrapping the scala functions with our own handlers.
transformer.applyAdviceToMethod(
named("bindAndHandleSync").and(takesArgument(0, named("scala.Function1"))),
this.getClass().getName() + "$AkkaHttpSyncAdvice");
transformer.applyAdviceToMethod(
named("bindAndHandleAsync").and(takesArgument(0, named("scala.Function1"))),
this.getClass().getName() + "$AkkaHttpAsyncAdvice");
}
public static class AkkaHttpSyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapHandler(
@Advice.Argument(value = 0, readOnly = false)
Function1<HttpRequest, HttpResponse> handler) {
handler = new AkkaHttpServerInstrumentationModule.SyncWrapper(handler);
}
}
public static class AkkaHttpAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapHandler(
@Advice.Argument(value = 0, readOnly = false)
Function1<HttpRequest, Future<HttpResponse>> handler,
@Advice.Argument(7) Materializer materializer) {
handler =
new AkkaHttpServerInstrumentationModule.AsyncWrapper(
handler, materializer.executionContext());
}
}
}

View File

@ -5,22 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.apachecamel;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.camel.CamelContext;
@AutoService(InstrumentationModule.class)
public class ApacheCamelInstrumentationModule extends InstrumentationModule {
@ -38,35 +28,4 @@ public class ApacheCamelInstrumentationModule extends InstrumentationModule {
public boolean isHelperClass(String className) {
return className.startsWith("io.opentelemetry.extension.aws.");
}
public static class CamelContextInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("org.apache.camel.CamelContext");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("org.apache.camel.CamelContext"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("start").and(isPublic()).and(takesArguments(0)),
ApacheCamelInstrumentationModule.class.getName() + "$ContextAdvice");
}
}
public static class ContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onContextStart(@Advice.This final CamelContext context) throws Exception {
if (context.hasService(CamelTracingService.class) == null) {
// start this service eager so we init before Camel is starting up
context.addService(new CamelTracingService(context), true, true);
}
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachecamel;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
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.apache.camel.CamelContext;
public class CamelContextInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("org.apache.camel.CamelContext");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("org.apache.camel.CamelContext"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("start").and(isPublic()).and(takesArguments(0)),
this.getClass().getName() + "$StartAdvice");
}
public static class StartAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onContextStart(@Advice.This final CamelContext context) throws Exception {
if (context.hasService(CamelTracingService.class) == null) {
// start this service eager so we init before Camel is starting up
context.addService(new CamelTracingService(context), true, true);
}
}
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.HttpUrlConnectionTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.HttpURLConnection;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
public class HttpUrlConnectionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return nameStartsWith("java.net.")
.or(ElementMatchers.<TypeDescription>nameStartsWith("sun.net"))
// In WebLogic, URL.openConnection() returns its own internal implementation of
// HttpURLConnection, which does not delegate the methods that have to be instrumented to
// the JDK superclass. Therefore it needs to be instrumented directly.
.or(named("weblogic.net.http.HttpURLConnection"))
// This class is a simple delegator. Skip because it does not update its `connected`
// field.
.and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl")))
.and(extendsClass(named("java.net.HttpURLConnection")));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(namedOneOf("connect", "getOutputStream", "getInputStream")),
this.getClass().getName() + "$HttpUrlConnectionAdvice");
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(named("getResponseCode")),
this.getClass().getName() + "$GetResponseCodeAdvice");
}
public static class HttpUrlConnectionAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("connected") boolean connected,
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = CallDepthThreadLocalMap.getCallDepth(HttpURLConnection.class);
if (callDepth.getAndIncrement() > 0) {
// only want the rest of the instrumentation rules (which are complex enough) to apply to
// top-level HttpURLConnection calls
return;
}
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
// using storage for a couple of reasons:
// - to start an operation in connect() and end it in getInputStream()
// - to avoid creating a new operation on multiple subsequent calls to getInputStream()
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
httpUrlState = storage.get(connection);
if (httpUrlState != null) {
if (!httpUrlState.finished) {
scope = httpUrlState.context.makeCurrent();
}
return;
}
Context context = tracer().startSpan(parentContext, connection);
httpUrlState = new HttpUrlState(context);
storage.put(connection, httpUrlState);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("responseCode") int responseCode,
@Advice.Thrown Throwable throwable,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
if (responseCode >= 400) {
// HttpURLConnection unnecessarily throws exception on error response.
// None of the other http clients do this, so not recording the exception on the span
// to be consistent with the telemetry for other http clients.
tracer().end(httpUrlState.context, new HttpUrlResponse(connection, responseCode));
} else {
tracer().endExceptionally(httpUrlState.context, throwable);
}
httpUrlState.finished = true;
} else if (methodName.equals("getInputStream") && responseCode > 0) {
// responseCode field is sometimes not populated.
// We can't call getResponseCode() due to some unwanted side-effects
// (e.g. breaks getOutputStream).
tracer().end(httpUrlState.context, new HttpUrlResponse(connection, responseCode));
httpUrlState.finished = true;
}
}
}
public static class GetResponseCodeAdvice {
@Advice.OnMethodExit
public static void methodExit(
@Advice.This HttpURLConnection connection, @Advice.Return int returnValue) {
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
HttpUrlState httpUrlState = storage.get(connection);
if (httpUrlState != null) {
Span span = Java8BytecodeBridge.spanFromContext(httpUrlState.context);
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, returnValue);
StatusCode statusCode = HttpStatusConverter.statusFromHttpStatus(returnValue);
if (statusCode != StatusCode.UNSET) {
span.setStatus(statusCode);
}
}
}
}
}

View File

@ -5,38 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.HttpUrlConnectionTracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.HttpStatusConverter;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.HttpURLConnection;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
@AutoService(InstrumentationModule.class)
public class HttpUrlConnectionInstrumentationModule extends InstrumentationModule {
@ -49,141 +23,4 @@ public class HttpUrlConnectionInstrumentationModule extends InstrumentationModul
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpUrlConnectionInstrumentation());
}
public static class HttpUrlConnectionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return nameStartsWith("java.net.")
.or(ElementMatchers.<TypeDescription>nameStartsWith("sun.net"))
// In WebLogic, URL.openConnection() returns its own internal implementation of
// HttpURLConnection, which does not delegate the methods that have to be instrumented to
// the JDK superclass. Therefore it needs to be instrumented directly.
.or(named("weblogic.net.http.HttpURLConnection"))
// This class is a simple delegator. Skip because it does not update its `connected`
// field.
.and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl")))
.and(extendsClass(named("java.net.HttpURLConnection")));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(namedOneOf("connect", "getOutputStream", "getInputStream")),
HttpUrlConnectionInstrumentationModule.class.getName() + "$HttpUrlConnectionAdvice");
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(named("getResponseCode")),
HttpUrlConnectionInstrumentationModule.class.getName() + "$GetResponseCodeAdvice");
}
}
public static class HttpUrlConnectionAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("connected") boolean connected,
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth = CallDepthThreadLocalMap.getCallDepth(HttpURLConnection.class);
if (callDepth.getAndIncrement() > 0) {
// only want the rest of the instrumentation rules (which are complex enough) to apply to
// top-level HttpURLConnection calls
return;
}
Context parentContext = currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
// using storage for a couple of reasons:
// - to start an operation in connect() and end it in getInputStream()
// - to avoid creating a new operation on multiple subsequent calls to getInputStream()
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
httpUrlState = storage.get(connection);
if (httpUrlState != null) {
if (!httpUrlState.finished) {
scope = httpUrlState.context.makeCurrent();
}
return;
}
Context context = tracer().startSpan(parentContext, connection);
httpUrlState = new HttpUrlState(context);
storage.put(connection, httpUrlState);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.This HttpURLConnection connection,
@Advice.FieldValue("responseCode") int responseCode,
@Advice.Thrown Throwable throwable,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelHttpUrlState") HttpUrlState httpUrlState,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) {
return;
}
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
if (responseCode >= 400) {
// HttpURLConnection unnecessarily throws exception on error response.
// None of the other http clients do this, so not recording the exception on the span
// to be consistent with the telemetry for other http clients.
tracer().end(httpUrlState.context, new HttpUrlResponse(connection, responseCode));
} else {
tracer().endExceptionally(httpUrlState.context, throwable);
}
httpUrlState.finished = true;
} else if (methodName.equals("getInputStream") && responseCode > 0) {
// responseCode field is sometimes not populated.
// We can't call getResponseCode() due to some unwanted side-effects
// (e.g. breaks getOutputStream).
tracer().end(httpUrlState.context, new HttpUrlResponse(connection, responseCode));
httpUrlState.finished = true;
}
}
}
public static class GetResponseCodeAdvice {
@Advice.OnMethodExit
public static void methodExit(
@Advice.This HttpURLConnection connection, @Advice.Return int returnValue) {
ContextStore<HttpURLConnection, HttpUrlState> storage =
InstrumentationContext.get(HttpURLConnection.class, HttpUrlState.class);
HttpUrlState httpUrlState = storage.get(connection);
if (httpUrlState != null) {
Span span = Java8BytecodeBridge.spanFromContext(httpUrlState.context);
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, returnValue);
StatusCode statusCode = HttpStatusConverter.statusFromHttpStatus(returnValue);
if (statusCode != StatusCode.UNSET) {
span.setStatus(statusCode);
}
}
}
}
// everything is public since called directly from advice code
// (which is inlined into other packages)
public static class HttpUrlState {
public final Context context;
public boolean finished;
public HttpUrlState(Context context) {
this.context = context;
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import io.opentelemetry.context.Context;
// everything is public since called directly from advice code
// (which is inlined into other packages)
public class HttpUrlState {
public final Context context;
public boolean finished;
public HttpUrlState(Context context) {
this.context = context;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.hystrix;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.netflix.hystrix.HystrixInvokableInfo;
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 rx.Observable;
public class HystrixCommandInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed(
"com.netflix.hystrix.HystrixCommand", "com.netflix.hystrix.HystrixObservableCommand");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(
namedOneOf(
"com.netflix.hystrix.HystrixCommand", "com.netflix.hystrix.HystrixObservableCommand"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("getExecutionObservable").and(returns(named("rx.Observable"))),
this.getClass().getName() + "$ExecuteAdvice");
transformer.applyAdviceToMethod(
named("getFallbackObservable").and(returns(named("rx.Observable"))),
this.getClass().getName() + "$FallbackAdvice");
}
public static class ExecuteAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.This HystrixInvokableInfo<?> command,
@Advice.Return(readOnly = false) Observable<?> result,
@Advice.Thrown Throwable throwable) {
result = Observable.create(new HystrixOnSubscribe<>(result, command, "execute"));
}
}
public static class FallbackAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.This HystrixInvokableInfo<?> command,
@Advice.Return(readOnly = false) Observable<?> result,
@Advice.Thrown Throwable throwable) {
result = Observable.create(new HystrixOnSubscribe<>(result, command, "fallback"));
}
}
}

View File

@ -5,33 +5,16 @@
package io.opentelemetry.javaagent.instrumentation.hystrix;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.javaagent.instrumentation.hystrix.HystrixTracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import com.netflix.hystrix.HystrixInvokableInfo;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.rxjava.TracedOnSubscribe;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import rx.Observable;
@AutoService(InstrumentationModule.class)
public class HystrixInstrumentationModule extends InstrumentationModule {
private static final String OPERATION_NAME = "hystrix.cmd";
public HystrixInstrumentationModule() {
super("hystrix", "hystrix-1.4");
}
@ -45,72 +28,4 @@ public class HystrixInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HystrixCommandInstrumentation());
}
public static class HystrixCommandInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed(
"com.netflix.hystrix.HystrixCommand", "com.netflix.hystrix.HystrixObservableCommand");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(
namedOneOf(
"com.netflix.hystrix.HystrixCommand",
"com.netflix.hystrix.HystrixObservableCommand"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("getExecutionObservable").and(returns(named("rx.Observable"))),
HystrixInstrumentationModule.class.getName() + "$ExecuteAdvice");
transformer.applyAdviceToMethod(
named("getFallbackObservable").and(returns(named("rx.Observable"))),
HystrixInstrumentationModule.class.getName() + "$FallbackAdvice");
}
}
public static class ExecuteAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.This HystrixInvokableInfo<?> command,
@Advice.Return(readOnly = false) Observable<?> result,
@Advice.Thrown Throwable throwable) {
result = Observable.create(new HystrixOnSubscribe<>(result, command, "execute"));
}
}
public static class FallbackAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.This HystrixInvokableInfo<?> command,
@Advice.Return(readOnly = false) Observable<?> result,
@Advice.Thrown Throwable throwable) {
result = Observable.create(new HystrixOnSubscribe<>(result, command, "fallback"));
}
}
public static class HystrixOnSubscribe<T> extends TracedOnSubscribe<T> {
private final HystrixInvokableInfo<?> command;
private final String methodName;
public HystrixOnSubscribe(
Observable<T> originalObservable, HystrixInvokableInfo<?> command, String methodName) {
super(originalObservable, OPERATION_NAME, tracer(), INTERNAL);
this.command = command;
this.methodName = methodName;
}
@Override
protected void decorateSpan(Span span) {
tracer().onCommand(span, command, methodName);
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.hystrix;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static io.opentelemetry.javaagent.instrumentation.hystrix.HystrixTracer.tracer;
import com.netflix.hystrix.HystrixInvokableInfo;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.rxjava.TracedOnSubscribe;
import rx.Observable;
public class HystrixOnSubscribe<T> extends TracedOnSubscribe<T> {
private static final String OPERATION_NAME = "hystrix.cmd";
private final HystrixInvokableInfo<?> command;
private final String methodName;
public HystrixOnSubscribe(
Observable<T> originalObservable, HystrixInvokableInfo<?> command, String methodName) {
super(originalObservable, OPERATION_NAME, tracer(), INTERNAL);
this.command = command;
this.methodName = methodName;
}
@Override
protected void decorateSpan(Span span) {
tracer().onCommand(span, command, methodName);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.internal.osgi;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.internal.InClassLoaderMatcher;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* The ClassLoaderMatcher's call to ClassLoader.getResource() causes the Eclipse OSGi class loader
* to "dynamically import" a bundle for the package if such a bundle is not found, which can lead to
* application failure later on due to the bundle hierarchy no longer being "consistent".
*
* <p>Any side-effect of the ClassLoaderMatcher's call to ClassLoader.getResource() is generally
* undesirable, and so this instrumentation patches the behavior and suppresses the "dynamic import"
* of the missing package/bundle when the call is originating from ClassLoaderMatcher..
*/
class EclipseOsgiInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.eclipse.osgi.internal.loader.BundleLoader");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(named("isDynamicallyImported")).and(returns(boolean.class)),
this.getClass().getName() + "$IsDynamicallyImportedAdvice");
}
public static class IsDynamicallyImportedAdvice {
// "skipOn" is used to skip execution of the instrumented method when a ClassLoaderMatcher is
// currently executing, since we will be returning false regardless in onExit below
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class, suppress = Throwable.class)
public static boolean onEnter() {
return InClassLoaderMatcher.get();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Return(readOnly = false) boolean result,
@Advice.Enter boolean inClassLoaderMatcher) {
if (inClassLoaderMatcher) {
result = false;
}
}
}
}

View File

@ -6,29 +6,12 @@
package io.opentelemetry.javaagent.instrumentation.internal.osgi;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.internal.InClassLoaderMatcher;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* The ClassLoaderMatcher's call to ClassLoader.getResource() causes the Eclipse OSGi class loader
* to "dynamically import" a bundle for the package if such a bundle is not found, which can lead to
* application failure later on due to the bundle hierarchy no longer being "consistent".
*
* <p>Any side-effect of the ClassLoaderMatcher's call to ClassLoader.getResource() is generally
* undesirable, and so this instrumentation patches the behavior and suppresses the "dynamic import"
* of the missing package/bundle when the call is originating from ClassLoaderMatcher..
*/
@AutoService(InstrumentationModule.class)
public class EclipseOsgiInstrumentationModule extends InstrumentationModule {
public EclipseOsgiInstrumentationModule() {
@ -39,38 +22,4 @@ public class EclipseOsgiInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new EclipseOsgiInstrumentation());
}
private static class EclipseOsgiInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.eclipse.osgi.internal.loader.BundleLoader");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(named("isDynamicallyImported")).and(returns(boolean.class)),
EclipseOsgiInstrumentation.class.getName() + "$IsDynamicallyImportedAdvice");
}
public static class IsDynamicallyImportedAdvice {
// "skipOn" is used to skip execution of the instrumented method when a ClassLoaderMatcher is
// currently executing, since we will be returning false regardless in onExit below
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class, suppress = Throwable.class)
public static boolean onEnter() {
return InClassLoaderMatcher.get();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Return(readOnly = false) boolean result,
@Advice.Enter boolean inClassLoaderMatcher) {
if (inClassLoaderMatcher) {
result = false;
}
}
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1;
import static io.opentelemetry.instrumentation.api.tracer.HttpServerTracer.CONTEXT_ATTRIBUTE;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1.JaxRsClientV1Tracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
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;
public class ClientHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("com.sun.jersey.api.client.ClientHandler");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("com.sun.jersey.api.client.ClientHandler"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("handle")
.and(takesArgument(0, extendsClass(named("com.sun.jersey.api.client.ClientRequest"))))
.and(returns(extendsClass(named("com.sun.jersey.api.client.ClientResponse")))),
this.getClass().getName() + "$HandleAdvice");
}
public static class HandleAdvice {
@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(0) ClientRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// WARNING: this might be a chain...so we only have to trace the first in the chain.
boolean isRootClientHandler = null == request.getProperties().get(CONTEXT_ATTRIBUTE);
Context parentContext = currentContext();
if (isRootClientHandler && tracer().shouldStartSpan(parentContext)) {
context = tracer().startSpan(parentContext, request, request);
scope = context.makeCurrent();
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Return ClientResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
tracer().end(context, response);
}
}
}
}

View File

@ -5,29 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1;
import static io.opentelemetry.instrumentation.api.tracer.HttpServerTracer.CONTEXT_ATTRIBUTE;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v1_1.JaxRsClientV1Tracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class JaxRsClientInstrumentationModule extends InstrumentationModule {
@ -40,60 +23,4 @@ public class JaxRsClientInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ClientHandlerInstrumentation());
}
public static class ClientHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("com.sun.jersey.api.client.ClientHandler");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("com.sun.jersey.api.client.ClientHandler"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("handle")
.and(takesArgument(0, extendsClass(named("com.sun.jersey.api.client.ClientRequest"))))
.and(returns(extendsClass(named("com.sun.jersey.api.client.ClientResponse")))),
JaxRsClientInstrumentationModule.class.getName() + "$HandleAdvice");
}
}
public static class HandleAdvice {
@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(0) ClientRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// WARNING: this might be a chain...so we only have to trace the first in the chain.
boolean isRootClientHandler = null == request.getProperties().get(CONTEXT_ATTRIBUTE);
Context parentContext = currentContext();
if (isRootClientHandler && tracer().shouldStartSpan(parentContext)) {
context = tracer().startSpan(parentContext, request, request);
scope = context.makeCurrent();
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Return ClientResponse response,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
tracer().end(context, response);
}
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.ws.rs.client.Client;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class ClientBuilderInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("javax.ws.rs.client.ClientBuilder");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(named("javax.ws.rs.client.ClientBuilder"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("build").and(returns(implementsInterface(named("javax.ws.rs.client.Client")))),
this.getClass().getName() + "$BuildAdvice");
}
public static class BuildAdvice {
@Advice.OnMethodExit
public static void registerFeature(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Client client) {
// Register on the generated client instead of the builder
// The build() can be called multiple times and is not thread safe
// A client is only created once
client.register(ClientTracingFeature.class);
}
}
}

View File

@ -5,23 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import javax.ws.rs.client.Client;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class JaxRsClientInstrumentationModule extends InstrumentationModule {
@ -34,35 +23,4 @@ public class JaxRsClientInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ClientBuilderInstrumentation());
}
public static class ClientBuilderInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("javax.ws.rs.client.ClientBuilder");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(named("javax.ws.rs.client.ClientBuilder"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("build").and(returns(implementsInterface(named("javax.ws.rs.client.Client")))),
JaxRsClientInstrumentationModule.class.getName() + "$ClientBuilderAdvice");
}
}
public static class ClientBuilderAdvice {
@Advice.OnMethodExit
public static void registerFeature(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Client client) {
// Register on the generated client instead of the builder
// The build() can be called multiple times and is not thread safe
// A client is only created once
client.register(ClientTracingFeature.class);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.cxf.message.Message;
public class CxfAsyncClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.cxf.jaxrs.client.JaxrsClientCallback");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("handleException")
.and(
takesArgument(0, named(Map.class.getName()))
.and(takesArgument(1, named(Throwable.class.getName())))),
this.getClass().getName() + "$HandleExceptionAdvice");
}
public static class HandleExceptionAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) Map<String, Object> map, @Advice.Argument(1) Throwable throwable) {
if (throwable != null && map instanceof Message) {
CxfClientUtil.handleException((Message) map, throwable);
}
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
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.apache.cxf.message.Message;
public class CxfClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.cxf.jaxrs.client.AbstractClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("preProcessResult").and(takesArgument(0, named("org.apache.cxf.message.Message"))),
this.getClass().getName() + "$PreProcessResultAdvice");
}
public static class PreProcessResultAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) Message message, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
CxfClientUtil.handleException(message, throwable);
}
}
}
}

View File

@ -6,19 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static java.util.Arrays.asList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.cxf.message.Message;
/**
* JAX-RS Client API doesn't define a good point where we can handle connection failures, so we must
@ -37,57 +29,4 @@ public class CxfClientInstrumentationModule extends InstrumentationModule {
new CxfClientConnectionErrorInstrumentation(),
new CxfAsyncClientConnectionErrorInstrumentation());
}
public static class CxfClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.cxf.jaxrs.client.AbstractClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("preProcessResult").and(takesArgument(0, named("org.apache.cxf.message.Message"))),
CxfClientInstrumentationModule.class.getName() + "$ErrorAdvice");
}
}
public static class CxfAsyncClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.cxf.jaxrs.client.JaxrsClientCallback");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("handleException")
.and(
takesArgument(0, named(Map.class.getName()))
.and(takesArgument(1, named(Throwable.class.getName())))),
CxfClientInstrumentationModule.class.getName() + "$AsyncErrorAdvice");
}
}
public static class ErrorAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) Message message, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
CxfClientUtil.handleException(message, throwable);
}
}
}
public static class AsyncErrorAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) Map<String, Object> map, @Advice.Argument(1) Throwable throwable) {
if (throwable != null && map instanceof Message) {
CxfClientUtil.handleException((Message) map, throwable);
}
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
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.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.glassfish.jersey.client.ClientRequest;
import org.glassfish.jersey.client.OpenTelemetryResponseCallbackWrapper;
public class JerseyClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.glassfish.jersey.client.ClientRuntime");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("invoke"))
.and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest"))),
this.getClass().getName() + "$InvokeAdvice");
transformer.applyAdviceToMethod(
isMethod()
.and(named("submit").or(named("createRunnableForAsyncProcessing")))
.and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest")))
.and(takesArgument(1, named("org.glassfish.jersey.client.ResponseCallback"))),
this.getClass().getName() + "$SubmitAdvice");
}
public static class InvokeAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) ClientRequest context, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
JerseyClientUtil.handleException(context, throwable);
}
}
}
public static class SubmitAdvice {
// using dynamic typing because parameter type is package private
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) ClientRequest context,
@Advice.Argument(value = 1, readOnly = false, typing = Assigner.Typing.DYNAMIC)
Object callback) {
callback = OpenTelemetryResponseCallbackWrapper.wrap(context, callback);
}
}
}

View File

@ -5,23 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
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 io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Collections;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.glassfish.jersey.client.ClientRequest;
import org.glassfish.jersey.client.OpenTelemetryResponseCallbackWrapper;
/**
* JAX-RS Client API doesn't define a good point where we can handle connection failures, so we must
@ -41,52 +30,6 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new JerseyClientConnectionErrorInstrumentation());
}
public static class JerseyClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.glassfish.jersey.client.ClientRuntime");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("invoke"))
.and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest"))),
JerseyClientInstrumentationModule.class.getName() + "$InvokeAdvice");
transformer.applyAdviceToMethod(
isMethod()
.and(named("submit").or(named("createRunnableForAsyncProcessing")))
.and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest")))
.and(takesArgument(1, named("org.glassfish.jersey.client.ResponseCallback"))),
JerseyClientInstrumentationModule.class.getName() + "$SubmitAdvice");
}
}
public static class InvokeAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) ClientRequest context, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
JerseyClientUtil.handleException(context, throwable);
}
}
}
public static class SubmitAdvice {
// using dynamic typing because parameter type is package private
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void handleError(
@Advice.Argument(0) ClientRequest context,
@Advice.Argument(value = 1, readOnly = false, typing = Assigner.Typing.DYNAMIC)
Object callback) {
callback = OpenTelemetryResponseCallbackWrapper.wrap(context, callback);
}
return singletonList(new JerseyClientConnectionErrorInstrumentation());
}
}

View File

@ -5,26 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorInstrumentationUtils;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.State;
import io.opentelemetry.javaagent.instrumentation.jetty.common.JettyHandlerInstrumentation;
import java.util.Arrays;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class Jetty8InstrumentationModule extends InstrumentationModule {
@ -41,42 +27,4 @@ public class Jetty8InstrumentationModule extends InstrumentationModule {
Jetty8InstrumentationModule.class.getPackage().getName() + ".Jetty8HandlerAdvice"),
new JettyQueuedThreadPoolInstrumentation());
}
public static class JettyQueuedThreadPoolInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.eclipse.jetty.util.thread.QueuedThreadPool");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("dispatch").and(takesArguments(1)).and(takesArgument(0, Runnable.class)),
Jetty8InstrumentationModule.class.getName() + "$SetExecuteRunnableStateAdvice");
}
}
public static class SetExecuteRunnableStateAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static State enterJobSubmit(
@Advice.Argument(value = 0, readOnly = false) Runnable task) {
Runnable newTask = RunnableWrapper.wrapIfNeeded(task);
if (ExecutorInstrumentationUtils.shouldAttachStateToTask(newTask)) {
task = newTask;
ContextStore<Runnable, State> contextStore =
InstrumentationContext.get(Runnable.class, State.class);
return ExecutorInstrumentationUtils.setupState(
contextStore, newTask, Java8BytecodeBridge.currentContext());
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exitJobSubmit(
@Advice.Enter State state, @Advice.Thrown Throwable throwable) {
ExecutorInstrumentationUtils.cleanUpOnMethodExit(state, throwable);
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorInstrumentationUtils;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper;
import io.opentelemetry.javaagent.instrumentation.api.concurrent.State;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class JettyQueuedThreadPoolInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.eclipse.jetty.util.thread.QueuedThreadPool");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("dispatch").and(takesArguments(1)).and(takesArgument(0, Runnable.class)),
this.getClass().getName() + "$DispatchAdvice");
}
public static class DispatchAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static State enterJobSubmit(
@Advice.Argument(value = 0, readOnly = false) Runnable task) {
Runnable newTask = RunnableWrapper.wrapIfNeeded(task);
if (ExecutorInstrumentationUtils.shouldAttachStateToTask(newTask)) {
task = newTask;
ContextStore<Runnable, State> contextStore =
InstrumentationContext.get(Runnable.class, State.class);
return ExecutorInstrumentationUtils.setupState(
contextStore, newTask, Java8BytecodeBridge.currentContext());
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exitJobSubmit(
@Advice.Enter State state, @Advice.Thrown Throwable throwable) {
ExecutorInstrumentationUtils.cleanUpOnMethodExit(state, throwable);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.kotlinxcoroutines;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import kotlin.coroutines.CoroutineContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class KotlinCoroutinesInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("kotlinx.coroutines.BuildersKt");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("launch")
.or(named("launch$default"))
.and(takesArgument(1, named("kotlin.coroutines.CoroutineContext"))),
this.getClass().getName() + "$LaunchAdvice");
transformer.applyAdviceToMethod(
named("runBlocking")
.or(named("runBlocking$default"))
.and(takesArgument(0, named("kotlin.coroutines.CoroutineContext"))),
this.getClass().getName() + "$RunBlockingAdvice");
}
public static class LaunchAdvice {
@Advice.OnMethodEnter
public static void enter(
@Advice.Argument(value = 1, readOnly = false) CoroutineContext coroutineContext) {
coroutineContext =
KotlinCoroutinesInstrumentationHelper.addOpenTelemetryContext(coroutineContext);
}
}
public static class RunBlockingAdvice {
@Advice.OnMethodEnter
public static void enter(
@Advice.Argument(value = 0, readOnly = false) CoroutineContext coroutineContext) {
coroutineContext =
KotlinCoroutinesInstrumentationHelper.addOpenTelemetryContext(coroutineContext);
}
}
}

View File

@ -6,18 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.kotlinxcoroutines;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import kotlin.coroutines.CoroutineContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class KotlinCoroutinesInstrumentationModule extends InstrumentationModule {
@ -33,45 +26,6 @@ public class KotlinCoroutinesInstrumentationModule extends InstrumentationModule
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new CoroutineScopeLaunchInstrumentation());
}
public static class CoroutineScopeLaunchInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("kotlinx.coroutines.BuildersKt");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("launch")
.or(named("launch$default"))
.and(takesArgument(1, named("kotlin.coroutines.CoroutineContext"))),
KotlinCoroutinesInstrumentationModule.class.getName() + "$LaunchAdvice");
transformer.applyAdviceToMethod(
named("runBlocking")
.or(named("runBlocking$default"))
.and(takesArgument(0, named("kotlin.coroutines.CoroutineContext"))),
KotlinCoroutinesInstrumentationModule.class.getName() + "$RunBlockingAdvice");
}
}
public static class LaunchAdvice {
@Advice.OnMethodEnter
public static void enter(
@Advice.Argument(value = 1, readOnly = false) CoroutineContext coroutineContext) {
coroutineContext =
KotlinCoroutinesInstrumentationHelper.addOpenTelemetryContext(coroutineContext);
}
}
public static class RunBlockingAdvice {
@Advice.OnMethodEnter
public static void enter(
@Advice.Argument(value = 0, readOnly = false) CoroutineContext coroutineContext) {
coroutineContext =
KotlinCoroutinesInstrumentationHelper.addOpenTelemetryContext(coroutineContext);
}
return singletonList(new KotlinCoroutinesInstrumentation());
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.kubernetesclient;
import static io.opentelemetry.javaagent.instrumentation.kubernetesclient.KubernetesClientTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.kubernetes.client.openapi.ApiCallback;
import io.kubernetes.client.openapi.ApiResponse;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import okhttp3.Call;
import okhttp3.Request;
public class ApiClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.kubernetes.client.openapi.ApiClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isPublic().and(named("buildRequest")).and(takesArguments(10)),
this.getClass().getName() + "$BuildRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("execute"))
.and(takesArguments(2))
.and(takesArgument(0, named("okhttp3.Call"))),
this.getClass().getName() + "$ExecuteAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("executeAsync"))
.and(takesArguments(3))
.and(takesArgument(0, named("okhttp3.Call")))
.and(takesArgument(2, named("io.kubernetes.client.openapi.ApiCallback"))),
this.getClass().getName() + "$ExecuteAsyncAdvice");
}
public static class BuildRequestAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.Return(readOnly = false) Request request) {
Context parentContext = Java8BytecodeBridge.currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
Request.Builder requestWithPropagation = request.newBuilder();
Context context = tracer().startSpan(parentContext, request, requestWithPropagation);
CurrentContextAndScope.set(parentContext, context);
request = requestWithPropagation.build();
}
}
public static class ExecuteAdvice {
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Return ApiResponse<?> response, @Advice.Thrown Throwable throwable) {
Context context = CurrentContextAndScope.removeAndClose();
if (context == null) {
return;
}
if (throwable == null) {
tracer().end(context, response);
} else {
tracer().endExceptionally(context, response, throwable);
}
}
}
public static class ExecuteAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Call httpCall,
@Advice.Argument(value = 2, readOnly = false) ApiCallback<?> callback,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
CurrentContextAndScope current = CurrentContextAndScope.remove();
if (current != null) {
context = current.getContext();
scope = current.getScope();
callback = new TracingApiCallback<>(callback, current.getParentContext(), context);
}
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
}
// else span will be ended in the TracingApiCallback
}
}
}

View File

@ -5,28 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.kubernetesclient;
import static io.opentelemetry.javaagent.instrumentation.kubernetesclient.KubernetesClientTracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.kubernetes.client.openapi.ApiCallback;
import io.kubernetes.client.openapi.ApiResponse;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import okhttp3.Call;
import okhttp3.Request;
@AutoService(InstrumentationModule.class)
public class KubernetesClientInstrumentationModule extends InstrumentationModule {
@ -39,95 +23,4 @@ public class KubernetesClientInstrumentationModule extends InstrumentationModule
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ApiClientInstrumentation());
}
public static class ApiClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.kubernetes.client.openapi.ApiClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isPublic().and(named("buildRequest")).and(takesArguments(10)),
KubernetesClientInstrumentationModule.class.getName() + "$BuildRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("execute"))
.and(takesArguments(2))
.and(takesArgument(0, named("okhttp3.Call"))),
KubernetesClientInstrumentationModule.class.getName() + "$ExecuteAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("executeAsync"))
.and(takesArguments(3))
.and(takesArgument(0, named("okhttp3.Call")))
.and(takesArgument(2, named("io.kubernetes.client.openapi.ApiCallback"))),
KubernetesClientInstrumentationModule.class.getName() + "$ExecuteAsyncAdvice");
}
}
public static class BuildRequestAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.Return(readOnly = false) Request request) {
Context parentContext = Java8BytecodeBridge.currentContext();
if (!tracer().shouldStartSpan(parentContext)) {
return;
}
Request.Builder requestWithPropagation = request.newBuilder();
Context context = tracer().startSpan(parentContext, request, requestWithPropagation);
CurrentContextAndScope.set(parentContext, context);
request = requestWithPropagation.build();
}
}
public static class ExecuteAdvice {
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Return ApiResponse<?> response, @Advice.Thrown Throwable throwable) {
Context context = CurrentContextAndScope.removeAndClose();
if (context == null) {
return;
}
if (throwable == null) {
tracer().end(context, response);
} else {
tracer().endExceptionally(context, response, throwable);
}
}
}
public static class ExecuteAsyncAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Call httpCall,
@Advice.Argument(value = 2, readOnly = false) ApiCallback<?> callback,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
CurrentContextAndScope current = CurrentContextAndScope.remove();
if (current != null) {
context = current.getContext();
scope = current.getScope();
callback = new TracingApiCallback<>(callback, current.getParentContext(), context);
}
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
}
// else span will be ended in the TracingApiCallback
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.lettuce.core.resource.DefaultClientResources;
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;
public class DefaultClientResourcesInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.lettuce.core.resource.DefaultClientResources");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(isStatic()).and(named("builder")),
this.getClass().getName() + "$BuilderAdvice");
}
public static class BuilderAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void methodEnter(@Advice.Return DefaultClientResources.Builder builder) {
builder.tracing(TracingHolder.TRACING);
}
}
}

View File

@ -7,19 +7,11 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.lettuce.core.resource.DefaultClientResources;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
@ -38,26 +30,4 @@ public class LettuceInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new DefaultClientResourcesInstrumentation());
}
public static class DefaultClientResourcesInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.lettuce.core.resource.DefaultClientResources");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(isStatic()).and(named("builder")),
LettuceInstrumentationModule.class.getName() + "$DefaultClientResourcesAdvice");
}
}
public static class DefaultClientResourcesAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void methodEnter(@Advice.Return DefaultClientResources.Builder builder) {
builder.tracing(TracingHolder.TRACING);
}
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.liberty;
import static io.opentelemetry.javaagent.instrumentation.liberty.LibertyHttpServerTracer.tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.asm.Advice;
@SuppressWarnings("unused")
public class LibertyHandleRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0) ServletRequest request,
@Advice.Argument(value = 1) ServletResponse response) {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// it is a bit too early to start span at this point because calling
// some methods on HttpServletRequest will give a NPE
// just remember the request and use it a bit later to start the span
ThreadLocalContext.startRequest(httpServletRequest, (HttpServletResponse) response);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Argument(0) ServletRequest servletRequest,
@Advice.Argument(1) ServletResponse servletResponse,
@Advice.Thrown Throwable throwable) {
ThreadLocalContext ctx = ThreadLocalContext.endRequest();
if (ctx == null) {
return;
}
Context context = ctx.getContext();
Scope scope = ctx.getScope();
if (scope == null) {
return;
}
scope.close();
if (context == null) {
// an existing span was found
return;
}
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
tracer().setPrincipal(context, request);
if (throwable != null) {
tracer().endExceptionally(context, throwable, response);
return;
}
if (ServletAndFilterAdviceHelper.mustEndOnHandlerMethodExit(tracer(), request)) {
tracer().end(context, response);
}
}
}

View File

@ -6,26 +6,23 @@
package io.opentelemetry.javaagent.instrumentation.liberty;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* Instrumenting request handling in Liberty.
*
* <ul>
* <li>On entry to WebApp.handleRequest remember request. {@link LibertyHandleRequestAdvice}
* <li>On entry to WebApp.handleRequest remember request. {@link
* LibertyWebAppInstrumentation.HandleRequestAdvice}
* <li>On call to WebApp.isForbidden (called from WebApp.handleRequest) start span based on
* remembered request. We don't start span immediately at the start or handleRequest because
* HttpServletRequest isn't usable yet. {@link LibertyStartSpanAdvice}
* <li>On exit from WebApp.handleRequest close the span. {@link LibertyHandleRequestAdvice}
* HttpServletRequest isn't usable yet. {@link LibertyWebAppInstrumentation.IsForbiddenAdvice}
* <li>On exit from WebApp.handleRequest close the span. {@link
* LibertyWebAppInstrumentation.HandleRequestAdvice}
* </ul>
*/
@AutoService(InstrumentationModule.class)
@ -37,30 +34,6 @@ public class LibertyInstrumentationModule extends InstrumentationModule {
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new WebAppInstrumentation());
}
public static class WebAppInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.ibm.ws.webcontainer.webapp.WebApp");
}
@Override
public void transform(TypeTransformer transformer) {
// https://github.com/OpenLiberty/open-liberty/blob/master/dev/com.ibm.ws.webcontainer/src/com/ibm/ws/webcontainer/webapp/WebApp.java
transformer.applyAdviceToMethod(
named("handleRequest")
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
.and(takesArgument(2, named("com.ibm.wsspi.http.HttpInboundConnection"))),
LibertyHandleRequestAdvice.class.getName());
// isForbidden is called from handleRequest
transformer.applyAdviceToMethod(
named("isForbidden").and(takesArgument(0, named(String.class.getName()))),
LibertyStartSpanAdvice.class.getName());
}
return singletonList(new LibertyWebAppInstrumentation());
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.liberty;
import static io.opentelemetry.javaagent.instrumentation.liberty.LibertyHttpServerTracer.tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice;
@SuppressWarnings("unused")
public class LibertyStartSpanAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
ThreadLocalContext ctx = ThreadLocalContext.get();
if (ctx == null || !ctx.startSpan()) {
return;
}
Context context = tracer().startSpan(ctx.getRequest());
Scope scope = context.makeCurrent();
ctx.setContext(context);
ctx.setScope(scope);
// Must be set here since Liberty RequestProcessors can use startAsync outside of servlet scope.
tracer().setAsyncListenerResponse(ctx.getRequest(), ctx.getResponse());
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.liberty;
import static io.opentelemetry.javaagent.instrumentation.liberty.LibertyHttpServerTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class LibertyWebAppInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.ibm.ws.webcontainer.webapp.WebApp");
}
@Override
public void transform(TypeTransformer transformer) {
// https://github.com/OpenLiberty/open-liberty/blob/master/dev/com.ibm.ws.webcontainer/src/com/ibm/ws/webcontainer/webapp/WebApp.java
transformer.applyAdviceToMethod(
named("handleRequest")
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
.and(takesArgument(2, named("com.ibm.wsspi.http.HttpInboundConnection"))),
this.getClass().getName() + "$HandleRequestAdvice");
// isForbidden is called from handleRequest
transformer.applyAdviceToMethod(
named("isForbidden").and(takesArgument(0, named(String.class.getName()))),
this.getClass().getName() + "$IsForbiddenAdvice");
}
@SuppressWarnings("unused")
public static class HandleRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0) ServletRequest request,
@Advice.Argument(value = 1) ServletResponse response) {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
return;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// it is a bit too early to start span at this point because calling
// some methods on HttpServletRequest will give a NPE
// just remember the request and use it a bit later to start the span
ThreadLocalContext.startRequest(httpServletRequest, (HttpServletResponse) response);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Argument(0) ServletRequest servletRequest,
@Advice.Argument(1) ServletResponse servletResponse,
@Advice.Thrown Throwable throwable) {
ThreadLocalContext ctx = ThreadLocalContext.endRequest();
if (ctx == null) {
return;
}
Context context = ctx.getContext();
Scope scope = ctx.getScope();
if (scope == null) {
return;
}
scope.close();
if (context == null) {
// an existing span was found
return;
}
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
tracer().setPrincipal(context, request);
if (throwable != null) {
tracer().endExceptionally(context, throwable, response);
return;
}
if (ServletAndFilterAdviceHelper.mustEndOnHandlerMethodExit(tracer(), request)) {
tracer().end(context, response);
}
}
}
@SuppressWarnings("unused")
public static class IsForbiddenAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
ThreadLocalContext ctx = ThreadLocalContext.get();
if (ctx == null || !ctx.startSpan()) {
return;
}
Context context = tracer().startSpan(ctx.getRequest());
Scope scope = context.makeCurrent();
ctx.setContext(context);
ctx.setScope(scope);
// Must be set here since Liberty RequestProcessors can use startAsync outside of servlet
// scope.
tracer().setAsyncListenerResponse(ctx.getRequest(), ctx.getResponse());
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.log4j.v2_7;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
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.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.logging.log4j.core.ContextDataInjector;
public class ContextDataInjectorFactoryInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.logging.log4j.core.impl.ContextDataInjectorFactory");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(isStatic())
.and(named("createInjector"))
.and(returns(named("org.apache.logging.log4j.core.ContextDataInjector"))),
this.getClass().getName() + "$CreateInjectorAdvice");
}
public static class CreateInjectorAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false)
ContextDataInjector injector) {
injector = new SpanDecoratingContextDataInjector(injector);
}
}
}

View File

@ -7,23 +7,13 @@ package io.opentelemetry.javaagent.instrumentation.log4j.v2_7;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.logging.log4j.core.ContextDataInjector;
@AutoService(InstrumentationModule.class)
public class Log4j27InstrumentationModule extends InstrumentationModule {
@ -41,30 +31,4 @@ public class Log4j27InstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ContextDataInjectorFactoryInstrumentation());
}
public static class ContextDataInjectorFactoryInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.logging.log4j.core.impl.ContextDataInjectorFactory");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(isStatic())
.and(named("createInjector"))
.and(returns(named("org.apache.logging.log4j.core.ContextDataInjector"))),
Log4j27InstrumentationModule.class.getName() + "$CreateInjectorAdvice");
}
}
public static class CreateInjectorAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(
@Advice.Return(typing = Typing.DYNAMIC, readOnly = false) ContextDataInjector injector) {
injector = new SpanDecoratingContextDataInjector(injector);
}
}
}

View File

@ -6,19 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.okhttp.v2_2;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class OkHttp2InstrumentationModule extends InstrumentationModule {
@ -30,30 +22,4 @@ public class OkHttp2InstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new OkHttpClientInstrumentation());
}
public static class OkHttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.squareup.okhttp.OkHttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), OkHttp2InstrumentationModule.class.getName() + "$OkHttp2ClientAdvice");
}
}
public static class OkHttp2ClientAdvice {
@Advice.OnMethodExit
public static void addTracingInterceptor(@Advice.This OkHttpClient client) {
for (Interceptor interceptor : client.interceptors()) {
if (interceptor instanceof TracingInterceptor) {
return;
}
}
client.interceptors().add(new TracingInterceptor());
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.okhttp.v2_2;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
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;
public class OkHttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.squareup.okhttp.OkHttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), this.getClass().getName() + "$ConstructorAdvice");
}
public static class ConstructorAdvice {
@Advice.OnMethodExit
public static void addTracingInterceptor(@Advice.This OkHttpClient client) {
for (Interceptor interceptor : client.interceptors()) {
if (interceptor instanceof TracingInterceptor) {
return;
}
}
client.interceptors().add(new TracingInterceptor());
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import okhttp3.OkHttpClient;
public class OkHttp3Instrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("okhttp3.OkHttpClient$Builder");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), this.getClass().getName() + "$ConstructorAdvice");
}
public static class ConstructorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void trackCallDepth(@Advice.Local("callDepth") int callDepth) {
callDepth = CallDepthThreadLocalMap.incrementCallDepth(OkHttpClient.Builder.class);
}
@Advice.OnMethodExit(suppress = Throwable.class)
public static void addTracingInterceptor(
@Advice.This OkHttpClient.Builder builder, @Advice.Local("callDepth") int callDepth) {
// No-args constructor is automatically called by constructors with args, but we only want to
// run once from the constructor with args because that is where the dedupe needs to happen.
if (callDepth > 0) {
return;
}
CallDepthThreadLocalMap.reset(OkHttpClient.Builder.class);
if (builder.interceptors().contains(OkHttp3Interceptors.TRACING_INTERCEPTOR)) {
return;
}
builder.addInterceptor(OkHttp3Interceptors.TRACING_INTERCEPTOR);
}
}
}

View File

@ -6,19 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import okhttp3.OkHttpClient;
@AutoService(InstrumentationModule.class)
public class OkHttp3InstrumentationModule extends InstrumentationModule {
@ -29,41 +21,6 @@ public class OkHttp3InstrumentationModule extends InstrumentationModule {
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new OkHttpClientInstrumentation());
}
public static class OkHttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("okhttp3.OkHttpClient$Builder");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), OkHttp3InstrumentationModule.class.getName() + "$OkHttp3Advice");
}
}
public static class OkHttp3Advice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void trackCallDepth(@Advice.Local("callDepth") int callDepth) {
callDepth = CallDepthThreadLocalMap.incrementCallDepth(OkHttpClient.Builder.class);
}
@Advice.OnMethodExit(suppress = Throwable.class)
public static void addTracingInterceptor(
@Advice.This OkHttpClient.Builder builder, @Advice.Local("callDepth") int callDepth) {
// No-args constructor is automatically called by constructors with args, but we only want to
// run once from the constructor with args because that is where the dedupe needs to happen.
if (callDepth > 0) {
return;
}
CallDepthThreadLocalMap.reset(OkHttpClient.Builder.class);
if (builder.interceptors().contains(OkHttp3Interceptors.TRACING_INTERCEPTOR)) {
return;
}
builder.addInterceptor(OkHttp3Interceptors.TRACING_INTERCEPTOR);
}
return singletonList(new OkHttp3Instrumentation());
}
}

View File

@ -5,22 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.oshi;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class OshiInstrumentationModule extends InstrumentationModule {
@ -33,30 +23,4 @@ public class OshiInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new SystemInfoInstrumentation());
}
public static class SystemInfoInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("oshi.SystemInfo");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("oshi.SystemInfo");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(isStatic()).and(named("getCurrentPlatformEnum")),
OshiInstrumentationModule.class.getName() + "$OshiInstrumentationAdvice");
}
}
public static class OshiInstrumentationAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
SystemMetrics.registerObservers();
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.oshi;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
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;
public class SystemInfoInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("oshi.SystemInfo");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("oshi.SystemInfo");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(isStatic()).and(named("getCurrentPlatformEnum")),
this.getClass().getName() + "$GetCurrentPlatformEnumAdvice");
}
public static class GetCurrentPlatformEnumAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
SystemMetrics.registerObservers();
}
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.PlayTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import play.api.mvc.Action;
import play.api.mvc.Headers;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;
public class ActionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("play.api.mvc.Action");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("play.api.mvc.Action"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("apply")
.and(takesArgument(0, named("play.api.mvc.Request")))
.and(returns(named("scala.concurrent.Future"))),
this.getClass().getName() + "$ApplyAdvice");
}
public static class ApplyAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Request<?> req,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan("play.request", SpanKind.INTERNAL);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object thisAction,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated.
tracer().updateSpanName(Java8BytecodeBridge.spanFromContext(context), req);
// set the span name on the upstream akka/netty span
tracer().updateSpanName(ServerSpan.fromContextOrNull(context), req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) {
responseFuture.onComplete(
new RequestCompleteCallback(context), ((Action<?>) thisAction).executionContext());
} else {
tracer().endExceptionally(context, throwable);
}
}
// Unused method for muzzle
public static void muzzleCheck(Headers headers) {
// This distinguishes between 2.3 and 2.4, excluding the former
headers.get("aKey");
// system() method was removed in 2.6, so this line prevents from applying in play 2.6
play.libs.Akka.system();
}
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
import static io.opentelemetry.javaagent.instrumentation.play.v2_4.PlayTracer.tracer;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import play.api.mvc.Action;
import play.api.mvc.Headers;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;
public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Request<?> req,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan("play.request", SpanKind.INTERNAL);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object thisAction,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated.
tracer().updateSpanName(Java8BytecodeBridge.spanFromContext(context), req);
// set the span name on the upstream akka/netty span
tracer().updateSpanName(ServerSpan.fromContextOrNull(context), req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) {
responseFuture.onComplete(
new RequestCompleteCallback(context), ((Action<?>) thisAction).executionContext());
} else {
tracer().endExceptionally(context, throwable);
}
}
// Unused method for muzzle
public static void muzzleCheck(Headers headers) {
// This distinguishes between 2.3 and 2.4, excluding the former
headers.get("aKey");
// system() method was removed in 2.6, so this line prevents from applying in play 2.6
play.libs.Akka.system();
}
}

View File

@ -5,20 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.play.v2_4;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class PlayInstrumentationModule extends InstrumentationModule {
@ -31,25 +23,4 @@ public class PlayInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ActionInstrumentation());
}
public static class ActionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("play.api.mvc.Action");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("play.api.mvc.Action"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("apply")
.and(takesArgument(0, named("play.api.mvc.Request")))
.and(returns(named("scala.concurrent.Future"))),
PlayAdvice.class.getName());
}
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.play.v2_6;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.play.v2_6.PlayTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import play.api.mvc.Action;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;
public class ActionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("play.api.mvc.Action");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("play.api.mvc.Action"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("apply")
.and(takesArgument(0, named("play.api.mvc.Request")))
.and(returns(named("scala.concurrent.Future"))),
this.getClass().getName() + "$ApplyAdvice");
}
public static class ApplyAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Request<?> req,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan("play.request", SpanKind.INTERNAL);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object thisAction,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated.
tracer().updateSpanName(Java8BytecodeBridge.spanFromContext(context), req);
// set the span name on the upstream akka/netty span
tracer().updateSpanName(ServerSpan.fromContextOrNull(context), req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) {
responseFuture.onComplete(
new RequestCompleteCallback(context), ((Action<?>) thisAction).executionContext());
} else {
tracer().endExceptionally(context, throwable);
}
}
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.play.v2_6;
import static io.opentelemetry.javaagent.instrumentation.play.v2_6.PlayTracer.tracer;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import play.api.mvc.Action;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;
public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) Request<?> req,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan("play.request", SpanKind.INTERNAL);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse(
@Advice.This Object thisAction,
@Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated.
tracer().updateSpanName(Java8BytecodeBridge.spanFromContext(context), req);
// set the span name on the upstream akka/netty span
tracer().updateSpanName(ServerSpan.fromContextOrNull(context), req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) {
responseFuture.onComplete(
new RequestCompleteCallback(context), ((Action<?>) thisAction).executionContext());
} else {
tracer().endExceptionally(context, throwable);
}
}
}

View File

@ -5,20 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.play.v2_6;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class PlayInstrumentationModule extends InstrumentationModule {
@ -31,25 +23,4 @@ public class PlayInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ActionInstrumentation());
}
public static class ActionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("play.api.mvc.Action");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("play.api.mvc.Action"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("apply")
.and(takesArgument(0, named("play.api.mvc.Request")))
.and(returns(named("scala.concurrent.Future"))),
PlayAdvice.class.getName());
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactor;
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.instrumentation.reactor.TracingOperator;
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;
public class HooksInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.core.publisher.Hooks");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isTypeInitializer().or(named("resetOnEachOperator")),
this.getClass().getName() + "$ResetOnEachOperatorAdvice");
}
public static class ResetOnEachOperatorAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void postStaticInitializer() {
TracingOperator.registerOnEachOperator();
}
}
}

View File

@ -1,16 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactor;
import io.opentelemetry.instrumentation.reactor.TracingOperator;
import net.bytebuddy.asm.Advice;
public class ReactorHooksAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void postStaticInitializer() {
TracingOperator.registerOnEachOperator();
}
}

View File

@ -6,16 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.reactor;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class ReactorInstrumentationModule extends InstrumentationModule {
@ -28,17 +23,4 @@ public class ReactorInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HooksInstrumentation());
}
public static class HooksInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.core.publisher.Hooks");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isTypeInitializer().or(named("resetOnEachOperator")), ReactorHooksAdvice.class.getName());
}
}
}

View File

@ -136,8 +136,7 @@ public final class DecoratorFunctions {
}
}
// otherwise use the parent span context
return reactorContext.getOrDefault(
ReactorNettyInstrumentationModule.MapConnect.CONTEXT_ATTRIBUTE, null);
return reactorContext.getOrDefault(MapConnect.CONTEXT_ATTRIBUTE, null);
}
private DecoratorFunctions() {}

View File

@ -0,0 +1,151 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v0_9;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import java.util.function.BiConsumer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
public class HttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.netty.http.client.HttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isStatic().and(namedOneOf("create", "newConnection", "from")),
this.getClass().getName() + "$CreateAdvice");
// advice classes below expose current context in doOn*/doAfter* callbacks
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnRequest", "doAfterRequest"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnRequestError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnRequestErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnResponse", "doAfterResponse"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnResponseAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnResponseError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnResponseErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnError"))
.and(takesArguments(2))
.and(takesArgument(0, BiConsumer.class))
.and(takesArgument(1, BiConsumer.class)),
this.getClass().getName() + "$OnErrorAdvice");
}
public static class CreateAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable, @Advice.Return(readOnly = false) HttpClient client) {
if (CallDepthThreadLocalMap.decrementCallDepth(HttpClient.class) == 0 && throwable == null) {
client = client.doOnRequest(new OnRequest()).mapConnect(new MapConnect());
}
}
}
public static class OnRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnRequestDecorator(callback);
}
}
}
public static class OnRequestErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnRequestErrorDecorator(callback);
}
}
}
public static class OnResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Connection> callback,
@Advice.Origin("#m") String methodName) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
boolean forceParentContext = methodName.equals("doAfterResponse");
callback = new DecoratorFunctions.OnResponseDecorator(callback, forceParentContext);
}
}
}
public static class OnResponseErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnResponseErrorDecorator(callback);
}
}
}
public static class OnErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> requestCallback,
@Advice.Argument(value = 1, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> responseCallback) {
if (DecoratorFunctions.shouldDecorate(requestCallback.getClass())) {
requestCallback = new DecoratorFunctions.OnRequestErrorDecorator(requestCallback);
}
if (DecoratorFunctions.shouldDecorate(responseCallback.getClass())) {
responseCallback = new DecoratorFunctions.OnResponseErrorDecorator(responseCallback);
}
}
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v0_9;
import io.netty.bootstrap.Bootstrap;
import io.opentelemetry.context.Context;
import java.util.function.BiFunction;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
public class MapConnect
implements BiFunction<Mono<? extends Connection>, Bootstrap, Mono<? extends Connection>> {
static final String CONTEXT_ATTRIBUTE = MapConnect.class.getName() + ".Context";
@Override
public Mono<? extends Connection> apply(Mono<? extends Connection> m, Bootstrap b) {
return m.subscriberContext(s -> s.put(CONTEXT_ATTRIBUTE, Context.current()));
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v0_9;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.netty.v4_1.AttributeKeys;
import java.util.function.BiConsumer;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClientRequest;
public class OnRequest implements BiConsumer<HttpClientRequest, Connection> {
@Override
public void accept(HttpClientRequest r, Connection c) {
Context context = r.currentContext().get(MapConnect.CONTEXT_ATTRIBUTE);
c.channel().attr(AttributeKeys.WRITE_CONTEXT).set(context);
}
}

View File

@ -7,32 +7,15 @@ package io.opentelemetry.javaagent.instrumentation.reactornetty.v0_9;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.netty.bootstrap.Bootstrap;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.netty.v4_1.AttributeKeys;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
/**
* This instrumentation solves the problem of the correct context propagation through the roller
@ -57,149 +40,4 @@ public class ReactorNettyInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpClientInstrumentation());
}
public static class HttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.netty.http.client.HttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isStatic().and(namedOneOf("create", "newConnection", "from")),
ReactorNettyInstrumentationModule.class.getName() + "$CreateAdvice");
// advice classes below expose current context in doOn*/doAfter* callbacks
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnRequest", "doAfterRequest"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnRequestError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnRequestErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnResponse", "doAfterResponse"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnResponseAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnResponseError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnResponseErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnError"))
.and(takesArguments(2))
.and(takesArgument(0, BiConsumer.class))
.and(takesArgument(1, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnErrorAdvice");
}
}
public static class CreateAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable, @Advice.Return(readOnly = false) HttpClient client) {
if (CallDepthThreadLocalMap.decrementCallDepth(HttpClient.class) == 0 && throwable == null) {
client = client.doOnRequest(new OnRequest()).mapConnect(new MapConnect());
}
}
}
public static class MapConnect
implements BiFunction<Mono<? extends Connection>, Bootstrap, Mono<? extends Connection>> {
static final String CONTEXT_ATTRIBUTE = MapConnect.class.getName() + ".Context";
@Override
public Mono<? extends Connection> apply(Mono<? extends Connection> m, Bootstrap b) {
return m.subscriberContext(s -> s.put(CONTEXT_ATTRIBUTE, Context.current()));
}
}
public static class OnRequest implements BiConsumer<HttpClientRequest, Connection> {
@Override
public void accept(HttpClientRequest r, Connection c) {
Context context = r.currentContext().get(MapConnect.CONTEXT_ATTRIBUTE);
c.channel().attr(AttributeKeys.WRITE_CONTEXT).set(context);
}
}
public static class OnRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnRequestDecorator(callback);
}
}
}
public static class OnRequestErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnRequestErrorDecorator(callback);
}
}
}
public static class OnResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Connection> callback,
@Advice.Origin("#m") String methodName) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
boolean forceParentContext = methodName.equals("doAfterResponse");
callback = new DecoratorFunctions.OnResponseDecorator(callback, forceParentContext);
}
}
}
public static class OnResponseErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnResponseErrorDecorator(callback);
}
}
}
public static class OnErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> requestCallback,
@Advice.Argument(value = 1, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> responseCallback) {
if (DecoratorFunctions.shouldDecorate(requestCallback.getClass())) {
requestCallback = new DecoratorFunctions.OnRequestErrorDecorator(requestCallback);
}
if (DecoratorFunctions.shouldDecorate(responseCallback.getClass())) {
responseCallback = new DecoratorFunctions.OnResponseErrorDecorator(responseCallback);
}
}
}
}

View File

@ -74,8 +74,7 @@ public final class DecoratorFunctions {
}
}
// otherwise use the parent span context
return contextView.getOrDefault(
ReactorNettyInstrumentationModule.MapConnect.CONTEXT_ATTRIBUTE, null);
return contextView.getOrDefault(MapConnect.CONTEXT_ATTRIBUTE, null);
}
private DecoratorFunctions() {}

View File

@ -0,0 +1,149 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import java.util.function.BiConsumer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
public class HttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.netty.http.client.HttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isStatic().and(namedOneOf("create", "newConnection", "from")),
this.getClass().getName() + "$CreateAdvice");
// advice classes below expose current context in doOn*/doAfter* callbacks
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnRequest", "doAfterRequest"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnRequestError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnRequestErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnResponse", "doAfterResponseSuccess", "doOnRedirect"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnResponseAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnResponseError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
this.getClass().getName() + "$OnResponseErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnError"))
.and(takesArguments(2))
.and(takesArgument(0, BiConsumer.class))
.and(takesArgument(1, BiConsumer.class)),
this.getClass().getName() + "$OnErrorAdvice");
}
public static class CreateAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable, @Advice.Return(readOnly = false) HttpClient client) {
if (CallDepthThreadLocalMap.decrementCallDepth(HttpClient.class) == 0 && throwable == null) {
client = client.doOnRequest(new OnRequest()).mapConnect(new MapConnect());
}
}
}
public static class OnRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageDecorator<>(callback);
}
}
}
public static class OnRequestErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageErrorDecorator<>(callback);
}
}
}
public static class OnResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageDecorator<>(callback);
}
}
}
public static class OnResponseErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageErrorDecorator<>(callback);
}
}
}
public static class OnErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> requestCallback,
@Advice.Argument(value = 1, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> responseCallback) {
if (DecoratorFunctions.shouldDecorate(requestCallback.getClass())) {
requestCallback = new DecoratorFunctions.OnMessageErrorDecorator<>(requestCallback);
}
if (DecoratorFunctions.shouldDecorate(responseCallback.getClass())) {
responseCallback = new DecoratorFunctions.OnMessageErrorDecorator<>(responseCallback);
}
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import io.opentelemetry.context.Context;
import java.util.function.Function;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
public class MapConnect
implements Function<Mono<? extends Connection>, Mono<? extends Connection>> {
static final String CONTEXT_ATTRIBUTE = MapConnect.class.getName() + ".Context";
@Override
public Mono<? extends Connection> apply(Mono<? extends Connection> m) {
return m.contextWrite(s -> s.put(CONTEXT_ATTRIBUTE, Context.current()));
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.netty.v4_1.AttributeKeys;
import java.util.function.BiConsumer;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClientRequest;
public class OnRequest implements BiConsumer<HttpClientRequest, Connection> {
@Override
public void accept(HttpClientRequest r, Connection c) {
Context context = r.currentContextView().get(MapConnect.CONTEXT_ATTRIBUTE);
c.channel().attr(AttributeKeys.WRITE_CONTEXT).set(context);
}
}

View File

@ -7,31 +7,15 @@ package io.opentelemetry.javaagent.instrumentation.reactornetty.v1_0;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.netty.v4_1.AttributeKeys;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
/**
* This instrumentation solves the problem of the correct context propagation through the roller
@ -56,147 +40,4 @@ public class ReactorNettyInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpClientInstrumentation());
}
public static class HttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("reactor.netty.http.client.HttpClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isStatic().and(namedOneOf("create", "newConnection", "from")),
ReactorNettyInstrumentationModule.class.getName() + "$CreateAdvice");
// advice classes below expose current context in doOn*/doAfter* callbacks
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnRequest", "doAfterRequest"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnRequestAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnRequestError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnRequestErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(namedOneOf("doOnResponse", "doAfterResponseSuccess", "doOnRedirect"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnResponseAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnResponseError"))
.and(takesArguments(1))
.and(takesArgument(0, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnResponseErrorAdvice");
transformer.applyAdviceToMethod(
isPublic()
.and(named("doOnError"))
.and(takesArguments(2))
.and(takesArgument(0, BiConsumer.class))
.and(takesArgument(1, BiConsumer.class)),
ReactorNettyInstrumentationModule.class.getName() + "$OnErrorAdvice");
}
}
public static class CreateAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable, @Advice.Return(readOnly = false) HttpClient client) {
if (CallDepthThreadLocalMap.decrementCallDepth(HttpClient.class) == 0 && throwable == null) {
client = client.doOnRequest(new OnRequest()).mapConnect(new MapConnect());
}
}
}
public static class MapConnect
implements Function<Mono<? extends Connection>, Mono<? extends Connection>> {
static final String CONTEXT_ATTRIBUTE = MapConnect.class.getName() + ".Context";
@Override
public Mono<? extends Connection> apply(Mono<? extends Connection> m) {
return m.contextWrite(s -> s.put(CONTEXT_ATTRIBUTE, Context.current()));
}
}
public static class OnRequest implements BiConsumer<HttpClientRequest, Connection> {
@Override
public void accept(HttpClientRequest r, Connection c) {
Context context = r.currentContextView().get(MapConnect.CONTEXT_ATTRIBUTE);
c.channel().attr(AttributeKeys.WRITE_CONTEXT).set(context);
}
}
public static class OnRequestAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageDecorator<>(callback);
}
}
}
public static class OnRequestErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageErrorDecorator<>(callback);
}
}
}
public static class OnResponseAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Connection> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageDecorator<>(callback);
}
}
}
public static class OnResponseErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> callback) {
if (DecoratorFunctions.shouldDecorate(callback.getClass())) {
callback = new DecoratorFunctions.OnMessageErrorDecorator<>(callback);
}
}
}
public static class OnErrorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(value = 0, readOnly = false)
BiConsumer<? super HttpClientRequest, ? super Throwable> requestCallback,
@Advice.Argument(value = 1, readOnly = false)
BiConsumer<? super HttpClientResponse, ? super Throwable> responseCallback) {
if (DecoratorFunctions.shouldDecorate(requestCallback.getClass())) {
requestCallback = new DecoratorFunctions.OnMessageErrorDecorator<>(requestCallback);
}
if (DecoratorFunctions.shouldDecorate(responseCallback.getClass())) {
responseCallback = new DecoratorFunctions.OnMessageErrorDecorator<>(responseCallback);
}
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.rediscala;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer;
import io.opentelemetry.context.Context;
import scala.runtime.AbstractFunction1;
import scala.util.Try;
public class OnCompleteHandler extends AbstractFunction1<Try<Object>, Void> {
private final Context context;
public OnCompleteHandler(Context context) {
this.context = context;
}
@Override
public Void apply(Try<Object> result) {
if (result.isFailure()) {
tracer().endExceptionally(context, result.failed().get());
} else {
tracer().end(context);
}
return null;
}
}

View File

@ -5,33 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.rediscala;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.safeHasSuperType;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import redis.RedisCommand;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.runtime.AbstractFunction1;
import scala.util.Try;
@AutoService(InstrumentationModule.class)
public class RediscalaInstrumentationModule extends InstrumentationModule {
@ -44,78 +23,4 @@ public class RediscalaInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new RequestInstrumentation());
}
public static class RequestInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("redis.Request");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(
namedOneOf(
"redis.ActorRequest",
"redis.Request",
"redis.BufferedRequest",
"redis.RoundRobinPoolRequest"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("send"))
.and(takesArgument(0, named("redis.RedisCommand")))
.and(returns(named("scala.concurrent.Future"))),
RediscalaInstrumentationModule.class.getName() + "$RediscalaAdvice");
}
}
public static class RediscalaAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) RedisCommand<?, ?> cmd,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan(currentContext(), cmd, cmd);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable,
@Advice.FieldValue("executionContext") ExecutionContext ctx,
@Advice.Return(readOnly = false) Future<Object> responseFuture) {
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
responseFuture.onComplete(new OnCompleteHandler(context), ctx);
}
}
}
public static class OnCompleteHandler extends AbstractFunction1<Try<Object>, Void> {
private final Context context;
public OnCompleteHandler(Context context) {
this.context = context;
}
@Override
public Void apply(Try<Object> result) {
if (result.isFailure()) {
tracer().endExceptionally(context, result.failed().get());
} else {
tracer().end(context);
}
return null;
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.rediscala;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.safeHasSuperType;
import static io.opentelemetry.javaagent.extension.matcher.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.NameMatchers.namedOneOf;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
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 redis.RedisCommand;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
public class RequestInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("redis.Request");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(
namedOneOf(
"redis.ActorRequest",
"redis.Request",
"redis.BufferedRequest",
"redis.RoundRobinPoolRequest"));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("send"))
.and(takesArgument(0, named("redis.RedisCommand")))
.and(returns(named("scala.concurrent.Future"))),
this.getClass().getName() + "$SendAdvice");
}
public static class SendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) RedisCommand<?, ?> cmd,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan(currentContext(), cmd, cmd);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable,
@Advice.FieldValue("executionContext") ExecutionContext ctx,
@Advice.Return(readOnly = false) Future<Object> responseFuture) {
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
responseFuture.onComplete(new OnCompleteHandler(context), ctx);
}
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.redisson;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.redisson.RedissonInstrumenters.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.net.InetSocketAddress;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.redisson.client.RedisConnection;
public class RedisConnectionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.redisson.client.RedisConnection");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(named("send")), this.getClass().getName() + "$SendAdvice");
}
public static class SendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This RedisConnection connection,
@Advice.Argument(0) Object arg,
@Advice.Local("otelRedissonRequest") RedissonRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
InetSocketAddress remoteAddress = (InetSocketAddress) connection.getChannel().remoteAddress();
request = RedissonRequest.create(remoteAddress, arg);
if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRedissonRequest") RedissonRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
scope.close();
instrumenter().end(context, request, null, throwable);
}
}
}

View File

@ -5,24 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.redisson;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.redisson.RedissonInstrumenters.instrumenter;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.net.InetSocketAddress;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.redisson.client.RedisConnection;
@AutoService(InstrumentationModule.class)
public class RedissonInstrumentationModule extends InstrumentationModule {
@ -35,49 +23,4 @@ public class RedissonInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new RedisConnectionInstrumentation());
}
public static class RedisConnectionInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.redisson.client.RedisConnection");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod().and(named("send")),
RedissonInstrumentationModule.class.getName() + "$RedissonAdvice");
}
}
public static class RedissonAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This RedisConnection connection,
@Advice.Argument(0) Object arg,
@Advice.Local("otelRedissonRequest") RedissonRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
InetSocketAddress remoteAddress = (InetSocketAddress) connection.getChannel().remoteAddress();
request = RedissonRequest.create(remoteAddress, arg);
if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRedissonRequest") RedissonRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
scope.close();
instrumenter().end(context, request, null, throwable);
}
}
}