Remove deprecated SpanWithScope class (#1834)

This commit is contained in:
Mateusz Rzeszutek 2020-12-05 18:48:28 +01:00 committed by GitHub
parent ef02da9090
commit f520c2cd33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 451 additions and 428 deletions

View File

@ -18,8 +18,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.dropwizard.views.View; import io.dropwizard.views.View;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.InstrumentationModule; import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.List; import java.util.List;
@ -65,27 +65,30 @@ public class DropwizardViewsInstrumentationModule extends InstrumentationModule
public static class RenderAdvice { public static class RenderAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) View view) { public static void onEnter(
if (!Java8BytecodeBridge.currentSpan().getSpanContext().isValid()) { @Advice.Argument(0) View view,
return null; @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (Java8BytecodeBridge.currentSpan().getSpanContext().isValid()) {
span = tracer().startSpan("Render " + view.getTemplateName());
scope = span.makeCurrent();
} }
Span span = tracer().startSpan("Render " + view.getTemplateName());
return new SpanWithScope(span, span.makeCurrent());
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
if (spanWithScope == null) { @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return; return;
} }
Span span = spanWithScope.getSpan(); scope.close();
if (throwable == null) { if (throwable == null) {
tracer().end(span); tracer().end(span);
} else { } else {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
} }
spanWithScope.closeScope();
} }
} }
} }

View File

@ -22,8 +22,8 @@ import com.twitter.finatra.http.contexts.RouteInfo;
import com.twitter.util.Future; import com.twitter.util.Future;
import com.twitter.util.FutureEventListener; import com.twitter.util.FutureEventListener;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.InstrumentationModule; import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.List; import java.util.List;
@ -71,60 +71,61 @@ public class FinatraInstrumentationModule extends InstrumentationModule {
public static class RouteAdvice { public static class RouteAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope nameSpan( public static void nameSpan(
@Advice.FieldValue("routeInfo") RouteInfo routeInfo, @Advice.FieldValue("routeInfo") RouteInfo routeInfo,
@Advice.FieldValue("clazz") Class<?> clazz) { @Advice.FieldValue("clazz") Class<?> clazz,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
Span serverSpan = BaseTracer.getCurrentServerSpan(); Span serverSpan = BaseTracer.getCurrentServerSpan();
if (serverSpan != null) { if (serverSpan != null) {
serverSpan.updateName(routeInfo.path()); serverSpan.updateName(routeInfo.path());
} }
Span span = tracer().startSpan(clazz); span = tracer().startSpan(clazz);
scope = span.makeCurrent();
return new SpanWithScope(span, span.makeCurrent());
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void setupCallback( public static void setupCallback(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return Some<Future<Response>> responseOption) { @Advice.Return Some<Future<Response>> responseOption,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (spanWithScope == null) { if (scope == null) {
return; return;
} }
Span span = spanWithScope.getSpan();
if (throwable != null) { if (throwable != null) {
scope.close();
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
spanWithScope.closeScope();
return; return;
} }
responseOption.get().addEventListener(new Listener(spanWithScope)); responseOption.get().addEventListener(new Listener(span, scope));
} }
} }
public static class Listener implements FutureEventListener<Response> { public static class Listener implements FutureEventListener<Response> {
private final SpanWithScope spanWithScope; private final Span span;
private final Scope scope;
public Listener(SpanWithScope spanWithScope) { public Listener(Span span, Scope scope) {
this.spanWithScope = spanWithScope; this.span = span;
this.scope = scope;
} }
@Override @Override
public void onSuccess(Response response) { public void onSuccess(Response response) {
Span span = spanWithScope.getSpan(); scope.close();
tracer().end(span); tracer().end(span);
spanWithScope.closeScope();
} }
@Override @Override
public void onFailure(Throwable cause) { public void onFailure(Throwable cause) {
Span span = spanWithScope.getSpan(); scope.close();
tracer().endExceptionally(span, cause); tracer().endExceptionally(span, cause);
spanWithScope.closeScope();
} }
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -48,24 +48,33 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
public static class CriteriaMethodAdvice { public static class CriteriaMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod( public static void startMethod(
@Advice.This Criteria criteria, @Advice.Origin("#m") String name) { @Advice.This Criteria criteria,
@Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Criteria, Context> contextStore = ContextStore<Criteria, Context> contextStore =
InstrumentationContext.get(Criteria.class, Context.class); InstrumentationContext.get(Criteria.class, Context.class);
return SessionMethodUtils.startScopeFrom( context = SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, null);
contextStore, criteria, "Criteria." + name, null, true); if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity, @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity,
@Advice.Origin("#m") String name) { @Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, "Criteria." + name, entity); if (scope != null) {
SessionMethodUtils.end(context, throwable, "Criteria." + name, entity);
scope.close();
}
} }
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -47,20 +47,30 @@ public class QueryInstrumentation implements TypeInstrumentation {
public static class QueryMethodAdvice { public static class QueryMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod(@Advice.This Query query) { public static void startMethod(
@Advice.This Query query,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Query, Context> contextStore = ContextStore<Query, Context> contextStore =
InstrumentationContext.get(Query.class, Context.class); InstrumentationContext.get(Query.class, Context.class);
return SessionMethodUtils.startScopeFrom( context = SessionMethodUtils.startSpanFrom(contextStore, query, query.getQueryString(), null);
contextStore, query, query.getQueryString(), null, true); if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, null, null); if (scope != null) {
SessionMethodUtils.end(context, throwable, null, null);
scope.close();
}
} }
} }
} }

View File

@ -19,10 +19,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap; import java.util.HashMap;
@ -137,34 +138,53 @@ public class SessionInstrumentation implements TypeInstrumentation {
public static class SessionMethodAdvice { public static class SessionMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod( public static void startMethod(
@Advice.This Object session, @Advice.This Object session,
@Advice.Origin("#m") String name, @Advice.Origin("#m") String name,
@Advice.Argument(0) Object entity) { @Advice.Argument(0) Object entity,
@Advice.Local("otelContext") Context spanContext,
@Advice.Local("otelScope") Scope scope) {
boolean startSpan = !SCOPE_ONLY_METHODS.contains(name); Context sessionContext = null;
if (session instanceof Session) { if (session instanceof Session) {
ContextStore<Session, Context> contextStore = ContextStore<Session, Context> contextStore =
InstrumentationContext.get(Session.class, Context.class); InstrumentationContext.get(Session.class, Context.class);
return SessionMethodUtils.startScopeFrom( sessionContext = contextStore.get((Session) session);
contextStore, (Session) session, "Session." + name, entity, startSpan);
} else if (session instanceof StatelessSession) { } else if (session instanceof StatelessSession) {
ContextStore<StatelessSession, Context> contextStore = ContextStore<StatelessSession, Context> contextStore =
InstrumentationContext.get(StatelessSession.class, Context.class); InstrumentationContext.get(StatelessSession.class, Context.class);
return SessionMethodUtils.startScopeFrom( sessionContext = contextStore.get((StatelessSession) session);
contextStore, (StatelessSession) session, "Session." + name, entity, startSpan); }
if (sessionContext == null) {
return; // No state found. We aren't in a Session.
}
if (CallDepthThreadLocalMap.incrementCallDepth(SessionMethodUtils.class) > 0) {
return; // This method call is being traced already.
}
if (!SCOPE_ONLY_METHODS.contains(name)) {
Span span = tracer().startSpan(sessionContext, "Session." + name, entity);
spanContext = sessionContext.with(span);
scope = spanContext.makeCurrent();
} else {
scope = sessionContext.makeCurrent();
} }
return null;
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned, @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned,
@Advice.Origin("#m") String name) { @Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context spanContext,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, "Session." + name, returned); if (scope != null) {
scope.close();
SessionMethodUtils.end(spanContext, throwable, "Session." + name, returned);
}
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -47,20 +47,31 @@ public class TransactionInstrumentation implements TypeInstrumentation {
public static class TransactionCommitAdvice { public static class TransactionCommitAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startCommit(@Advice.This Transaction transaction) { public static void startCommit(
@Advice.This Transaction transaction,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Transaction, Context> contextStore = ContextStore<Transaction, Context> contextStore =
InstrumentationContext.get(Transaction.class, Context.class); InstrumentationContext.get(Transaction.class, Context.class);
return SessionMethodUtils.startScopeFrom( context =
contextStore, transaction, "Transaction.commit", null, true); SessionMethodUtils.startSpanFrom(contextStore, transaction, "Transaction.commit", null);
if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endCommit( public static void endCommit(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, null, null); if (scope != null) {
SessionMethodUtils.end(context, throwable, null, null);
scope.close();
}
} }
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -48,24 +48,33 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
public static class CriteriaMethodAdvice { public static class CriteriaMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod( public static void startMethod(
@Advice.This Criteria criteria, @Advice.Origin("#m") String name) { @Advice.This Criteria criteria,
@Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Criteria, Context> contextStore = ContextStore<Criteria, Context> contextStore =
InstrumentationContext.get(Criteria.class, Context.class); InstrumentationContext.get(Criteria.class, Context.class);
return SessionMethodUtils.startScopeFrom( context = SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, null);
contextStore, criteria, "Criteria." + name, null, true); if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity, @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity,
@Advice.Origin("#m") String name) { @Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, "Criteria." + name, entity); if (scope != null) {
scope.close();
SessionMethodUtils.end(context, throwable, "Criteria." + name, entity);
}
} }
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -47,20 +47,30 @@ public class QueryInstrumentation implements TypeInstrumentation {
public static class QueryMethodAdvice { public static class QueryMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod(@Advice.This Query query) { public static void startMethod(
@Advice.This Query query,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Query, Context> contextStore = ContextStore<Query, Context> contextStore =
InstrumentationContext.get(Query.class, Context.class); InstrumentationContext.get(Query.class, Context.class);
return SessionMethodUtils.startScopeFrom( context = SessionMethodUtils.startSpanFrom(contextStore, query, query.getQueryString(), null);
contextStore, query, query.getQueryString(), null, true); if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, null, null); if (scope != null) {
scope.close();
SessionMethodUtils.end(context, throwable, null, null);
}
} }
} }
} }

View File

@ -19,10 +19,11 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap; import java.util.HashMap;
@ -126,26 +127,46 @@ public class SessionInstrumentation implements TypeInstrumentation {
public static class SessionMethodAdvice { public static class SessionMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod( public static void startMethod(
@Advice.This SharedSessionContract session, @Advice.This SharedSessionContract session,
@Advice.Origin("#m") String name, @Advice.Origin("#m") String name,
@Advice.Argument(0) Object entity) { @Advice.Argument(0) Object entity,
@Advice.Local("otelContext") Context spanContext,
@Advice.Local("otelScope") Scope scope) {
boolean startSpan = !SCOPE_ONLY_METHODS.contains(name);
ContextStore<SharedSessionContract, Context> contextStore = ContextStore<SharedSessionContract, Context> contextStore =
InstrumentationContext.get(SharedSessionContract.class, Context.class); InstrumentationContext.get(SharedSessionContract.class, Context.class);
return SessionMethodUtils.startScopeFrom( Context sessionContext = contextStore.get(session);
contextStore, session, "Session." + name, entity, startSpan);
if (sessionContext == null) {
return; // No state found. We aren't in a Session.
}
if (CallDepthThreadLocalMap.incrementCallDepth(SessionMethodUtils.class) > 0) {
return; // This method call is being traced already.
}
if (!SCOPE_ONLY_METHODS.contains(name)) {
Span span = tracer().startSpan(sessionContext, "Session." + name, entity);
spanContext = sessionContext.with(span);
scope = spanContext.makeCurrent();
} else {
scope = sessionContext.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned, @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned,
@Advice.Origin("#m") String name) { @Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context spanContext,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, "Session." + name, returned); if (scope != null) {
scope.close();
SessionMethodUtils.end(spanContext, throwable, "Session." + name, returned);
}
} }
} }

View File

@ -13,9 +13,9 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -47,20 +47,31 @@ public class TransactionInstrumentation implements TypeInstrumentation {
public static class TransactionCommitAdvice { public static class TransactionCommitAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startCommit(@Advice.This Transaction transaction) { public static void startCommit(
@Advice.This Transaction transaction,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<Transaction, Context> contextStore = ContextStore<Transaction, Context> contextStore =
InstrumentationContext.get(Transaction.class, Context.class); InstrumentationContext.get(Transaction.class, Context.class);
return SessionMethodUtils.startScopeFrom( context =
contextStore, transaction, "Transaction.commit", null, true); SessionMethodUtils.startSpanFrom(contextStore, transaction, "Transaction.commit", null);
if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endCommit( public static void endCommit(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
SessionMethodUtils.closeScope(spanWithScope, throwable, null, null); if (scope != null) {
scope.close();
SessionMethodUtils.end(context, throwable, null, null);
}
} }
} }
} }

View File

@ -12,9 +12,9 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils; import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
@ -46,20 +46,33 @@ public class ProcedureCallInstrumentation implements TypeInstrumentation {
public static class ProcedureCallMethodAdvice { public static class ProcedureCallMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope startMethod( public static void startMethod(
@Advice.This ProcedureCall call, @Advice.Origin("#m") String name) { @Advice.This ProcedureCall call,
@Advice.Origin("#m") String name,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
ContextStore<ProcedureCall, Context> contextStore = ContextStore<ProcedureCall, Context> contextStore =
InstrumentationContext.get(ProcedureCall.class, Context.class); InstrumentationContext.get(ProcedureCall.class, Context.class);
return SessionMethodUtils.startScopeFrom( context =
contextStore, call, "ProcedureCall." + name, call.getProcedureName(), true); SessionMethodUtils.startSpanFrom(
contextStore, call, "ProcedureCall." + name, call.getProcedureName());
if (context != null) {
scope = context.makeCurrent();
}
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void endMethod( public static void endMethod(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
SessionMethodUtils.closeScope(spanWithScope, throwable, null, null); @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
SessionMethodUtils.end(context, throwable, null, null);
}
} }
} }
} }

View File

@ -11,24 +11,21 @@ import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
public class SessionMethodUtils { public class SessionMethodUtils {
public static final Set<String> SCOPE_ONLY_METHODS = public static final Set<String> SCOPE_ONLY_METHODS =
new HashSet<>(Arrays.asList("immediateLoad", "internalLoad")); new HashSet<>(Arrays.asList("immediateLoad", "internalLoad"));
// Starts a scope as a child from a Span, where the Span is attached to the given spanKey using public static <TARGET, ENTITY> Context startSpanFrom(
// the given contextStore.
public static <TARGET, ENTITY> SpanWithScope startScopeFrom(
ContextStore<TARGET, Context> contextStore, ContextStore<TARGET, Context> contextStore,
TARGET spanKey, TARGET spanKey,
String operationName, String operationName,
ENTITY entity, ENTITY entity) {
boolean createSpan) {
Context sessionContext = contextStore.get(spanKey); Context sessionContext = contextStore.get(spanKey);
if (sessionContext == null) { if (sessionContext == null) {
@ -40,40 +37,31 @@ public class SessionMethodUtils {
return null; // This method call is being traced already. return null; // This method call is being traced already.
} }
if (createSpan) { Span span = tracer().startSpan(sessionContext, operationName, entity);
Span span = tracer().startSpan(sessionContext, operationName, entity); return sessionContext.with(span);
return new SpanWithScope(span, sessionContext.with(span).makeCurrent());
} else {
return new SpanWithScope(null, sessionContext.makeCurrent());
}
} }
// Closes a Scope/Span, adding an error tag if the given Throwable is not null. public static void end(
public static void closeScope( @Nullable Context context, Throwable throwable, String operationName, Object entity) {
SpanWithScope spanWithScope, Throwable throwable, String operationName, Object entity) {
if (spanWithScope == null) {
// This method call was re-entrant. Do nothing, since it is being traced by the parent/first
// call.
return;
}
CallDepthThreadLocalMap.reset(SessionMethodUtils.class); CallDepthThreadLocalMap.reset(SessionMethodUtils.class);
Span span = spanWithScope.getSpan(); if (context == null) {
if (span != null) { return;
if (operationName != null && entity != null) { }
String entityName = tracer().entityName(entity);
if (entityName != null) { Span span = Span.fromContext(context);
span.updateName(operationName + " " + entityName); if (operationName != null && entity != null) {
} String entityName = tracer().entityName(entity);
} if (entityName != null) {
if (throwable != null) { span.updateName(operationName + " " + entityName);
tracer().endExceptionally(span, throwable);
} else {
tracer().end(span);
} }
} }
spanWithScope.closeScope(); if (throwable != null) {
tracer().endExceptionally(span, throwable);
} else {
tracer().end(span);
}
} }
// Copies a span from the given Session ContextStore into the targetContextStore. Used to // Copies a span from the given Session ContextStore into the targetContextStore. Used to

View File

@ -17,8 +17,8 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
@ -63,31 +63,34 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
public static class JaxRsAnnotationsAdvice { public static class JaxRsAnnotationsAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope nameSpan(@Advice.This Object target, @Advice.Origin Method method) { public static void nameSpan(
@Advice.This Object target,
@Advice.Origin Method method,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (CallDepthThreadLocalMap.incrementCallDepth(Path.class) > 0) { if (CallDepthThreadLocalMap.incrementCallDepth(Path.class) > 0) {
return null; return;
} }
span = tracer().startSpan(target.getClass(), method);
Span span = tracer().startSpan(target.getClass(), method); scope = span.makeCurrent();
return new SpanWithScope(span, span.makeCurrent());
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
if (spanWithScope == null) { @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return; return;
} }
CallDepthThreadLocalMap.reset(Path.class); CallDepthThreadLocalMap.reset(Path.class);
Span span = spanWithScope.getSpan(); scope.close();
if (throwable == null) { if (throwable == null) {
tracer().end(span); tracer().end(span);
} else { } else {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
} }
spanWithScope.closeScope();
} }
} }
} }

View File

@ -6,7 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.kafkaclients; package io.opentelemetry.javaagent.instrumentation.kafkaclients;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope; import io.opentelemetry.context.Scope;
import java.util.Iterator; import java.util.Iterator;
import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -24,7 +24,9 @@ public class TracingIterator implements Iterator<ConsumerRecord<?, ?>> {
* Note: this may potentially create problems if this iterator is used from different threads. But * Note: this may potentially create problems if this iterator is used from different threads. But
* at the moment we cannot do much about this. * at the moment we cannot do much about this.
*/ */
private SpanWithScope currentSpanWithScope; private Span currentSpan;
private Scope currentScope;
public TracingIterator( public TracingIterator(
Iterator<ConsumerRecord<?, ?>> delegateIterator, KafkaConsumerTracer tracer) { Iterator<ConsumerRecord<?, ?>> delegateIterator, KafkaConsumerTracer tracer) {
@ -35,30 +37,21 @@ public class TracingIterator implements Iterator<ConsumerRecord<?, ?>> {
@Override @Override
public boolean hasNext() { public boolean hasNext() {
if (currentSpanWithScope != null) { closeScopeAndEndSpan();
tracer.end(currentSpanWithScope.getSpan());
currentSpanWithScope.closeScope();
currentSpanWithScope = null;
}
return delegateIterator.hasNext(); return delegateIterator.hasNext();
} }
@Override @Override
public ConsumerRecord<?, ?> next() { public ConsumerRecord<?, ?> next() {
if (currentSpanWithScope != null) { // in case they didn't call hasNext()...
// in case they didn't call hasNext()... closeScopeAndEndSpan();
tracer.end(currentSpanWithScope.getSpan());
currentSpanWithScope.closeScope();
currentSpanWithScope = null;
}
ConsumerRecord<?, ?> next = delegateIterator.next(); ConsumerRecord<?, ?> next = delegateIterator.next();
try { try {
if (next != null) { if (next != null) {
Span span = tracer.startSpan(next); currentSpan = tracer.startSpan(next);
currentScope = currentSpan.makeCurrent();
currentSpanWithScope = new SpanWithScope(span, span.makeCurrent());
} }
} catch (Exception e) { } catch (Exception e) {
log.debug("Error during decoration", e); log.debug("Error during decoration", e);
@ -66,6 +59,15 @@ public class TracingIterator implements Iterator<ConsumerRecord<?, ?>> {
return next; return next;
} }
private void closeScopeAndEndSpan() {
if (currentScope != null) {
currentScope.close();
currentScope = null;
tracer.end(currentSpan);
currentSpan = null;
}
}
@Override @Override
public void remove() { public void remove() {
delegateIterator.remove(); delegateIterator.remove();

View File

@ -5,18 +5,25 @@
package io.opentelemetry.javaagent.instrumentation.kafkastreams; package io.opentelemetry.javaagent.instrumentation.kafkastreams;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
public class SpanScopeHolder { public class SpanScopeHolder {
public static final ThreadLocal<SpanScopeHolder> HOLDER = new ThreadLocal<>(); public static final ThreadLocal<SpanScopeHolder> HOLDER = new ThreadLocal<>();
private SpanWithScope spanWithScope; private Span span;
private Scope scope;
public SpanWithScope getSpanWithScope() { public void closeScope() {
return spanWithScope; scope.close();
} }
public void setSpanWithScope(SpanWithScope spanWithScope) { public Span getSpan() {
this.spanWithScope = spanWithScope; return span;
}
public void set(Span span, Scope scope) {
this.span = span;
this.scope = scope;
} }
} }

View File

@ -14,7 +14,6 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -56,7 +55,7 @@ public class StreamTaskStartInstrumentation implements TypeInstrumentation {
Span span = tracer().startSpan(record); Span span = tracer().startSpan(record);
holder.setSpanWithScope(new SpanWithScope(span, span.makeCurrent())); holder.set(span, span.makeCurrent());
} }
} }
} }

View File

@ -14,7 +14,6 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -49,11 +48,9 @@ public class StreamTaskStopInstrumentation implements TypeInstrumentation {
public static void stopSpan( public static void stopSpan(
@Advice.Enter SpanScopeHolder holder, @Advice.Thrown Throwable throwable) { @Advice.Enter SpanScopeHolder holder, @Advice.Thrown Throwable throwable) {
HOLDER.remove(); HOLDER.remove();
SpanWithScope spanWithScope = holder.getSpanWithScope(); Span span = holder.getSpan();
if (spanWithScope != null) { if (span != null) {
spanWithScope.closeScope(); holder.closeScope();
Span span = spanWithScope.getSpan();
if (throwable != null) { if (throwable != null) {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);

View File

@ -8,14 +8,13 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
import static com.lambdaworks.redis.protocol.CommandKeyword.SEGFAULT; import static com.lambdaworks.redis.protocol.CommandKeyword.SEGFAULT;
import static com.lambdaworks.redis.protocol.CommandType.DEBUG; import static com.lambdaworks.redis.protocol.CommandType.DEBUG;
import static com.lambdaworks.redis.protocol.CommandType.SHUTDOWN; import static com.lambdaworks.redis.protocol.CommandType.SHUTDOWN;
import static io.opentelemetry.javaagent.instrumentation.lettuce.v4_0.LettuceDatabaseClientTracer.tracer;
import com.lambdaworks.redis.RedisURI;
import com.lambdaworks.redis.protocol.AsyncCommand; import com.lambdaworks.redis.protocol.AsyncCommand;
import com.lambdaworks.redis.protocol.CommandType; import com.lambdaworks.redis.protocol.CommandType;
import com.lambdaworks.redis.protocol.ProtocolKeyword; import com.lambdaworks.redis.protocol.ProtocolKeyword;
import com.lambdaworks.redis.protocol.RedisCommand; import com.lambdaworks.redis.protocol.RedisCommand;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
@ -24,52 +23,30 @@ public final class InstrumentationPoints {
private static final Set<CommandType> NON_INSTRUMENTING_COMMANDS = EnumSet.of(SHUTDOWN, DEBUG); private static final Set<CommandType> NON_INSTRUMENTING_COMMANDS = EnumSet.of(SHUTDOWN, DEBUG);
public static SpanWithScope beforeCommand(RedisCommand<?, ?, ?> command) {
Span span = LettuceDatabaseClientTracer.tracer().startSpan(null, command);
return new SpanWithScope(span, LettuceDatabaseClientTracer.tracer().startScope(span));
}
public static void afterCommand( public static void afterCommand(
RedisCommand<?, ?, ?> command, RedisCommand<?, ?, ?> command,
SpanWithScope spanWithScope, Span span,
Throwable throwable, Throwable throwable,
AsyncCommand<?, ?, ?> asyncCommand) { AsyncCommand<?, ?, ?> asyncCommand) {
Span span = spanWithScope.getSpan();
if (throwable != null) { if (throwable != null) {
LettuceDatabaseClientTracer.tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
} else if (expectsResponse(command)) { } else if (expectsResponse(command)) {
asyncCommand.handleAsync( asyncCommand.handleAsync(
(value, ex) -> { (value, ex) -> {
if (ex == null) { if (ex == null) {
LettuceDatabaseClientTracer.tracer().end(span); tracer().end(span);
} else if (ex instanceof CancellationException) { } else if (ex instanceof CancellationException) {
span.setAttribute("lettuce.command.cancelled", true); span.setAttribute("lettuce.command.cancelled", true);
LettuceDatabaseClientTracer.tracer().end(span); tracer().end(span);
} else { } else {
LettuceDatabaseClientTracer.tracer().endExceptionally(span, ex); tracer().endExceptionally(span, ex);
} }
return null; return null;
}); });
} else { } else {
// No response is expected, so we must finish the span now. // No response is expected, so we must finish the span now.
LettuceDatabaseClientTracer.tracer().end(span); tracer().end(span);
} }
spanWithScope.closeScope();
}
public static SpanWithScope beforeConnect(RedisURI redisUri) {
Span span = LettuceConnectionDatabaseClientTracer.tracer().startSpan(redisUri, "CONNECT");
return new SpanWithScope(span, LettuceConnectionDatabaseClientTracer.tracer().startScope(span));
}
public static void afterConnect(SpanWithScope spanWithScope, Throwable throwable) {
Span span = spanWithScope.getSpan();
if (throwable != null) {
LettuceConnectionDatabaseClientTracer.tracer().endExceptionally(span, throwable);
} else {
LettuceConnectionDatabaseClientTracer.tracer().end(span);
}
spanWithScope.closeScope();
} }
/** /**

View File

@ -5,24 +5,33 @@
package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0; package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
import static io.opentelemetry.javaagent.instrumentation.lettuce.v4_0.LettuceDatabaseClientTracer.tracer;
import com.lambdaworks.redis.protocol.AsyncCommand; import com.lambdaworks.redis.protocol.AsyncCommand;
import com.lambdaworks.redis.protocol.RedisCommand; import com.lambdaworks.redis.protocol.RedisCommand;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
public class LettuceAsyncCommandsAdvice { public class LettuceAsyncCommandsAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) RedisCommand<?, ?, ?> command) { public static void onEnter(
return InstrumentationPoints.beforeCommand(command); @Advice.Argument(0) RedisCommand<?, ?, ?> command,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan(null, command);
scope = tracer().startScope(span);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Argument(0) RedisCommand<?, ?, ?> command, @Advice.Argument(0) RedisCommand<?, ?, ?> command,
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return AsyncCommand<?, ?, ?> asyncCommand) { @Advice.Return AsyncCommand<?, ?, ?> asyncCommand,
InstrumentationPoints.afterCommand(command, spanWithScope, throwable, asyncCommand); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
scope.close();
InstrumentationPoints.afterCommand(command, span, throwable, asyncCommand);
} }
} }

View File

@ -5,19 +5,34 @@
package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0; package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
import static io.opentelemetry.javaagent.instrumentation.lettuce.v4_0.LettuceConnectionDatabaseClientTracer.tracer;
import com.lambdaworks.redis.RedisURI; import com.lambdaworks.redis.RedisURI;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
public class RedisConnectionAdvice { public class RedisConnectionAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(1) RedisURI redisUri) { public static void onEnter(
return InstrumentationPoints.beforeConnect(redisUri); @Advice.Argument(1) RedisURI redisUri,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan(redisUri, "CONNECT");
scope = tracer().startScope(span);
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter SpanWithScope scope, @Advice.Thrown Throwable throwable) { public static void onExit(
InstrumentationPoints.afterConnect(scope, throwable); @Advice.Thrown Throwable throwable,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
scope.close();
if (throwable != null) {
tracer().endExceptionally(span, throwable);
} else {
tracer().end(span);
}
} }
} }

View File

@ -9,8 +9,8 @@ import static io.opentelemetry.javaagent.instrumentation.play.v2_3.PlayTracer.tr
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind; import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import play.api.mvc.Action; import play.api.mvc.Action;
import play.api.mvc.Headers; import play.api.mvc.Headers;
@ -20,30 +20,31 @@ import scala.concurrent.Future;
public class PlayAdvice { public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) final Request<?> req) { public static void onEnter(
Span span = tracer().startSpan("play.request", Kind.INTERNAL); @Advice.Argument(0) final Request<?> req,
@Advice.Local("otelSpan") Span span,
return new SpanWithScope(span, span.makeCurrent()); @Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan("play.request", Kind.INTERNAL);
scope = span.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse( public static void stopTraceOnResponse(
@Advice.Enter SpanWithScope playControllerScope,
@Advice.This Object thisAction, @Advice.This Object thisAction,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req, @Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture) { @Advice.Return(readOnly = false) Future<Result> responseFuture,
Span playControllerSpan = playControllerScope.getSpan(); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) { if (throwable == null) {
responseFuture.onComplete( responseFuture.onComplete(
new RequestCompleteCallback(playControllerSpan), new RequestCompleteCallback(span), ((Action<?>) thisAction).executionContext());
((Action<?>) thisAction).executionContext());
} else { } else {
tracer().endExceptionally(playControllerSpan, throwable); tracer().endExceptionally(span, throwable);
} }
playControllerScope.closeScope();
// span finished in RequestCompleteCallback
// set the span name on the upstream akka/netty span // set the span name on the upstream akka/netty span
tracer().updateSpanName(BaseTracer.getCurrentServerSpan(), req); tracer().updateSpanName(BaseTracer.getCurrentServerSpan(), req);

View File

@ -9,8 +9,8 @@ import static io.opentelemetry.javaagent.instrumentation.play.v2_4.PlayTracer.tr
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind; import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import play.api.mvc.Action; import play.api.mvc.Action;
import play.api.mvc.Headers; import play.api.mvc.Headers;
@ -20,36 +20,36 @@ import scala.concurrent.Future;
public class PlayAdvice { public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) Request<?> req) { public static void onEnter(
Span span = tracer().startSpan("play.request", Kind.INTERNAL); @Advice.Argument(0) Request<?> req,
return new SpanWithScope(span, span.makeCurrent()); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan("play.request", Kind.INTERNAL);
scope = span.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse( public static void stopTraceOnResponse(
@Advice.Enter SpanWithScope playControllerScope,
@Advice.This Object thisAction, @Advice.This Object thisAction,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req, @Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture) { @Advice.Return(readOnly = false) Future<Result> responseFuture,
Span playControllerSpan = playControllerScope.getSpan(); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated. // Call onRequest on return after tags are populated.
tracer().updateSpanName(playControllerSpan, req); tracer().updateSpanName(span, req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) { if (throwable == null) {
responseFuture.onComplete( responseFuture.onComplete(
new RequestCompleteCallback(playControllerSpan), new RequestCompleteCallback(span), ((Action<?>) thisAction).executionContext());
((Action<?>) thisAction).executionContext());
} else { } else {
tracer().endExceptionally(playControllerSpan, throwable); tracer().endExceptionally(span, throwable);
} }
playControllerScope.closeScope();
// span finished in RequestCompleteCallback
Span rootSpan = BaseTracer.getCurrentServerSpan();
// set the span name on the upstream akka/netty span // set the span name on the upstream akka/netty span
tracer().updateSpanName(rootSpan, req); tracer().updateSpanName(BaseTracer.getCurrentServerSpan(), req);
} }
// Unused method for muzzle // Unused method for muzzle

View File

@ -9,8 +9,8 @@ import static io.opentelemetry.javaagent.instrumentation.play.v2_6.PlayTracer.tr
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind; import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import play.api.mvc.Action; import play.api.mvc.Action;
import play.api.mvc.Request; import play.api.mvc.Request;
@ -19,36 +19,35 @@ import scala.concurrent.Future;
public class PlayAdvice { public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) Request<?> req) { public static void onEnter(
Span span = tracer().startSpan("play.request", Kind.INTERNAL); @Advice.Argument(0) Request<?> req,
@Advice.Local("otelSpan") Span span,
return new SpanWithScope(span, span.makeCurrent()); @Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan("play.request", Kind.INTERNAL);
scope = span.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse( public static void stopTraceOnResponse(
@Advice.Enter SpanWithScope playControllerScope,
@Advice.This Object thisAction, @Advice.This Object thisAction,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Argument(0) Request<?> req, @Advice.Argument(0) Request<?> req,
@Advice.Return(readOnly = false) Future<Result> responseFuture) { @Advice.Return(readOnly = false) Future<Result> responseFuture,
Span playControllerSpan = playControllerScope.getSpan(); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
// Call onRequest on return after tags are populated. // Call onRequest on return after tags are populated.
tracer().updateSpanName(playControllerSpan, req); tracer().updateSpanName(span, req);
scope.close();
// span finished in RequestCompleteCallback
if (throwable == null) { if (throwable == null) {
responseFuture.onComplete( responseFuture.onComplete(
new RequestCompleteCallback(playControllerSpan), new RequestCompleteCallback(span), ((Action<?>) thisAction).executionContext());
((Action<?>) thisAction).executionContext());
} else { } else {
tracer().endExceptionally(playControllerSpan, throwable); tracer().endExceptionally(span, throwable);
} }
playControllerScope.closeScope();
// span finished in RequestCompleteCallback
Span rootSpan = BaseTracer.getCurrentServerSpan();
// set the span name on the upstream akka/netty span // set the span name on the upstream akka/netty span
tracer().updateSpanName(rootSpan, req); tracer().updateSpanName(BaseTracer.getCurrentServerSpan(), req);
} }
} }

View File

@ -9,9 +9,9 @@ import static io.opentelemetry.javaagent.instrumentation.spring.webflux.server.S
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath; import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.HandlerMapping;
@ -21,10 +21,11 @@ import org.springframework.web.util.pattern.PathPattern;
public class HandlerAdapterAdvice { public class HandlerAdapterAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter( public static void methodEnter(
@Advice.Argument(0) ServerWebExchange exchange, @Advice.Argument(1) Object handler) { @Advice.Argument(0) ServerWebExchange exchange,
@Advice.Argument(1) Object handler,
@Advice.Local("otelScope") Scope scope) {
SpanWithScope spanWithScope = null;
Context context = exchange.getAttribute(AdviceUtils.CONTEXT_ATTRIBUTE); Context context = exchange.getAttribute(AdviceUtils.CONTEXT_ATTRIBUTE);
if (handler != null && context != null) { if (handler != null && context != null) {
Span span = Span.fromContext(context); Span span = Span.fromContext(context);
@ -44,7 +45,7 @@ public class HandlerAdapterAdvice {
span.updateName(operationName); span.updateName(operationName);
span.setAttribute("spring-webflux.handler.type", handlerType); span.setAttribute("spring-webflux.handler.type", handlerType);
spanWithScope = new SpanWithScope(span, context.makeCurrent()); scope = context.makeCurrent();
} }
if (context != null) { if (context != null) {
@ -56,20 +57,18 @@ public class HandlerAdapterAdvice {
ServletContextPath.prepend(Context.current(), bestPattern.toString())); ServletContextPath.prepend(Context.current(), bestPattern.toString()));
} }
} }
return spanWithScope;
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.Argument(0) ServerWebExchange exchange, @Advice.Argument(0) ServerWebExchange exchange,
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable,
@Advice.Thrown Throwable throwable) { @Advice.Local("otelScope") Scope scope) {
if (throwable != null) { if (throwable != null) {
AdviceUtils.finishSpanIfPresent(exchange, throwable); AdviceUtils.finishSpanIfPresent(exchange, throwable);
} }
if (spanWithScope != null) { if (scope != null) {
spanWithScope.closeScope(); scope.close();
// span finished in SpanFinishingSubscriber // span finished in SpanFinishingSubscriber
} }
} }

View File

@ -14,8 +14,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -84,21 +84,25 @@ public class DispatcherServletInstrumentation implements TypeInstrumentation {
public static class RenderAdvice { public static class RenderAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) ModelAndView mv) { public static void onEnter(
Span span = tracer().startSpan(mv); @Advice.Argument(0) ModelAndView mv,
return new SpanWithScope(span, span.makeCurrent()); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
span = tracer().startSpan(mv);
scope = span.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
Span span = spanWithScope.getSpan(); @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
scope.close();
if (throwable == null) { if (throwable == null) {
tracer().end(span); tracer().end(span);
} else { } else {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
} }
spanWithScope.closeScope();
} }
} }

View File

@ -18,9 +18,9 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -54,35 +54,36 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
public static class ControllerAdvice { public static class ControllerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope nameResourceAndStartSpan( public static void nameResourceAndStartSpan(
@Advice.Argument(0) HttpServletRequest request, @Advice.Argument(2) Object handler) { @Advice.Argument(0) HttpServletRequest request,
@Advice.Argument(2) Object handler,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
Context context = Java8BytecodeBridge.currentContext(); Context context = Java8BytecodeBridge.currentContext();
Span serverSpan = BaseTracer.getCurrentServerSpan(context); Span serverSpan = BaseTracer.getCurrentServerSpan(context);
if (serverSpan != null) { if (serverSpan != null) {
// Name the parent span based on the matching pattern // Name the parent span based on the matching pattern
tracer().onRequest(context, serverSpan, request); tracer().onRequest(context, serverSpan, request);
// Now create a span for handler/controller execution. // Now create a span for handler/controller execution.
Span span = tracer().startHandlerSpan(handler); span = tracer().startHandlerSpan(handler);
scope = context.with(span).makeCurrent();
return new SpanWithScope(span, context.with(span).makeCurrent());
} else {
return null;
} }
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Enter SpanWithScope spanWithScope, @Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable,
if (spanWithScope == null) { @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return; return;
} }
Span span = spanWithScope.getSpan(); scope.close();
if (throwable == null) { if (throwable == null) {
tracer().end(span); tracer().end(span);
} else { } else {
tracer().endExceptionally(span, throwable); tracer().endExceptionally(span, throwable);
} }
spanWithScope.closeScope();
} }
} }
} }

View File

@ -22,8 +22,8 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.twilio.Twilio; import com.twilio.Twilio;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -75,52 +75,48 @@ public class TwilioAsyncInstrumentation implements TypeInstrumentation {
/** Method entry instrumentation. */ /** Method entry instrumentation. */
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter( public static void methodEnter(
@Advice.This Object that, @Advice.Origin("#m") String methodName) { @Advice.This Object that,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
// Ensure that we only create a span for the top-level Twilio client method; except in the // Ensure that we only create a span for the top-level Twilio client method; except in the
// case of async operations where we want visibility into how long the task was delayed from // case of async operations where we want visibility into how long the task was delayed from
// starting. Our call depth checker does not span threads, so the async case is handled // starting. Our call depth checker does not span threads, so the async case is handled
// automatically for us. // automatically for us.
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(Twilio.class); if (CallDepthThreadLocalMap.incrementCallDepth(Twilio.class) > 0) {
if (callDepth > 0) { return;
return null;
} }
// Don't automatically close the span with the scope if we're executing an async method // Don't automatically close the span with the scope if we're executing an async method
Span span = tracer().startSpan(that, methodName); span = tracer().startSpan(that, methodName);
scope = tracer().startScope(span);
return new SpanWithScope(span, tracer().startScope(span));
} }
/** Method exit instrumentation. */ /** Method exit instrumentation. */
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return ListenableFuture<?> response) { @Advice.Return ListenableFuture<?> response,
if (spanWithScope == null) { @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return; return;
} }
CallDepthThreadLocalMap.reset(Twilio.class); CallDepthThreadLocalMap.reset(Twilio.class);
// If we have a scope (i.e. we were the top-level Twilio SDK invocation), // span finished in SpanFinishingCallback
try { scope.close();
Span span = spanWithScope.getSpan(); if (throwable != null) {
// There was an synchronous error,
if (throwable != null) { // which means we shouldn't wait for a callback to close the span.
// There was an synchronous error, tracer().endExceptionally(span, throwable);
// which means we shouldn't wait for a callback to close the span. } else {
tracer().endExceptionally(span, throwable); // We're calling an async operation, we still need to finish the span when it's
} else { // complete and report the results; set an appropriate callback
// We're calling an async operation, we still need to finish the span when it's Futures.addCallback(
// complete and report the results; set an appropriate callback response, new SpanFinishingCallback<>(span), Twilio.getExecutorService());
Futures.addCallback(
response, new SpanFinishingCallback<>(span), Twilio.getExecutorService());
}
} finally {
spanWithScope.closeScope();
// span finished in SpanFinishingCallback
} }
} }
} }

View File

@ -17,8 +17,8 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import com.twilio.Twilio; import com.twilio.Twilio;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
import io.opentelemetry.javaagent.instrumentation.api.SpanWithScope;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -71,45 +71,41 @@ public class TwilioSyncInstrumentation implements TypeInstrumentation {
/** Method entry instrumentation. */ /** Method entry instrumentation. */
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope methodEnter( public static void methodEnter(
@Advice.This Object that, @Advice.Origin("#m") String methodName) { @Advice.This Object that,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
// Ensure that we only create a span for the top-level Twilio client method; except in the // Ensure that we only create a span for the top-level Twilio client method; except in the
// case of async operations where we want visibility into how long the task was delayed from // case of async operations where we want visibility into how long the task was delayed from
// starting. Our call depth checker does not span threads, so the async case is handled // starting. Our call depth checker does not span threads, so the async case is handled
// automatically for us. // automatically for us.
int callDepth = CallDepthThreadLocalMap.incrementCallDepth(Twilio.class); if (CallDepthThreadLocalMap.incrementCallDepth(Twilio.class) > 0) {
if (callDepth > 0) { return;
return null;
} }
Span span = tracer().startSpan(that, methodName); span = tracer().startSpan(that, methodName);
scope = tracer().startScope(span);
return new SpanWithScope(span, tracer().startScope(span));
} }
/** Method exit instrumentation. */ /** Method exit instrumentation. */
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static void methodExit(
@Advice.Enter SpanWithScope spanWithScope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Return Object response) { @Advice.Return Object response,
if (spanWithScope == null) { @Advice.Local("otelSpan") Span span,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return; return;
} }
CallDepthThreadLocalMap.reset(Twilio.class); CallDepthThreadLocalMap.reset(Twilio.class);
// If we have a scope (i.e. we were the top-level Twilio SDK invocation), scope.close();
try { if (throwable != null) {
Span span = spanWithScope.getSpan(); tracer().endExceptionally(span, throwable);
} else {
if (throwable != null) { tracer().end(span, response);
tracer().endExceptionally(span, throwable);
} else {
tracer().end(span, response);
}
} finally {
spanWithScope.closeScope();
} }
} }
} }

View File

@ -1,88 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.api;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
/**
* This is deprecated.
*
* <p>Originally, we used {@code SpanWithScope} to pass the {@link Span} and {@link Scope} between
* {@code @Advice.OnMethodEnter} and {@code @Advice.OnMethodExit}, e.g.
*
* <pre>
* &#64;Advice.OnMethodEnter(...)
* public static SpanWithScope onEnter(...) {
* ...
* Span span = ...
* return new SpanWithScope(span, span.makeCurrent());
* }
*
* &#64;Advice.OnMethodExit(...)
* public static void stopSpan(
* ...
* &#64;Advice.Enter final SpanWithScope spanWithScope) {
* Span span = spanWithScope.getSpan();
* ...
* span.end();
* spanWithScope.closeScope();
* }
* </pre>
*
* <p>We are (slowly) migrating to a new pattern using `@Advice.Local`:
*
* <pre>
* &#64;Advice.OnMethodEnter(...)
* public static void onEnter(
* ...
* &#64;Advice.Local("otelSpan") Span span,
* &#64;Advice.Local("otelScope") Scope scope) {
* ...
* span = ...
* scope = ...
* }
*
* &#64;Advice.OnMethodExit
* public static void onExit(
* ...
* &#64;Advice.Local("otelSpan") Span span,
* &#64;Advice.Local("otelScope") Scope scope) {
* ...
* span.end();
* scope.close();
* }
* </pre>
*
* <p>This new pattern has the following benefits:
*
* <ul>
* <li>The new pattern is more efficient since it doesn't require instantiating the {@code
* SpanWithScope} holder object
* <li>The new pattern extends nicely in the common case where we also need to pass {@link
* CallDepth} between the methods
* </ul>
*
* @deprecated see above
*/
@Deprecated
public class SpanWithScope {
private final Span span;
private final Scope scope;
public SpanWithScope(Span span, Scope scope) {
this.span = span;
this.scope = scope;
}
public Span getSpan() {
return span;
}
public void closeScope() {
scope.close();
}
}