make spring indy-ready (#14183)

Co-authored-by: Lauri Tulmin <tulmin@gmail.com>
This commit is contained in:
SylvainJuge 2025-07-15 04:43:27 +02:00 committed by GitHub
parent f1fa49f31f
commit 57301cade1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 849 additions and 642 deletions

View File

@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldTraceItems;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.getChunkContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.itemInstrumenter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons;
import javax.annotation.Nullable;
import org.springframework.batch.core.scope.context.ChunkContext;
public final class AdviceScope {
private final Context context;
private final String item;
private final Scope scope;
private AdviceScope(Context context, Scope scope, String item) {
this.context = context;
this.scope = scope;
this.item = item;
}
@Nullable
public static AdviceScope enter(String itemOperationName) {
Context parentContext = Context.current();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return null;
}
String item = ItemSingletons.itemName(chunkContext, itemOperationName);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return null;
}
Context context = itemInstrumenter().start(parentContext, item);
return new AdviceScope(context, context.makeCurrent(), item);
}
public void exit(@Nullable Throwable thrown) {
scope.close();
itemInstrumenter().end(context, item, null, thrown);
}
}

View File

@ -10,6 +10,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk.StepBuilderInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk.StepBuilderInstrumentation;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ChunkOrientedTaskletInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ChunkOrientedTaskletInstrumentation;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.JsrChunkProcessorInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.JsrChunkProcessorInstrumentation;
@ -25,7 +26,8 @@ import java.util.List;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringBatchInstrumentationModule extends InstrumentationModule { public class SpringBatchInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringBatchInstrumentationModule() { public SpringBatchInstrumentationModule() {
super("spring-batch", "spring-batch-3.0"); super("spring-batch", "spring-batch-3.0");
} }
@ -59,4 +61,9 @@ public class SpringBatchInstrumentationModule extends InstrumentationModule {
// TODO: replace this with an experimental flag // TODO: replace this with an experimental flag
return false; return false;
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -10,14 +10,11 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.ContextAndScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.builder.AbstractTaskletStepBuilder; import org.springframework.batch.core.step.builder.AbstractTaskletStepBuilder;
public class StepBuilderInstrumentation implements TypeInstrumentation { public class StepBuilderInstrumentation implements TypeInstrumentation {
@ -43,10 +40,7 @@ public class StepBuilderInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.This AbstractTaskletStepBuilder<?> stepBuilder) { public static void onEnter(@Advice.This AbstractTaskletStepBuilder<?> stepBuilder) {
VirtualField<ChunkContext, ContextAndScope> chunkExecutionVirtualField = stepBuilder.listener(new TracingChunkExecutionListener(stepBuilder.getClass()));
VirtualField.find(ChunkContext.class, ContextAndScope.class);
stepBuilder.listener(
new TracingChunkExecutionListener(chunkExecutionVirtualField, stepBuilder.getClass()));
} }
} }
} }

View File

@ -5,8 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.rootContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldCreateRootSpanForChunk; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldCreateRootSpanForChunk;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk.ChunkSingletons.chunkInstrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk.ChunkSingletons.chunkInstrumenter;
@ -20,19 +18,18 @@ import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
public final class TracingChunkExecutionListener implements ChunkListener, Ordered { public final class TracingChunkExecutionListener implements ChunkListener, Ordered {
private final VirtualField<ChunkContext, ContextAndScope> executionVirtualField; private static final VirtualField<ChunkContext, ContextAndScope> CONTEXT_AND_SCOPE =
VirtualField.find(ChunkContext.class, ContextAndScope.class);
private final Class<?> builderClass; private final Class<?> builderClass;
private ChunkContextAndBuilder chunkContextAndBuilder; private ChunkContextAndBuilder chunkContextAndBuilder;
public TracingChunkExecutionListener( public TracingChunkExecutionListener(Class<?> builderClass) {
VirtualField<ChunkContext, ContextAndScope> executionVirtualField, Class<?> builderClass) {
this.executionVirtualField = executionVirtualField;
this.builderClass = builderClass; this.builderClass = builderClass;
} }
@Override @Override
public void beforeChunk(ChunkContext chunkContext) { public void beforeChunk(ChunkContext chunkContext) {
Context parentContext = shouldCreateRootSpanForChunk() ? rootContext() : currentContext(); Context parentContext = shouldCreateRootSpanForChunk() ? Context.root() : Context.current();
chunkContextAndBuilder = new ChunkContextAndBuilder(chunkContext, builderClass); chunkContextAndBuilder = new ChunkContextAndBuilder(chunkContext, builderClass);
if (!chunkInstrumenter().shouldStart(parentContext, chunkContextAndBuilder)) { if (!chunkInstrumenter().shouldStart(parentContext, chunkContextAndBuilder)) {
return; return;
@ -41,7 +38,7 @@ public final class TracingChunkExecutionListener implements ChunkListener, Order
Context context = chunkInstrumenter().start(parentContext, chunkContextAndBuilder); Context context = chunkInstrumenter().start(parentContext, chunkContextAndBuilder);
// beforeJob & afterJob always execute on the same thread // beforeJob & afterJob always execute on the same thread
Scope scope = context.makeCurrent(); Scope scope = context.makeCurrent();
executionVirtualField.set(chunkContext, new ContextAndScope(context, scope)); CONTEXT_AND_SCOPE.set(chunkContext, new ContextAndScope(context, scope));
} }
@Override @Override
@ -57,12 +54,12 @@ public final class TracingChunkExecutionListener implements ChunkListener, Order
} }
private void end(ChunkContext chunkContext, @Nullable Throwable throwable) { private void end(ChunkContext chunkContext, @Nullable Throwable throwable) {
ContextAndScope contextAndScope = executionVirtualField.get(chunkContext); ContextAndScope contextAndScope = CONTEXT_AND_SCOPE.get(chunkContext);
if (contextAndScope == null) { if (contextAndScope == null) {
return; return;
} }
executionVirtualField.set(chunkContext, null); CONTEXT_AND_SCOPE.set(chunkContext, null);
contextAndScope.closeScope(); contextAndScope.closeScope();
chunkInstrumenter().end(contextAndScope.getContext(), chunkContextAndBuilder, null, throwable); chunkInstrumenter().end(contextAndScope.getContext(), chunkContextAndBuilder, null, throwable);
} }

View File

@ -17,6 +17,7 @@ import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -44,17 +45,18 @@ public class ChunkOrientedTaskletInstrumentation implements TypeInstrumentation
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ExecuteAdvice { public static class ExecuteAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static Scope onEnter(@Advice.Argument(1) ChunkContext chunkContext) {
@Advice.Argument(1) ChunkContext chunkContext, @Advice.Local("otelScope") Scope scope) { if (!shouldTraceItems()) {
if (shouldTraceItems()) { return null;
Context context = startChunk(currentContext(), chunkContext);
scope = context.makeCurrent();
} }
Context context = startChunk(currentContext(), chunkContext);
return context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(@Advice.Local("otelScope") Scope scope) { public static void onExit(@Advice.Enter @Nullable Scope scope) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
} }

View File

@ -5,25 +5,20 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldTraceItems;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_PROCESS; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_PROCESS;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_READ; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_READ;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_WRITE; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_WRITE;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.getChunkContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.itemInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.isProtected;
import static net.bytebuddy.matcher.ElementMatchers.named; 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.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.AdviceScope;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.scope.context.ChunkContext;
public class JsrChunkProcessorInstrumentation implements TypeInstrumentation { public class JsrChunkProcessorInstrumentation implements TypeInstrumentation {
@Override @Override
@ -47,114 +42,57 @@ public class JsrChunkProcessorInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ProvideAdvice { public static class ProvideAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter() {
@Advice.Local("otelContext") Context context, return AdviceScope.enter(ITEM_OPERATION_READ);
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_READ);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope, if (adviceScope != null) {
@Advice.Local("otelItem") String item) { adviceScope.exit(thrown);
if (scope == null) {
return;
} }
scope.close();
itemInstrumenter().end(context, item, null, thrown);
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class TransformAdvice { public static class TransformAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter() {
@Advice.Local("otelContext") Context context, return AdviceScope.enter(ITEM_OPERATION_PROCESS);
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_PROCESS);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope, if (adviceScope != null) {
@Advice.Local("otelItem") String item) { adviceScope.exit(thrown);
if (scope == null) {
return;
} }
scope.close();
itemInstrumenter().end(context, item, null, thrown);
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class PersistAdvice { public static class PersistAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter() {
@Advice.Local("otelContext") Context context, return AdviceScope.enter(ITEM_OPERATION_WRITE);
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_WRITE);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope, if (adviceScope != null) {
@Advice.Local("otelItem") String item) { adviceScope.exit(thrown);
if (scope == null) {
return;
} }
scope.close();
itemInstrumenter().end(context, item, null, thrown);
} }
} }
} }

View File

@ -5,24 +5,19 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldTraceItems;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_PROCESS; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_PROCESS;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_WRITE; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_WRITE;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.getChunkContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.itemInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.isProtected;
import static net.bytebuddy.matcher.ElementMatchers.named; 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.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.AdviceScope;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.ItemWriter;
@ -45,78 +40,40 @@ public class SimpleChunkProcessorInstrumentation implements TypeInstrumentation
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ProcessAdvice { public static class ProcessAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter(
@Advice.FieldValue("itemProcessor") ItemProcessor<?, ?> itemProcessor, @Advice.FieldValue("itemProcessor") ItemProcessor<?, ?> itemProcessor) {
@Advice.Local("otelContext") Context context, return AdviceScope.enter(ITEM_OPERATION_PROCESS);
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_PROCESS);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope, if (adviceScope != null) {
@Advice.Local("otelItem") String item) { adviceScope.exit(thrown);
if (scope == null) {
return;
} }
scope.close();
itemInstrumenter().end(context, item, null, thrown);
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class WriteAdvice { public static class WriteAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter(@Advice.FieldValue("itemWriter") ItemWriter<?> itemWriter) {
@Advice.FieldValue("itemWriter") ItemWriter<?> itemWriter, return AdviceScope.enter(ITEM_OPERATION_WRITE);
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || itemWriter == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_WRITE);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
if (scope == null) {
return;
}
scope.close(); if (adviceScope != null) {
itemInstrumenter().end(context, item, null, thrown); adviceScope.exit(thrown);
}
} }
} }
} }

View File

@ -5,23 +5,18 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.SpringBatchInstrumentationConfig.shouldTraceItems;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_READ; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.ITEM_OPERATION_READ;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.getChunkContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item.ItemSingletons.itemInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.isProtected;
import static net.bytebuddy.matcher.ElementMatchers.named; 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.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.AdviceScope;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.scope.context.ChunkContext;
// item read instrumentation *cannot* use ItemReadListener: sometimes afterRead() is not called // item read instrumentation *cannot* use ItemReadListener: sometimes afterRead() is not called
// after beforeRead(), using listener here would cause unfinished spans/scopes // after beforeRead(), using listener here would cause unfinished spans/scopes
@ -41,38 +36,19 @@ public class SimpleChunkProviderInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ReadAdvice { public static class ReadAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static AdviceScope onEnter() {
@Advice.Local("otelContext") Context context, return AdviceScope.enter(ITEM_OPERATION_READ);
@Advice.Local("otelScope") Scope scope,
@Advice.Local("otelItem") String item) {
Context parentContext = currentContext();
ChunkContext chunkContext = getChunkContext(parentContext);
if (chunkContext == null || !shouldTraceItems()) {
return;
}
item = ItemSingletons.itemName(chunkContext, ITEM_OPERATION_READ);
if (!itemInstrumenter().shouldStart(parentContext, item)) {
return;
}
context = itemInstrumenter().start(parentContext, item);
scope = context.makeCurrent();
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Thrown Throwable thrown, @Advice.Thrown @Nullable Throwable thrown,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope, if (adviceScope != null) {
@Advice.Local("otelItem") String item) { adviceScope.exit(thrown);
if (scope == null) {
return;
} }
scope.close();
itemInstrumenter().end(context, item, null, thrown);
} }
} }
} }

View File

@ -10,14 +10,11 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.ContextAndScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.job.builder.JobBuilderHelper; import org.springframework.batch.core.job.builder.JobBuilderHelper;
public class JobBuilderHelperInstrumentation implements TypeInstrumentation { public class JobBuilderHelperInstrumentation implements TypeInstrumentation {
@ -42,9 +39,7 @@ public class JobBuilderHelperInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.This JobBuilderHelper<?> jobBuilder) { public static void onEnter(@Advice.This JobBuilderHelper<?> jobBuilder) {
VirtualField<JobExecution, ContextAndScope> executionVirtualField = jobBuilder.listener(new TracingJobExecutionListener());
VirtualField.find(JobExecution.class, ContextAndScope.class);
jobBuilder.listener(new TracingJobExecutionListener(executionVirtualField));
} }
} }
} }

View File

@ -12,15 +12,14 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.ContextAndScope; import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.jsr.configuration.xml.JobFactoryBean; import org.springframework.batch.core.jsr.configuration.xml.JobFactoryBean;
public class JobFactoryBeanInstrumentation implements TypeInstrumentation { public class JobFactoryBeanInstrumentation implements TypeInstrumentation {
@ -55,20 +54,17 @@ public class JobFactoryBeanInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class SetListenersAdvice { public static class SetListenersAdvice {
@AssignReturned.ToArguments(@ToArgument(0))
@Advice.AssignReturned.AsScalar
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.Argument(value = 0, readOnly = false) Object[] listeners) { public static Object[] onEnter(@Advice.Argument(0) @Nullable Object[] listeners) {
VirtualField<JobExecution, ContextAndScope> executionVirtualField =
VirtualField.find(JobExecution.class, ContextAndScope.class);
JobExecutionListener tracingListener = new TracingJobExecutionListener(executionVirtualField);
if (listeners == null) { if (listeners == null) {
listeners = new Object[] {tracingListener}; return new Object[] {new TracingJobExecutionListener()};
} else {
Object[] newListeners = new Object[listeners.length + 1];
newListeners[0] = tracingListener;
System.arraycopy(listeners, 0, newListeners, 1, listeners.length);
listeners = newListeners;
} }
Object[] newListeners = new Object[listeners.length + 1];
newListeners[0] = new TracingJobExecutionListener();
System.arraycopy(listeners, 0, newListeners, 1, listeners.length);
return newListeners;
} }
} }
} }

View File

@ -12,14 +12,14 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.ContextAndScope; import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean; import org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean;
@ -55,21 +55,18 @@ public class JobParserJobFactoryBeanInstrumentation implements TypeInstrumentati
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class SetListenersAdvice { public static class SetListenersAdvice {
@AssignReturned.ToArguments(@ToArgument(0))
@Advice.AssignReturned.AsScalar
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static JobExecutionListener[] onEnter(
@Advice.Argument(value = 0, readOnly = false) JobExecutionListener[] listeners) { @Advice.Argument(0) @Nullable JobExecutionListener[] listeners) {
VirtualField<JobExecution, ContextAndScope> executionVirtualField =
VirtualField.find(JobExecution.class, ContextAndScope.class);
JobExecutionListener tracingListener = new TracingJobExecutionListener(executionVirtualField);
if (listeners == null) { if (listeners == null) {
listeners = new JobExecutionListener[] {tracingListener}; return new JobExecutionListener[] {new TracingJobExecutionListener()};
} else {
JobExecutionListener[] newListeners = new JobExecutionListener[listeners.length + 1];
newListeners[0] = tracingListener;
System.arraycopy(listeners, 0, newListeners, 1, listeners.length);
listeners = newListeners;
} }
JobExecutionListener[] newListeners = new JobExecutionListener[listeners.length + 1];
newListeners[0] = new TracingJobExecutionListener();
System.arraycopy(listeners, 0, newListeners, 1, listeners.length);
return newListeners;
} }
} }
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.job; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.job;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.job.JobSingletons.jobInstrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.job.JobSingletons.jobInstrumenter;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
@ -18,16 +17,14 @@ import org.springframework.batch.core.JobExecutionListener;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
public final class TracingJobExecutionListener implements JobExecutionListener, Ordered { public final class TracingJobExecutionListener implements JobExecutionListener, Ordered {
private final VirtualField<JobExecution, ContextAndScope> executionVirtualField; private static final VirtualField<JobExecution, ContextAndScope> CONTEXT_AND_SCOPE =
VirtualField.find(JobExecution.class, ContextAndScope.class);
public TracingJobExecutionListener( public TracingJobExecutionListener() {}
VirtualField<JobExecution, ContextAndScope> executionVirtualField) {
this.executionVirtualField = executionVirtualField;
}
@Override @Override
public void beforeJob(JobExecution jobExecution) { public void beforeJob(JobExecution jobExecution) {
Context parentContext = currentContext(); Context parentContext = Context.current();
if (!jobInstrumenter().shouldStart(parentContext, jobExecution)) { if (!jobInstrumenter().shouldStart(parentContext, jobExecution)) {
return; return;
} }
@ -35,16 +32,16 @@ public final class TracingJobExecutionListener implements JobExecutionListener,
Context context = jobInstrumenter().start(parentContext, jobExecution); Context context = jobInstrumenter().start(parentContext, jobExecution);
// beforeJob & afterJob always execute on the same thread // beforeJob & afterJob always execute on the same thread
Scope scope = context.makeCurrent(); Scope scope = context.makeCurrent();
executionVirtualField.set(jobExecution, new ContextAndScope(context, scope)); CONTEXT_AND_SCOPE.set(jobExecution, new ContextAndScope(context, scope));
} }
@Override @Override
public void afterJob(JobExecution jobExecution) { public void afterJob(JobExecution jobExecution) {
ContextAndScope contextAndScope = executionVirtualField.get(jobExecution); ContextAndScope contextAndScope = CONTEXT_AND_SCOPE.get(jobExecution);
if (contextAndScope == null) { if (contextAndScope == null) {
return; return;
} }
executionVirtualField.set(jobExecution, null); CONTEXT_AND_SCOPE.set(jobExecution, null);
contextAndScope.closeScope(); contextAndScope.closeScope();
jobInstrumenter().end(contextAndScope.getContext(), jobExecution, null, null); jobInstrumenter().end(contextAndScope.getContext(), jobExecution, null, null);
} }

View File

@ -10,14 +10,11 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.ContextAndScope;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.step.builder.StepBuilderHelper; import org.springframework.batch.core.step.builder.StepBuilderHelper;
public class StepBuilderHelperInstrumentation implements TypeInstrumentation { public class StepBuilderHelperInstrumentation implements TypeInstrumentation {
@ -41,9 +38,7 @@ public class StepBuilderHelperInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.This StepBuilderHelper<?> stepBuilder) { public static void onEnter(@Advice.This StepBuilderHelper<?> stepBuilder) {
VirtualField<StepExecution, ContextAndScope> executionVirtualField = stepBuilder.listener(new TracingStepExecutionListener());
VirtualField.find(StepExecution.class, ContextAndScope.class);
stepBuilder.listener(new TracingStepExecutionListener(executionVirtualField));
} }
} }
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.step; package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.step;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.step.StepSingletons.stepInstrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.step.StepSingletons.stepInstrumenter;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
@ -19,16 +18,14 @@ import org.springframework.batch.core.StepExecutionListener;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
public final class TracingStepExecutionListener implements StepExecutionListener, Ordered { public final class TracingStepExecutionListener implements StepExecutionListener, Ordered {
private final VirtualField<StepExecution, ContextAndScope> executionVirtualField; private static final VirtualField<StepExecution, ContextAndScope> CONTEXT_AND_SCOPE =
VirtualField.find(StepExecution.class, ContextAndScope.class);
public TracingStepExecutionListener( public TracingStepExecutionListener() {}
VirtualField<StepExecution, ContextAndScope> executionVirtualField) {
this.executionVirtualField = executionVirtualField;
}
@Override @Override
public void beforeStep(StepExecution stepExecution) { public void beforeStep(StepExecution stepExecution) {
Context parentContext = currentContext(); Context parentContext = Context.current();
if (!stepInstrumenter().shouldStart(parentContext, stepExecution)) { if (!stepInstrumenter().shouldStart(parentContext, stepExecution)) {
return; return;
} }
@ -36,17 +33,17 @@ public final class TracingStepExecutionListener implements StepExecutionListener
Context context = stepInstrumenter().start(parentContext, stepExecution); Context context = stepInstrumenter().start(parentContext, stepExecution);
// beforeStep & afterStep always execute on the same thread // beforeStep & afterStep always execute on the same thread
Scope scope = context.makeCurrent(); Scope scope = context.makeCurrent();
executionVirtualField.set(stepExecution, new ContextAndScope(context, scope)); CONTEXT_AND_SCOPE.set(stepExecution, new ContextAndScope(context, scope));
} }
@Override @Override
public ExitStatus afterStep(StepExecution stepExecution) { public ExitStatus afterStep(StepExecution stepExecution) {
ContextAndScope contextAndScope = executionVirtualField.get(stepExecution); ContextAndScope contextAndScope = CONTEXT_AND_SCOPE.get(stepExecution);
if (contextAndScope == null) { if (contextAndScope == null) {
return null; return null;
} }
executionVirtualField.set(stepExecution, null); CONTEXT_AND_SCOPE.set(stepExecution, null);
contextAndScope.closeScope(); contextAndScope.closeScope();
stepInstrumenter().end(contextAndScope.getContext(), stepExecution, null, null); stepInstrumenter().end(contextAndScope.getContext(), stepExecution, null, null);
return null; return null;

View File

@ -13,6 +13,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -33,8 +34,10 @@ public class AutoConfigurationImportSelectorInstrumentation implements TypeInstr
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class GetCandidateConfigurationsAdvice { public static class GetCandidateConfigurationsAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.Return(readOnly = false) List<String> configurations) { public static List<String> onExit(@Advice.Return List<String> originalConfigurations) {
List<String> configurations = originalConfigurations;
if (configurations.contains( if (configurations.contains(
"org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration")) { "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration")) {
List<String> configs = new ArrayList<>(configurations.size() + 1); List<String> configs = new ArrayList<>(configurations.size() + 1);
@ -44,6 +47,7 @@ public class AutoConfigurationImportSelectorInstrumentation implements TypeInstr
configs.add(OpenTelemetryMeterRegistryAutoConfiguration.class.getName()); configs.add(OpenTelemetryMeterRegistryAutoConfiguration.class.getName());
configurations = configs; configurations = configs;
} }
return configurations;
} }
} }
} }

View File

@ -69,4 +69,9 @@ public class SpringBootActuatorInstrumentationModule extends InstrumentationModu
// produces a lot of metrics that are already captured - e.g. JVM memory usage // produces a lot of metrics that are already captured - e.g. JVM memory usage
return false; return false;
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -40,4 +40,9 @@ public class SpringAwsSqsInstrumentationModule extends InstrumentationModule
new AcknowledgementExecutionContextInstrumentation(), new AcknowledgementExecutionContextInstrumentation(),
new MessageHeaderUtilsInstrumentation()); new MessageHeaderUtilsInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -13,6 +13,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -32,9 +33,10 @@ public class SqsTemplateInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class GetQueueAttributesAdvice { public static class GetQueueAttributesAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class)
public static void methodExit(@Advice.Return(readOnly = false) CompletableFuture<?> result) { public static CompletableFuture<?> methodExit(@Advice.Return CompletableFuture<?> result) {
result = CompletableFutureWrapper.wrap(result, Java8BytecodeBridge.currentContext()); return CompletableFutureWrapper.wrap(result, Java8BytecodeBridge.currentContext());
} }
} }
} }

View File

@ -10,12 +10,14 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringJmsInstrumentationModule extends InstrumentationModule { public class SpringJmsInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringJmsInstrumentationModule() { public SpringJmsInstrumentationModule() {
super("spring-jms", "spring-jms-2.0"); super("spring-jms", "spring-jms-2.0");
@ -34,4 +36,9 @@ public class SpringJmsInstrumentationModule extends InstrumentationModule {
new JmsDestinationAccessorInstrumentation(), new JmsDestinationAccessorInstrumentation(),
new AbstractPollingMessageListenerContainerInstrumentation()); new AbstractPollingMessageListenerContainerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -15,12 +15,12 @@ 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.context.Scope;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.bootstrap.jms.JmsReceiveContextHolder; import io.opentelemetry.javaagent.bootstrap.jms.JmsReceiveContextHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination; import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination;
import io.opentelemetry.javaagent.instrumentation.jms.v1_1.JavaxMessageAdapter; import io.opentelemetry.javaagent.instrumentation.jms.v1_1.JavaxMessageAdapter;
import javax.annotation.Nullable;
import javax.jms.Message; import javax.jms.Message;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
@ -52,39 +52,53 @@ public class SpringJmsMessageListenerInstrumentation implements TypeInstrumentat
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class MessageListenerAdvice { public static class MessageListenerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final MessageWithDestination request;
@Advice.Argument(0) Message message, private final Context context;
@Advice.Local("otelRequest") MessageWithDestination request, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext(); private AdviceScope(MessageWithDestination request, Context context, Scope scope) {
this.request = request;
this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(Message message) {
Context parentContext = Context.current();
Context receiveContext = JmsReceiveContextHolder.getReceiveContext(parentContext); Context receiveContext = JmsReceiveContextHolder.getReceiveContext(parentContext);
if (receiveContext != null) { if (receiveContext != null) {
parentContext = receiveContext; parentContext = receiveContext;
} }
request = MessageWithDestination.create(JavaxMessageAdapter.create(message), null);
MessageWithDestination request =
MessageWithDestination.create(JavaxMessageAdapter.create(message), null);
if (!listenerInstrumenter().shouldStart(parentContext, request)) { if (!listenerInstrumenter().shouldStart(parentContext, request)) {
return; return null;
}
Context context = listenerInstrumenter().start(parentContext, request);
return new AdviceScope(request, context, context.makeCurrent());
} }
context = listenerInstrumenter().start(parentContext, request); public void exit(Throwable throwable) {
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Local("otelRequest") MessageWithDestination request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
if (scope == null) {
return;
}
scope.close(); scope.close();
listenerInstrumenter().end(context, request, null, throwable); listenerInstrumenter().end(context, request, null, throwable);
} }
} }
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) Message message) {
return AdviceScope.enter(message);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown @Nullable Throwable throwable,
@Advice.Enter @Nullable AdviceScope adviceScope) {
if (adviceScope != null) {
adviceScope.exit(throwable);
}
}
}
} }

View File

@ -12,11 +12,13 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringJmsInstrumentationModule extends InstrumentationModule { public class SpringJmsInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringJmsInstrumentationModule() { public SpringJmsInstrumentationModule() {
super("spring-jms", "spring-jms-6.0"); super("spring-jms", "spring-jms-6.0");
@ -35,4 +37,9 @@ public class SpringJmsInstrumentationModule extends InstrumentationModule {
new JmsDestinationAccessorInstrumentation(), new JmsDestinationAccessorInstrumentation(),
new AbstractPollingMessageListenerContainerInstrumentation()); new AbstractPollingMessageListenerContainerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -15,13 +15,13 @@ 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.context.Scope;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.bootstrap.jms.JmsReceiveContextHolder; import io.opentelemetry.javaagent.bootstrap.jms.JmsReceiveContextHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination; import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination;
import io.opentelemetry.javaagent.instrumentation.jms.v3_0.JakartaMessageAdapter; import io.opentelemetry.javaagent.instrumentation.jms.v3_0.JakartaMessageAdapter;
import jakarta.jms.Message; import jakarta.jms.Message;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -52,39 +52,54 @@ public class SpringJmsMessageListenerInstrumentation implements TypeInstrumentat
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class MessageListenerAdvice { public static class MessageListenerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final MessageWithDestination request;
@Advice.Argument(0) Message message, private final Context context;
@Advice.Local("otelRequest") MessageWithDestination request, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext(); private AdviceScope(MessageWithDestination request, Context context, Scope scope) {
this.request = request;
this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(Message message) {
Context parentContext = Context.current();
Context receiveContext = JmsReceiveContextHolder.getReceiveContext(parentContext); Context receiveContext = JmsReceiveContextHolder.getReceiveContext(parentContext);
if (receiveContext != null) { if (receiveContext != null) {
parentContext = receiveContext; parentContext = receiveContext;
} }
request = MessageWithDestination.create(JakartaMessageAdapter.create(message), null); MessageWithDestination request =
MessageWithDestination.create(JakartaMessageAdapter.create(message), null);
if (!listenerInstrumenter().shouldStart(parentContext, request)) { if (!listenerInstrumenter().shouldStart(parentContext, request)) {
return; return null;
} }
context = listenerInstrumenter().start(parentContext, request); Context context = listenerInstrumenter().start(parentContext, request);
scope = context.makeCurrent(); return new AdviceScope(request, context, context.makeCurrent());
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public void exit(Throwable throwable) {
public static void stopSpan(
@Advice.Local("otelRequest") MessageWithDestination request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
if (scope == null) {
return;
}
scope.close(); scope.close();
listenerInstrumenter().end(context, request, null, throwable); listenerInstrumenter().end(context, request, null, throwable);
} }
} }
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) Message message) {
return AdviceScope.enter(message);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown @Nullable Throwable throwable,
@Advice.Enter @Nullable AdviceScope adviceScope) {
if (adviceScope != null) {
adviceScope.exit(throwable);
}
}
}
} }

View File

@ -14,6 +14,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.kafka.listener.RecordInterceptor; import org.springframework.kafka.listener.RecordInterceptor;
@ -43,9 +44,11 @@ public class AbstractMessageListenerContainerInstrumentation implements TypeInst
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class GetRecordInterceptorAdvice { public static class GetRecordInterceptorAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class)
public static <K, V> void onExit( public static <K, V> RecordInterceptor<K, V> onExit(
@Advice.Return(readOnly = false) RecordInterceptor<K, V> interceptor) { @Advice.Return RecordInterceptor<K, V> originalInterceptor) {
RecordInterceptor<K, V> interceptor = originalInterceptor;
if (interceptor == null if (interceptor == null
|| !interceptor || !interceptor
@ -55,6 +58,7 @@ public class AbstractMessageListenerContainerInstrumentation implements TypeInst
"io.opentelemetry.instrumentation.spring.kafka.v2_7.InstrumentedRecordInterceptor")) { "io.opentelemetry.instrumentation.spring.kafka.v2_7.InstrumentedRecordInterceptor")) {
interceptor = telemetry().createRecordInterceptor(interceptor); interceptor = telemetry().createRecordInterceptor(interceptor);
} }
return interceptor;
} }
} }
} }

View File

@ -19,6 +19,7 @@ import io.opentelemetry.javaagent.bootstrap.kafka.KafkaClientsConsumerProcessTra
import io.opentelemetry.javaagent.bootstrap.spring.SpringSchedulingTaskTracing; import io.opentelemetry.javaagent.bootstrap.spring.SpringSchedulingTaskTracing;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -77,37 +78,53 @@ public class ListenerConsumerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class InvokeBatchAdvice { public static class InvokeBatchAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final KafkaReceiveRequest request;
@Advice.Argument(0) ConsumerRecords<?, ?> records, private final Context context;
@Advice.FieldValue("consumer") Consumer<?, ?> consumer, private final Scope scope;
@Advice.Local("otelRequest") KafkaReceiveRequest request,
@Advice.Local("otelContext") Context context, private AdviceScope(KafkaReceiveRequest request, Context context, Scope scope) {
@Advice.Local("otelScope") Scope scope) { this.request = request;
this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(ConsumerRecords<?, ?> records, Consumer<?, ?> consumer) {
KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.get(records); KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.get(records);
Context receiveContext = consumerContext.getContext(); Context receiveContext = consumerContext.getContext();
// use the receive CONSUMER span as parent if it's available // use the receive CONSUMER span as parent if it's available
Context parentContext = receiveContext != null ? receiveContext : Context.current(); Context parentContext = receiveContext != null ? receiveContext : Context.current();
KafkaReceiveRequest request = KafkaReceiveRequest.create(records, consumer);
request = KafkaReceiveRequest.create(records, consumer); if (!batchProcessInstrumenter().shouldStart(parentContext, request)) {
if (batchProcessInstrumenter().shouldStart(parentContext, request)) { return null;
context = batchProcessInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
} }
Context context = batchProcessInstrumenter().start(parentContext, request);
return new AdviceScope(request, context, context.makeCurrent());
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public void exit(@Nullable Throwable throwable) {
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") KafkaReceiveRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); scope.close();
batchProcessInstrumenter().end(context, request, null, throwable); batchProcessInstrumenter().end(context, request, null, throwable);
} }
} }
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(
@Advice.Argument(0) ConsumerRecords<?, ?> records,
@Advice.FieldValue("consumer") Consumer<?, ?> consumer) {
return AdviceScope.enter(records, consumer);
}
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(
@Advice.Thrown @Nullable Throwable throwable,
@Advice.Enter @Nullable AdviceScope adviceScope) {
if (adviceScope != null) {
adviceScope.exit(throwable);
}
}
}
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringKafkaInstrumentationModule extends InstrumentationModule { public class SpringKafkaInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringKafkaInstrumentationModule() { public SpringKafkaInstrumentationModule() {
super("spring-kafka", "spring-kafka-2.7"); super("spring-kafka", "spring-kafka-2.7");
} }
@ -24,4 +26,9 @@ public class SpringKafkaInstrumentationModule extends InstrumentationModule {
new AbstractMessageListenerContainerInstrumentation(), new AbstractMessageListenerContainerInstrumentation(),
new ListenerConsumerInstrumentation()); new ListenerConsumerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -15,6 +15,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.VirtualFieldStore; import io.opentelemetry.javaagent.instrumentation.pulsar.v2_8.VirtualFieldStore;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -38,29 +39,40 @@ public class DefaultPulsarMessageListenerContainerInstrumentation implements Typ
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class DispatchMessageToListenerAdvice { public static class DispatchMessageToListenerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final Context context;
@Advice.Argument(0) Message<?> message, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { public AdviceScope(Context context, Scope scope) {
Context parentContext = VirtualFieldStore.extract(message); this.context = context;
if (instrumenter().shouldStart(parentContext, message)) { this.scope = scope;
context = instrumenter().start(parentContext, message);
scope = context.makeCurrent();
} }
public void exit(@Nullable Throwable throwable, Message<?> message) {
scope.close();
instrumenter().end(context, message, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) Message<?> message) {
Context parentContext = VirtualFieldStore.extract(message);
if (!instrumenter().shouldStart(parentContext, message)) {
return null;
}
Context context = instrumenter().start(parentContext, message);
return new AdviceScope(context, context.makeCurrent());
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit( public static void onExit(
@Advice.Argument(0) Message<?> message, @Advice.Argument(0) Message<?> message,
@Advice.Local("otelContext") Context context, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelScope") Scope scope, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Thrown Throwable throwable) { if (adviceScope != null) {
if (scope == null) { adviceScope.exit(throwable, message);
return;
} }
scope.close();
instrumenter().end(context, message, null, throwable);
} }
} }
} }

View File

@ -11,11 +11,13 @@ import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringPulsarInstrumentationModule extends InstrumentationModule { public class SpringPulsarInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringPulsarInstrumentationModule() { public SpringPulsarInstrumentationModule() {
super("spring-pulsar", "spring-pulsar-1.0"); super("spring-pulsar", "spring-pulsar-1.0");
} }
@ -31,4 +33,9 @@ public class SpringPulsarInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new DefaultPulsarMessageListenerContainerInstrumentation()); return singletonList(new DefaultPulsarMessageListenerContainerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -15,6 +15,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -40,34 +41,46 @@ public class AbstractMessageListenerContainerInstrumentation implements TypeInst
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class InvokeListenerAdvice { public static class InvokeListenerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static class AdviceScope {
@Advice.Argument(1) Object data, private final Context context;
@Advice.Local("otelContext") Context context, private final Scope scope;
@Advice.Local("otelScope") Scope scope) {
if (!(data instanceof Message)) { public AdviceScope(Context context, Scope scope) {
return; this.context = context;
this.scope = scope;
} }
public void exit(@Nullable Throwable throwable, Message message) {
scope.close();
instrumenter().end(context, message, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(1) Object data) {
if (!(data instanceof Message)) {
return null;
}
Context parentContext = Java8BytecodeBridge.currentContext(); Context parentContext = Java8BytecodeBridge.currentContext();
Message message = (Message) data; Message message = (Message) data;
if (instrumenter().shouldStart(parentContext, message)) { if (!instrumenter().shouldStart(parentContext, message)) {
context = instrumenter().start(parentContext, message); return null;
scope = context.makeCurrent();
} }
Context context = instrumenter().start(parentContext, message);
return new AdviceScope(context, context.makeCurrent());
} }
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onEnter( public static void onEnter(
@Advice.Argument(1) Object data, @Advice.Argument(1) Object data,
@Advice.Local("otelContext") Context context, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelScope") Scope scope, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Thrown Throwable throwable) { if (adviceScope == null || !(data instanceof Message)) {
if (scope == null || !(data instanceof Message)) {
return; return;
} }
scope.close(); adviceScope.exit(throwable, (Message) data);
instrumenter().end(context, (Message) data, null, throwable);
} }
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringRabbitInstrumentationModule extends InstrumentationModule { public class SpringRabbitInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringRabbitInstrumentationModule() { public SpringRabbitInstrumentationModule() {
super("spring-rabbit", "spring-rabbit-1.0"); super("spring-rabbit", "spring-rabbit-1.0");
} }
@ -22,4 +24,9 @@ public class SpringRabbitInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new AbstractMessageListenerContainerInstrumentation()); return singletonList(new AbstractMessageListenerContainerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -10,12 +10,14 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.instrumentation.spring.rmi.v4_0.client.ClientInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.rmi.v4_0.client.ClientInstrumentation;
import io.opentelemetry.javaagent.instrumentation.spring.rmi.v4_0.server.ServerInstrumentation; import io.opentelemetry.javaagent.instrumentation.spring.rmi.v4_0.server.ServerInstrumentation;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringRmiInstrumentationModule extends InstrumentationModule { public class SpringRmiInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringRmiInstrumentationModule() { public SpringRmiInstrumentationModule() {
super("spring-rmi", "spring-rmi-4.0"); super("spring-rmi", "spring-rmi-4.0");
@ -25,4 +27,9 @@ public class SpringRmiInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return asList(new ClientInstrumentation(), new ServerInstrumentation()); return asList(new ClientInstrumentation(), new ServerInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -17,6 +17,7 @@ import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -42,35 +43,42 @@ public class ClientInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class InvokeMethodAdvice { public static class InvokeMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final Method method;
@Advice.Argument(0) MethodInvocation methodInv, private final Context context;
@Advice.Local("method") Method method, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
method = methodInv.getMethod(); public AdviceScope(Method method, Context context, Scope scope) {
Context parentContext = Java8BytecodeBridge.currentContext(); this.method = method;
if (!clientInstrumenter().shouldStart(parentContext, method)) { this.context = context;
return; this.scope = scope;
} }
context = clientInstrumenter().start(parentContext, method); public void exit(@Nullable Throwable throwable) {
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Local("method") Method method,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); scope.close();
clientInstrumenter().end(context, method, null, throwable); clientInstrumenter().end(context, method, null, throwable);
} }
} }
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) MethodInvocation methodInv) {
Method method = methodInv.getMethod();
Context parentContext = Java8BytecodeBridge.currentContext();
if (!clientInstrumenter().shouldStart(parentContext, method)) {
return null;
}
Context context = clientInstrumenter().start(parentContext, method);
return new AdviceScope(method, context, context.makeCurrent());
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown @Nullable Throwable throwable,
@Advice.Enter @Nullable AdviceScope adviceScope) {
if (adviceScope != null) {
adviceScope.exit(throwable);
}
}
}
} }

View File

@ -17,6 +17,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMetho
import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -24,6 +25,7 @@ import org.springframework.remoting.rmi.RmiBasedExporter;
import org.springframework.remoting.support.RemoteInvocation; import org.springframework.remoting.support.RemoteInvocation;
public class ServerInstrumentation implements TypeInstrumentation { public class ServerInstrumentation implements TypeInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.springframework.remoting.rmi.RmiBasedExporter"); return named("org.springframework.remoting.rmi.RmiBasedExporter");
@ -41,49 +43,59 @@ public class ServerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class InvokeMethodAdvice { public static class InvokeMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final CallDepth callDepth;
@Advice.This RmiBasedExporter thisObject, private final ClassAndMethod request;
@Advice.Argument(0) RemoteInvocation remoteInv, private final Context context;
@Advice.Local("otelCallDepth") CallDepth callDepth, private final Scope scope;
@Advice.Local("otelRequest") ClassAndMethod request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
callDepth = CallDepth.forClass(RmiBasedExporter.class); private AdviceScope(
CallDepth callDepth, ClassAndMethod request, Context context, Scope scope) {
this.callDepth = callDepth;
this.request = request;
this.context = context;
this.scope = scope;
}
public static AdviceScope enter(
CallDepth callDepth, RmiBasedExporter rmiExporter, RemoteInvocation remoteInvocation) {
if (callDepth.getAndIncrement() > 0) { if (callDepth.getAndIncrement() > 0) {
return; return new AdviceScope(callDepth, null, null, null);
} }
Context parentContext = THREAD_LOCAL_CONTEXT.getAndResetContext(); Context parentContext = THREAD_LOCAL_CONTEXT.getAndResetContext();
Class<?> serverClass = thisObject.getService().getClass(); Class<?> serverClass = rmiExporter.getService().getClass();
String methodName = remoteInv.getMethodName(); String methodName = remoteInvocation.getMethodName();
request = ClassAndMethod.create(serverClass, methodName); ClassAndMethod request = ClassAndMethod.create(serverClass, methodName);
if (!serverInstrumenter().shouldStart(parentContext, request)) { if (!serverInstrumenter().shouldStart(parentContext, request)) {
return; return new AdviceScope(callDepth, request, null, null);
} }
context = serverInstrumenter().start(parentContext, request); Context context = serverInstrumenter().start(parentContext, request);
scope = context.makeCurrent(); return new AdviceScope(callDepth, request, context, context.makeCurrent());
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public void exit(@Nullable Throwable throwable) {
public static void stopSpan( if (callDepth.decrementAndGet() > 0 || scope == null) {
@Advice.Thrown Throwable throwable,
@Advice.Local("otelCallDepth") CallDepth callDepth,
@Advice.Local("otelRequest") ClassAndMethod request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (callDepth.decrementAndGet() > 0) {
return;
}
if (scope == null) {
return; return;
} }
scope.close(); scope.close();
serverInstrumenter().end(context, request, null, throwable); serverInstrumenter().end(context, request, null, throwable);
} }
} }
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(
@Advice.This RmiBasedExporter thisObject, @Advice.Argument(0) RemoteInvocation remoteInv) {
return AdviceScope.enter(CallDepth.forClass(RmiBasedExporter.class), thisObject, remoteInv);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown @Nullable Throwable throwable, @Advice.Enter AdviceScope adviceScope) {
adviceScope.exit(throwable);
}
}
} }

View File

@ -16,6 +16,8 @@ import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.util.ErrorHandler; import org.springframework.util.ErrorHandler;
@ -39,12 +41,14 @@ public class DelegatingErrorHandlingRunnableInstrumentation implements TypeInstr
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class WrapErrorHandlerAdvice { public static class WrapErrorHandlerAdvice {
@AssignReturned.ToArguments(@ToArgument(1))
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static ErrorHandler onEnter(@Advice.Argument(1) ErrorHandler originalErrorHandler) {
@Advice.Argument(value = 1, readOnly = false) ErrorHandler errorHandler) { ErrorHandler errorHandler = originalErrorHandler;
if (errorHandler != null) { if (errorHandler != null) {
errorHandler = new ErrorHandlerWrapper(errorHandler); errorHandler = new ErrorHandlerWrapper(errorHandler);
} }
return errorHandler;
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringSchedulingInstrumentationModule extends InstrumentationModule { public class SpringSchedulingInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringSchedulingInstrumentationModule() { public SpringSchedulingInstrumentationModule() {
super("spring-scheduling", "spring-scheduling-3.1"); super("spring-scheduling", "spring-scheduling-3.1");
@ -24,4 +26,9 @@ public class SpringSchedulingInstrumentationModule extends InstrumentationModule
return asList( return asList(
new TaskSchedulerInstrumentation(), new DelegatingErrorHandlingRunnableInstrumentation()); new TaskSchedulerInstrumentation(), new DelegatingErrorHandlingRunnableInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -13,6 +13,8 @@ import io.opentelemetry.javaagent.bootstrap.spring.SpringSchedulingTaskTracing;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -49,11 +51,14 @@ public class TaskSchedulerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ScheduleMethodAdvice { public static class ScheduleMethodAdvice {
@AssignReturned.ToArguments(@ToArgument(0))
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(@Advice.Argument(value = 0, readOnly = false) Runnable runnable) { public static Runnable onSchedule(@Advice.Argument(0) Runnable originalRunnable) {
Runnable runnable = originalRunnable;
if (SpringSchedulingTaskTracing.wrappingEnabled()) { if (SpringSchedulingTaskTracing.wrappingEnabled()) {
runnable = SpringSchedulingRunnableWrapper.wrapIfNeeded(runnable); runnable = SpringSchedulingRunnableWrapper.wrapIfNeeded(runnable);
} }
return runnable;
} }
} }
} }

View File

@ -14,6 +14,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
@ -45,28 +46,31 @@ public class DispatcherHandlerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class HandleAdvice { public static class HandleAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static Mono<Void> methodExit(
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Argument(0) ServerWebExchange exchange, @Advice.Argument(0) ServerWebExchange exchange,
@Advice.Return(readOnly = false) Mono<Void> mono) { @Advice.Return Mono<Void> originalMono) {
Mono<Void> mono = originalMono;
if (mono != null) { if (mono != null) {
// note: it seems like this code should go in @OnMethodExit of // note: it seems like this code should go in @OnMethodExit of
// HandlerAdapterInstrumentation.HandleAdvice instead, but for some reason "GET to bad // HandlerAdapterInstrumentation.HandleAdvice instead, but for some reason "GET to bad
// endpoint annotation API fail Mono" test fails with that placement // endpoint annotation API fail Mono" test fails with that placement
mono = AdviceUtils.end(mono, exchange); mono = AdviceUtils.end(mono, exchange);
} }
return mono;
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class HandleResultAdvice { public static class HandleResultAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class) @Advice.OnMethodExit(suppress = Throwable.class)
public static void methodExit( public static Mono<Void> methodExit(
@Advice.Argument(0) ServerWebExchange exchange, @Advice.Argument(0) ServerWebExchange exchange, @Advice.Return Mono<Void> mono) {
@Advice.Return(readOnly = false) Mono<Void> mono) { return AdviceUtils.wrapMono(mono, exchange.getAttribute(AdviceUtils.CONTEXT));
mono = AdviceUtils.wrapMono(mono, exchange.getAttribute(AdviceUtils.CONTEXT));
} }
} }
} }

View File

@ -23,7 +23,9 @@ import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResult;
@ -58,45 +60,43 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class HandleAdvice { public static class HandleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void methodEnter( private final Context context;
@Advice.Argument(0) ServerWebExchange exchange, private final Scope scope;
@Advice.Argument(1) Object handler,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
private AdviceScope(Context context, Scope scope) {
this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(ServerWebExchange exchange, Object handler) {
Context parentContext = Context.current(); Context parentContext = Context.current();
// HttpRouteSource.CONTROLLER has useFirst true, and it will update http.route only once // HttpRouteSource.CONTROLLER has useFirst true, and it will update http.route only once
// using the last portion of the nested path. // using the last portion of the nested path.
// HttpRouteSource.NESTED_CONTROLLER has useFirst false, and it will make http.route updated // HttpRouteSource.NESTED_CONTROLLER has useFirst false, and it will make http.route updated
// twice: 1st using the last portion of the nested path, 2nd time using the full nested path. // twice: 1st using the last portion of the nested path, 2nd time using the full nested
// path.
HttpServerRoute.update( HttpServerRoute.update(
parentContext, HttpServerRouteSource.NESTED_CONTROLLER, httpRouteGetter(), exchange); parentContext, HttpServerRouteSource.NESTED_CONTROLLER, httpRouteGetter(), exchange);
if (handler == null) { if (handler == null) {
return; return null;
} }
if (!instrumenter().shouldStart(parentContext, handler)) { if (!instrumenter().shouldStart(parentContext, handler)) {
return; return null;
}
Context context = instrumenter().start(parentContext, handler);
return new AdviceScope(context, context.makeCurrent());
} }
context = instrumenter().start(parentContext, handler); public Mono<HandlerResult> exit(
scope = context.makeCurrent(); Throwable throwable,
} ServerWebExchange exchange,
Object handler,
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) Mono<HandlerResult> mono) {
public static void methodExit(
@Advice.Return(readOnly = false) Mono<HandlerResult> mono,
@Advice.Argument(0) ServerWebExchange exchange,
@Advice.Argument(1) Object handler,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close(); scope.close();
if (throwable != null) { if (throwable != null) {
@ -109,6 +109,31 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
// the Mono is already wrapped at this point, but doesn't read the ON_SPAN_END until // the Mono is already wrapped at this point, but doesn't read the ON_SPAN_END until
// the Mono is resolved, which is after this point // the Mono is resolved, which is after this point
} }
return mono;
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope methodEnter(
@Advice.Argument(0) ServerWebExchange exchange, @Advice.Argument(1) Object handler) {
return AdviceScope.enter(exchange, handler);
}
@AssignReturned.ToReturned
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static Mono<HandlerResult> methodExit(
@Advice.Return Mono<HandlerResult> mono,
@Advice.Argument(0) ServerWebExchange exchange,
@Advice.Argument(1) Object handler,
@Advice.Thrown Throwable throwable,
@Advice.Enter @Nullable AdviceScope adviceScope) {
if (adviceScope == null) {
return mono;
}
return adviceScope.exit(throwable, exchange, handler, mono);
} }
} }
} }

View File

@ -18,6 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.HandlerFunction;
@ -61,14 +62,16 @@ public class RouterFunctionInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class RouteAdvice { public static class RouteAdvice {
@AssignReturned.ToReturned
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit( public static Mono<HandlerFunction<?>> methodExit(
@Advice.This RouterFunction<?> thiz, @Advice.This RouterFunction<?> thiz,
@Advice.Return(readOnly = false) Mono<HandlerFunction<?>> result, @Advice.Return Mono<HandlerFunction<?>> result,
@Advice.Thrown Throwable throwable) { @Advice.Thrown Throwable throwable) {
if (throwable == null) { if (throwable == null) {
result = result.doOnNext(new RouteOnSuccess(thiz)); return result.doOnNext(new RouteOnSuccess(thiz));
} }
return result;
} }
} }
} }

View File

@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class WebfluxServerInstrumentationModule extends InstrumentationModule { public class WebfluxServerInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public WebfluxServerInstrumentationModule() { public WebfluxServerInstrumentationModule() {
super("spring-webflux", "spring-webflux-5.0", "spring-webflux-server"); super("spring-webflux", "spring-webflux-5.0", "spring-webflux-server");
@ -26,4 +28,9 @@ public class WebfluxServerInstrumentationModule extends InstrumentationModule {
new HandlerAdapterInstrumentation(), new HandlerAdapterInstrumentation(),
new RouterFunctionInstrumentation()); new RouterFunctionInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext;
import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -35,6 +36,7 @@ public class ContextHandlerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class CreateOperationsAdvice { public static class CreateOperationsAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope onEnter(@Advice.Argument(0) Channel channel) { public static Scope onEnter(@Advice.Argument(0) Channel channel) {
// set context to the first unprocessed request // set context to the first unprocessed request
@ -46,7 +48,7 @@ public class ContextHandlerInstrumentation implements TypeInstrumentation {
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter Scope scope) { public static void onExit(@Advice.Enter @Nullable Scope scope) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
} }

View File

@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext;
import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts; import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContexts;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -35,6 +36,7 @@ public class HttpTrafficHandlerInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class RunAdvice { public static class RunAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope onEnter( public static Scope onEnter(
@Advice.FieldValue("ctx") ChannelHandlerContext channelHandlerContext) { @Advice.FieldValue("ctx") ChannelHandlerContext channelHandlerContext) {
@ -47,7 +49,7 @@ public class HttpTrafficHandlerInstrumentation implements TypeInstrumentation {
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter Scope scope) { public static void onExit(@Advice.Enter @Nullable Scope scope) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1; package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.SpringWebMvcSingletons.modelAndViewInstrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.SpringWebMvcSingletons.modelAndViewInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.isProtected;
@ -19,6 +18,7 @@ import io.opentelemetry.javaagent.bootstrap.InstrumentationProxyHelper;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -76,29 +76,45 @@ public class DispatcherServletInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class RenderAdvice { public static class RenderAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final Context context;
@Advice.Argument(0) ModelAndView mv, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { private AdviceScope(Context context, Scope scope) {
Context parentContext = currentContext(); this.context = context;
if (modelAndViewInstrumenter().shouldStart(parentContext, mv)) { this.scope = scope;
context = modelAndViewInstrumenter().start(parentContext, mv);
scope = context.makeCurrent();
} }
@Nullable
public static AdviceScope enter(ModelAndView mv) {
Context parentContext = Context.current();
if (!modelAndViewInstrumenter().shouldStart(parentContext, mv)) {
return null;
}
Context context = modelAndViewInstrumenter().start(parentContext, mv);
return new AdviceScope(context, context.makeCurrent());
}
public void exit(ModelAndView mv, @Nullable Throwable throwable) {
scope.close();
modelAndViewInstrumenter().end(context, mv, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) ModelAndView mv) {
return AdviceScope.enter(mv);
} }
@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.Argument(0) ModelAndView mv, @Advice.Argument(0) ModelAndView mv,
@Advice.Thrown Throwable throwable, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope) { if (adviceScope != null) {
if (scope == null) { adviceScope.exit(mv, throwable);
return;
} }
scope.close();
modelAndViewInstrumenter().end(context, mv, null, throwable);
} }
} }
} }

View File

@ -16,13 +16,14 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; 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.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler; import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
@ -54,23 +55,28 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ControllerAdvice { public static class ControllerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void nameResourceAndStartSpan( private final Context context;
@Advice.Argument(0) HttpServletRequest request, private final Scope scope;
@Advice.Argument(2) Object handler,
@Advice.Local("otelContext") Context context, public AdviceScope(Context context, Scope scope) {
@Advice.Local("otelScope") Scope scope) { this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(HttpServletRequest request, Object handler) {
// TODO (trask) should there be a way to customize Instrumenter.shouldStart()? // TODO (trask) should there be a way to customize Instrumenter.shouldStart()?
if (IsGrailsHandler.isGrailsHandler(handler)) { if (IsGrailsHandler.isGrailsHandler(handler)) {
// skip creating handler span for grails, grails instrumentation will take care of it // skip creating handler span for grails, grails instrumentation will take care of it
return; return null;
} }
Context parentContext = Java8BytecodeBridge.currentContext(); Context parentContext = Context.current();
// don't start a new top-level span // don't start a new top-level span
if (!Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) { if (!Span.fromContext(parentContext).getSpanContext().isValid()) {
return; return null;
} }
// Name the parent span based on the matching pattern // Name the parent span based on the matching pattern
@ -78,25 +84,36 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
parentContext, CONTROLLER, SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME, request); parentContext, CONTROLLER, SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME, request);
if (!handlerInstrumenter().shouldStart(parentContext, handler)) { if (!handlerInstrumenter().shouldStart(parentContext, handler)) {
return; return null;
} }
// Now create a span for handler/controller execution. // Now create a span for handler/controller execution.
context = handlerInstrumenter().start(parentContext, handler); Context context = handlerInstrumenter().start(parentContext, handler);
scope = context.makeCurrent(); return new AdviceScope(context, context.makeCurrent());
}
public void exit(Object handler, @Nullable Throwable throwable) {
scope.close();
handlerInstrumenter().end(context, handler, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope nameResourceAndStartSpan(
@Advice.Argument(0) HttpServletRequest request, @Advice.Argument(2) Object handler) {
return AdviceScope.enter(request, handler);
} }
@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.Argument(2) Object handler, @Advice.Argument(2) Object handler,
@Advice.Thrown Throwable throwable, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope) {
if (scope == null) { if (adviceScope != null) {
return; adviceScope.exit(handler, throwable);
} }
scope.close();
handlerInstrumenter().end(context, handler, null, throwable);
} }
} }
} }

View File

@ -53,4 +53,9 @@ public class SpringWebMvcInstrumentationModule extends InstrumentationModule
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return asList(new DispatcherServletInstrumentation(), new HandlerAdapterInstrumentation()); return asList(new DispatcherServletInstrumentation(), new HandlerAdapterInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0; package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.SpringWebMvcSingletons.modelAndViewInstrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.SpringWebMvcSingletons.modelAndViewInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isProtected; import static net.bytebuddy.matcher.ElementMatchers.isProtected;
@ -19,6 +18,7 @@ import io.opentelemetry.javaagent.bootstrap.InstrumentationProxyHelper;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -77,29 +77,45 @@ public class DispatcherServletInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class RenderAdvice { public static class RenderAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void onEnter( private final Context context;
@Advice.Argument(0) ModelAndView mv, private final Scope scope;
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { private AdviceScope(Context context, Scope scope) {
Context parentContext = currentContext(); this.context = context;
if (modelAndViewInstrumenter().shouldStart(parentContext, mv)) { this.scope = scope;
context = modelAndViewInstrumenter().start(parentContext, mv);
scope = context.makeCurrent();
} }
@Nullable
public static AdviceScope enter(ModelAndView mv) {
Context parentContext = Context.current();
if (!modelAndViewInstrumenter().shouldStart(parentContext, mv)) {
return null;
}
Context context = modelAndViewInstrumenter().start(parentContext, mv);
return new AdviceScope(context, context.makeCurrent());
}
public void exit(ModelAndView mv, @Nullable Throwable throwable) {
scope.close();
modelAndViewInstrumenter().end(context, mv, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope onEnter(@Advice.Argument(0) ModelAndView mv) {
return AdviceScope.enter(mv);
} }
@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.Argument(0) ModelAndView mv, @Advice.Argument(0) ModelAndView mv,
@Advice.Thrown Throwable throwable, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope) { if (adviceScope != null) {
if (scope == null) { adviceScope.exit(mv, throwable);
return;
} }
scope.close();
modelAndViewInstrumenter().end(context, mv, null, throwable);
} }
} }
} }

View File

@ -16,14 +16,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; 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.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler; import io.opentelemetry.javaagent.instrumentation.spring.webmvc.IsGrailsHandler;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -54,23 +55,28 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class ControllerAdvice { public static class ControllerAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void nameResourceAndStartSpan( private final Context context;
@Advice.Argument(0) HttpServletRequest request, private final Scope scope;
@Advice.Argument(2) Object handler,
@Advice.Local("otelContext") Context context, private AdviceScope(Context context, Scope scope) {
@Advice.Local("otelScope") Scope scope) { this.context = context;
this.scope = scope;
}
@Nullable
public static AdviceScope enter(HttpServletRequest request, Object handler) {
// TODO (trask) should there be a way to customize Instrumenter.shouldStart()? // TODO (trask) should there be a way to customize Instrumenter.shouldStart()?
if (IsGrailsHandler.isGrailsHandler(handler)) { if (IsGrailsHandler.isGrailsHandler(handler)) {
// skip creating handler span for grails, grails instrumentation will take care of it // skip creating handler span for grails, grails instrumentation will take care of it
return; return null;
} }
Context parentContext = Java8BytecodeBridge.currentContext(); Context parentContext = Context.current();
// don't start a new top-level span // don't start a new top-level span
if (!Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) { if (!Span.fromContext(parentContext).getSpanContext().isValid()) {
return; return null;
} }
// Name the parent span based on the matching pattern // Name the parent span based on the matching pattern
@ -78,25 +84,35 @@ public class HandlerAdapterInstrumentation implements TypeInstrumentation {
parentContext, CONTROLLER, SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME, request); parentContext, CONTROLLER, SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME, request);
if (!handlerInstrumenter().shouldStart(parentContext, handler)) { if (!handlerInstrumenter().shouldStart(parentContext, handler)) {
return; return null;
} }
// Now create a span for handler/controller execution. // Now create a span for handler/controller execution.
context = handlerInstrumenter().start(parentContext, handler); Context context = handlerInstrumenter().start(parentContext, handler);
scope = context.makeCurrent(); return new AdviceScope(context, context.makeCurrent());
}
public void exit(Object handler, @Nullable Throwable throwable) {
scope.close();
handlerInstrumenter().end(context, handler, null, throwable);
}
}
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope nameResourceAndStartSpan(
@Advice.Argument(0) HttpServletRequest request, @Advice.Argument(2) Object handler) {
return AdviceScope.enter(request, handler);
} }
@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.Argument(2) Object handler, @Advice.Argument(2) Object handler,
@Advice.Thrown Throwable throwable, @Advice.Thrown @Nullable Throwable throwable,
@Advice.Local("otelContext") Context context, @Advice.Enter @Nullable AdviceScope adviceScope) {
@Advice.Local("otelScope") Scope scope) { if (adviceScope != null) {
if (scope == null) { adviceScope.exit(handler, throwable);
return;
} }
scope.close();
handlerInstrumenter().end(context, handler, null, throwable);
} }
} }
} }

View File

@ -53,4 +53,9 @@ public class SpringWebMvcInstrumentationModule extends InstrumentationModule
public List<TypeInstrumentation> typeInstrumentations() { public List<TypeInstrumentation> typeInstrumentations() {
return asList(new DispatcherServletInstrumentation(), new HandlerAdapterInstrumentation()); return asList(new DispatcherServletInstrumentation(), new HandlerAdapterInstrumentation());
} }
@Override
public boolean isIndyReady() {
return true;
}
} }

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.spring.ws.v2_0; package io.opentelemetry.javaagent.instrumentation.spring.ws.v2_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.spring.ws.v2_0.SpringWsSingletons.instrumenter; import static io.opentelemetry.javaagent.instrumentation.spring.ws.v2_0.SpringWsSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod; import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
@ -18,6 +17,7 @@ import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@ -51,42 +51,58 @@ public class AnnotatedMethodInstrumentation implements TypeInstrumentation {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class AnnotatedMethodAdvice { public static class AnnotatedMethodAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) public static class AdviceScope {
public static void startSpan( private final CallDepth callDepth;
@Advice.Origin("#t") Class<?> codeClass, private final SpringWsRequest request;
@Advice.Origin("#m") String methodName, private final Context context;
@Advice.Local("otelCallDepth") CallDepth callDepth, private final Scope scope;
@Advice.Local("otelRequest") SpringWsRequest request,
@Advice.Local("otelContext") Context context, private AdviceScope(
@Advice.Local("otelScope") Scope scope) { CallDepth callDepth, SpringWsRequest request, Context context, Scope scope) {
callDepth = CallDepth.forClass(PayloadRoot.class); this.callDepth = callDepth;
this.request = request;
this.context = context;
this.scope = scope;
}
public static AdviceScope enter(CallDepth callDepth, Class<?> codeClass, String methodName) {
if (callDepth.getAndIncrement() > 0) { if (callDepth.getAndIncrement() > 0) {
return; return new AdviceScope(callDepth, null, null, null);
} }
Context parentContext = currentContext(); Context parentContext = Context.current();
request = SpringWsRequest.create(codeClass, methodName); SpringWsRequest request = SpringWsRequest.create(codeClass, methodName);
if (!instrumenter().shouldStart(parentContext, request)) { if (!instrumenter().shouldStart(parentContext, request)) {
return new AdviceScope(callDepth, null, null, null);
}
Context context = instrumenter().start(parentContext, request);
return new AdviceScope(callDepth, request, context, parentContext.makeCurrent());
}
public void exit(@Nullable Throwable throwable) {
if (callDepth.decrementAndGet() > 0) {
return; return;
} }
if (scope == null) {
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("otelCallDepth") CallDepth callDepth,
@Advice.Local("otelRequest") SpringWsRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (callDepth.decrementAndGet() > 0 || scope == null) {
return; return;
} }
scope.close(); scope.close();
instrumenter().end(context, request, null, throwable); instrumenter().end(context, request, null, throwable);
} }
} }
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AdviceScope startSpan(
@Advice.Origin("#t") Class<?> codeClass, @Advice.Origin("#m") String methodName) {
CallDepth callDepth = CallDepth.forClass(PayloadRoot.class);
return AdviceScope.enter(callDepth, codeClass, methodName);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown @Nullable Throwable throwable, @Advice.Enter AdviceScope adviceScope) {
adviceScope.exit(throwable);
}
}
} }

View File

@ -9,12 +9,14 @@ import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@AutoService(InstrumentationModule.class) @AutoService(InstrumentationModule.class)
public class SpringWsInstrumentationModule extends InstrumentationModule { public class SpringWsInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public SpringWsInstrumentationModule() { public SpringWsInstrumentationModule() {
super("spring-ws", "spring-ws-2.0"); super("spring-ws", "spring-ws-2.0");
} }
@ -29,4 +31,9 @@ public class SpringWsInstrumentationModule extends InstrumentationModule {
// this instrumentation only produces controller telemetry // this instrumentation only produces controller telemetry
return super.defaultEnabled(config) && ExperimentalConfig.get().controllerTelemetryEnabled(); return super.defaultEnabled(config) && ExperimentalConfig.get().controllerTelemetryEnabled();
} }
@Override
public boolean isIndyReady() {
return true;
}
} }