From 1a9f3acbd796bb524db9ee4a1e0fd87c08fcb0d8 Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Fri, 10 Feb 2023 11:29:54 -0600 Subject: [PATCH 01/12] WithCurrentSpan annotation --- ...ntelemetry-instrumentation-annotations.txt | 9 +- .../support/AttributeBindingFactory.java | 40 +++++++++ .../support/CombinedAttributeBindings.java | 37 ++++++++ .../support/EmptyAttributeBindings.java | 21 +++++ .../MethodSpanAttributesExtractor.java | 84 +------------------ .../support/SpanAttributesExtractor.java | 42 ++++++++++ .../annotations/WithCurrentSpan.java | 27 ++++++ .../AnnotationInstrumentation.java | 52 ++++++++++++ .../AnnotationInstrumentationModule.java | 39 +++++++++ ...gletons.java => AnnotationSingletons.java} | 26 ++++-- .../WithCurrentSpanInstrumentation.java | 79 +++++++++++++++++ .../WithSpanInstrumentation.java | 45 +--------- .../WithSpanInstrumentationModule.java | 28 ------- .../WithCurrentSpanInstrumentationTest.groovy | 69 +++++++++++++++ .../SpanAttributesWithCurrentSpan.java | 34 ++++++++ 15 files changed, 474 insertions(+), 158 deletions(-) create mode 100644 instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/CombinedAttributeBindings.java create mode 100644 instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/EmptyAttributeBindings.java create mode 100644 instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java create mode 100644 instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/{WithSpanSingletons.java => AnnotationSingletons.java} (77%) create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java delete mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt index df26146497bf..4ed40a0d4f12 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt @@ -1,2 +1,9 @@ Comparing source compatibility of against -No changes. \ No newline at end of file ++++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.WithCurrentSpan (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW INTERFACE: java.lang.annotation.Annotation + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW ANNOTATION: java.lang.annotation.Target + +++ NEW ELEMENT: value=java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.CONSTRUCTOR (+) + +++ NEW ANNOTATION: java.lang.annotation.Retention + +++ NEW ELEMENT: value=java.lang.annotation.RetentionPolicy.RUNTIME (+) diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java index a959394ae5e8..91723aea729f 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.api.annotation.support; import io.opentelemetry.api.common.AttributeKey; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.AbstractList; import java.util.Arrays; @@ -338,4 +340,42 @@ private static AttributeBinding defaultBinding(String name) { AttributeKey key = AttributeKey.stringKey(name); return (setter, arg) -> setter.put(key, arg.toString()); } + + /** + * Creates a binding of the parameters of the traced method to span attributes. + * + * @param method the traced method + * @return the bindings of the parameters + */ + static AttributeBindings bind( + Method method, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) { + AttributeBindings bindings = EmptyAttributeBindings.INSTANCE; + + Parameter[] parameters = method.getParameters(); + if (parameters.length == 0) { + return bindings; + } + + String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters); + if (attributeNames == null || attributeNames.length != parameters.length) { + return bindings; + } + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + String attributeName = attributeNames[i]; + if (attributeName == null || attributeName.isEmpty()) { + continue; + } + + bindings = + new CombinedAttributeBindings( + bindings, + i, + AttributeBindingFactory.createBinding( + attributeName, parameter.getParameterizedType())); + } + + return bindings; + } } diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/CombinedAttributeBindings.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/CombinedAttributeBindings.java new file mode 100644 index 000000000000..961e85da677e --- /dev/null +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/CombinedAttributeBindings.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.annotation.support; + +import io.opentelemetry.api.common.AttributesBuilder; + +/** AttributeBindings implementation that is able to chain multiple AttributeBindings. */ +final class CombinedAttributeBindings implements AttributeBindings { + private final AttributeBindings parent; + private final int index; + private final AttributeBinding binding; + + public CombinedAttributeBindings(AttributeBindings parent, int index, AttributeBinding binding) { + this.parent = parent; + this.index = index; + this.binding = binding; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public void apply(AttributesBuilder target, Object[] args) { + parent.apply(target, args); + if (args != null && args.length > index) { + Object arg = args[index]; + if (arg != null) { + binding.apply(target, arg); + } + } + } +} diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/EmptyAttributeBindings.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/EmptyAttributeBindings.java new file mode 100644 index 000000000000..c7923814a5ad --- /dev/null +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/EmptyAttributeBindings.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.annotation.support; + +import io.opentelemetry.api.common.AttributesBuilder; + +/** Singleton empty implementation of AttributeBindings. */ +enum EmptyAttributeBindings implements AttributeBindings { + INSTANCE; + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public void apply(AttributesBuilder target, Object[] args) {} +} diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java index 4e143eecf1c3..c1be336c4204 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java @@ -10,7 +10,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.internal.cache.Cache; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import javax.annotation.Nullable; /** Extractor of {@link io.opentelemetry.api.common.Attributes} for a traced method. */ @@ -48,7 +47,10 @@ public static MethodSpanAttributesExtractor AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor)); if (!bindings.isEmpty()) { Object[] args = methodArgumentsExtractor.extract(request); bindings.apply(attributes, args); @@ -62,82 +64,4 @@ public void onEnd( REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) {} - - /** - * Creates a binding of the parameters of the traced method to span attributes. - * - * @param method the traced method - * @return the bindings of the parameters - */ - private AttributeBindings bind(Method method) { - AttributeBindings bindings = EmptyAttributeBindings.INSTANCE; - - Parameter[] parameters = method.getParameters(); - if (parameters.length == 0) { - return bindings; - } - - String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters); - if (attributeNames.length != parameters.length) { - return bindings; - } - - for (int i = 0; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - String attributeName = attributeNames[i]; - if (attributeName == null || attributeName.isEmpty()) { - continue; - } - - bindings = - new CombinedAttributeBindings( - bindings, - i, - AttributeBindingFactory.createBinding( - attributeName, parameter.getParameterizedType())); - } - - return bindings; - } - - protected enum EmptyAttributeBindings implements AttributeBindings { - INSTANCE; - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public void apply(AttributesBuilder target, Object[] args) {} - } - - private static final class CombinedAttributeBindings implements AttributeBindings { - private final AttributeBindings parent; - private final int index; - private final AttributeBinding binding; - - public CombinedAttributeBindings( - AttributeBindings parent, int index, AttributeBinding binding) { - this.parent = parent; - this.index = index; - this.binding = binding; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public void apply(AttributesBuilder target, Object[] args) { - parent.apply(target, args); - if (args != null && args.length > index) { - Object arg = args[index]; - if (arg != null) { - binding.apply(target, arg); - } - } - } - } } diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java new file mode 100644 index 000000000000..975733a3d386 --- /dev/null +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.annotation.support; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.internal.cache.Cache; +import java.lang.reflect.Method; + +/** Extractor of {@link io.opentelemetry.api.common.Attributes} for a traced method. */ +public final class SpanAttributesExtractor { + + private final Cache cache; + private final ParameterAttributeNamesExtractor parameterAttributeNamesExtractor; + + public static SpanAttributesExtractor newInstance( + ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) { + return new SpanAttributesExtractor(parameterAttributeNamesExtractor, new MethodCache<>()); + } + + SpanAttributesExtractor( + ParameterAttributeNamesExtractor parameterAttributeNamesExtractor, + Cache cache) { + this.parameterAttributeNamesExtractor = parameterAttributeNamesExtractor; + this.cache = cache; + } + + public Attributes getAttributes(Method method, Object[] args) { + AttributesBuilder attributes = Attributes.builder(); + AttributeBindings bindings = + cache.computeIfAbsent( + method, + (Method m) -> AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor)); + if (!bindings.isEmpty()) { + bindings.apply(attributes, args); + } + return attributes.build(); + } +} diff --git a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java new file mode 100644 index 000000000000..dd81b0020f29 --- /dev/null +++ b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation marks that an execution of this method or constructor is able to add attributes + * to the current span {@link io.opentelemetry.api.trace.Span}. + * + *

Application developers can use this annotation to signal OpenTelemetry auto-instrumentation + * that attributes annotated with the {@link + * io.opentelemetry.instrumentation.annotations.SpanAttribute} should be detected and added to the + * current span. + * + *

If you are a library developer, then probably you should NOT use this annotation, because it + * is non-functional without some form of auto-instrumentation. + */ +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface WithCurrentSpan {} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java new file mode 100644 index 000000000000..ebaca6e4e5fe --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; + +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.none; + +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser; +import java.util.Map; +import java.util.Set; +import net.bytebuddy.description.ByteCodeElement; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public abstract class AnnotationInstrumentation implements TypeInstrumentation { + + protected static final String TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG = + "otel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods"; + + /* + Returns a matcher for all methods that should be excluded from auto-instrumentation by + annotation-based advices. + */ + protected static ElementMatcher.Junction configureExcludedMethods() { + ElementMatcher.Junction result = none(); + + Map> excludedMethods = + MethodsConfigurationParser.parse( + InstrumentationConfig.get().getString(TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG)); + for (Map.Entry> entry : excludedMethods.entrySet()) { + String className = entry.getKey(); + ElementMatcher.Junction matcher = + isDeclaredBy(ElementMatchers.named(className)); + + Set methodNames = entry.getValue(); + if (!methodNames.isEmpty()) { + matcher = matcher.and(namedOneOf(methodNames.toArray(new String[0]))); + } + + result = result.or(matcher); + } + + return result; + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java new file mode 100644 index 000000000000..fac9d378a8ab --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; + +import static java.util.Arrays.asList; + +import application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan; +import application.io.opentelemetry.instrumentation.annotations.WithSpan; +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; + +/** + * Instrumentation for methods annotated with {@link WithSpan} and {@link WithCurrentSpan} + * annotations. + */ +@AutoService(InstrumentationModule.class) +public class AnnotationInstrumentationModule extends InstrumentationModule { + + public AnnotationInstrumentationModule() { + super("opentelemetry-instrumentation-annotations"); + } + + @Override + public int order() { + // Run first to ensure other automatic intstrumentation is added after and therefore is executed + // earlier in the instrumented method and create the span to attach attributes to. + return Integer.MIN_VALUE; + } + + @Override + public List typeInstrumentations() { + return asList(new WithSpanInstrumentation(), new WithCurrentSpanInstrumentation()); + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java similarity index 77% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java index c0fd5bd7f63b..ade948bf563d 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java @@ -11,21 +11,23 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor; +import io.opentelemetry.instrumentation.api.annotation.support.SpanAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.util.SpanNames; import java.lang.reflect.Method; import java.util.logging.Logger; -public final class WithSpanSingletons { +public final class AnnotationSingletons { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.opentelemetry-instrumentation-annotations-1.16"; - private static final Logger logger = Logger.getLogger(WithSpanSingletons.class.getName()); + private static final Logger logger = Logger.getLogger(AnnotationSingletons.class.getName()); private static final Instrumenter INSTRUMENTER = createInstrumenter(); private static final Instrumenter INSTRUMENTER_WITH_ATTRIBUTES = createInstrumenterWithAttributes(); + private static final SpanAttributesExtractor ATTRIBUTES = createAttributesExtractor(); public static Instrumenter instrumenter() { return INSTRUMENTER; @@ -35,18 +37,24 @@ public static Instrumenter instrumenterWithAttributes() { return INSTRUMENTER_WITH_ATTRIBUTES; } + public static SpanAttributesExtractor attributes() { + return ATTRIBUTES; + } + private static Instrumenter createInstrumenter() { return Instrumenter.builder( - GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethod) + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + AnnotationSingletons::spanNameFromMethod) .addAttributesExtractor(CodeAttributesExtractor.create(MethodCodeAttributesGetter.INSTANCE)) - .buildInstrumenter(WithSpanSingletons::spanKindFromMethod); + .buildInstrumenter(AnnotationSingletons::spanKindFromMethod); } private static Instrumenter createInstrumenterWithAttributes() { return Instrumenter.builder( GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, - WithSpanSingletons::spanNameFromMethodRequest) + AnnotationSingletons::spanNameFromMethodRequest) .addAttributesExtractor( CodeAttributesExtractor.create(MethodRequestCodeAttributesGetter.INSTANCE)) .addAttributesExtractor( @@ -54,7 +62,11 @@ private static Instrumenter createInstrumenterWithAttribu MethodRequest::method, WithSpanParameterAttributeNamesExtractor.INSTANCE, MethodRequest::args)) - .buildInstrumenter(WithSpanSingletons::spanKindFromMethodRequest); + .buildInstrumenter(AnnotationSingletons::spanKindFromMethodRequest); + } + + private static SpanAttributesExtractor createAttributesExtractor() { + return SpanAttributesExtractor.newInstance(WithSpanParameterAttributeNamesExtractor.INSTANCE); } private static SpanKind spanKindFromMethodRequest(MethodRequest request) { @@ -92,5 +104,5 @@ private static String spanNameFromMethod(Method method) { return spanName; } - private WithSpanSingletons() {} + private AnnotationSingletons() {} } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java new file mode 100644 index 000000000000..9262d6712b49 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; + +import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.AnnotationSingletons.attributes; +import static net.bytebuddy.matcher.ElementMatchers.declaresMethod; +import static net.bytebuddy.matcher.ElementMatchers.hasParameters; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.whereAny; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.lang.reflect.Method; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationSource; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.matcher.ElementMatcher; + +public class WithCurrentSpanInstrumentation extends AnnotationInstrumentation { + + private final ElementMatcher.Junction annotatedMethodMatcher; + private final ElementMatcher.Junction annotatedParametersMatcher; + // this matcher matches all methods that should be excluded from transformation + private final ElementMatcher.Junction excludedMethodsMatcher; + + WithCurrentSpanInstrumentation() { + annotatedMethodMatcher = + isAnnotatedWith( + named("application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan")) + // Avoid repeat extraction if method is already annotation with WithSpan + .and( + not( + isAnnotatedWith( + named( + "application.io.opentelemetry.instrumentation.annotations.WithSpan")))); + annotatedParametersMatcher = + hasParameters( + whereAny( + isAnnotatedWith( + named( + "application.io.opentelemetry.instrumentation.annotations.SpanAttribute")))); + excludedMethodsMatcher = configureExcludedMethods(); + } + + @Override + public ElementMatcher typeMatcher() { + return declaresMethod(annotatedMethodMatcher); + } + + @Override + public void transform(TypeTransformer transformer) { + ElementMatcher.Junction tracedMethodsWithParameters = + annotatedMethodMatcher.and(not(excludedMethodsMatcher)).and(annotatedParametersMatcher); + + transformer.applyAdviceToMethod( + tracedMethodsWithParameters, + WithCurrentSpanInstrumentation.class.getName() + "$WithCurrentSpanAttributesAdvice"); + } + + public static class WithCurrentSpanAttributesAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.Origin Method method, + @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args) { + Span otelSpan = Span.current(); + if (otelSpan != null && otelSpan.isRecording() && otelSpan.getSpanContext().isValid()) { + otelSpan.setAllAttributes(attributes().getAttributes(method, args)); + } + } + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java index 02b104beb96e..e49c084127e3 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java @@ -6,15 +6,12 @@ package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; -import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.WithSpanSingletons.instrumenter; -import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.WithSpanSingletons.instrumenterWithAttributes; +import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.AnnotationSingletons.instrumenter; +import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.AnnotationSingletons.instrumenterWithAttributes; import static net.bytebuddy.matcher.ElementMatchers.declaresMethod; import static net.bytebuddy.matcher.ElementMatchers.hasParameters; import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; -import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; -import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.whereAny; @@ -23,26 +20,16 @@ import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; -import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser; import java.lang.reflect.Method; -import java.util.Map; -import java.util.Set; import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.ByteCodeElement; import net.bytebuddy.description.annotation.AnnotationSource; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatchers; -public class WithSpanInstrumentation implements TypeInstrumentation { - - private static final String TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG = - "otel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods"; +public class WithSpanInstrumentation extends AnnotationInstrumentation { private final ElementMatcher.Junction annotatedMethodMatcher; private final ElementMatcher.Junction annotatedParametersMatcher; @@ -92,32 +79,6 @@ public void transform(TypeTransformer transformer) { WithSpanInstrumentation.class.getName() + "$WithSpanAttributesAdvice"); } - /* - Returns a matcher for all methods that should be excluded from auto-instrumentation by - annotation-based advices. - */ - static ElementMatcher.Junction configureExcludedMethods() { - ElementMatcher.Junction result = none(); - - Map> excludedMethods = - MethodsConfigurationParser.parse( - InstrumentationConfig.get().getString(TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG)); - for (Map.Entry> entry : excludedMethods.entrySet()) { - String className = entry.getKey(); - ElementMatcher.Junction matcher = - isDeclaredBy(ElementMatchers.named(className)); - - Set methodNames = entry.getValue(); - if (!methodNames.isEmpty()) { - matcher = matcher.and(namedOneOf(methodNames.toArray(new String[0]))); - } - - result = result.or(matcher); - } - - return result; - } - @SuppressWarnings("unused") public static class WithSpanAdvice { diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java deleted file mode 100644 index 99d485452a6c..000000000000 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.instrumentationannotations; - -import static java.util.Collections.singletonList; - -import application.io.opentelemetry.instrumentation.annotations.WithSpan; -import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import java.util.List; - -/** Instrumentation for methods annotated with {@link WithSpan} annotation. */ -@AutoService(InstrumentationModule.class) -public class WithSpanInstrumentationModule extends InstrumentationModule { - - public WithSpanInstrumentationModule() { - super("opentelemetry-instrumentation-annotations"); - } - - @Override - public List typeInstrumentations() { - return singletonList(new WithSpanInstrumentation()); - } -} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy new file mode 100644 index 000000000000..99907a16a432 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy @@ -0,0 +1,69 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import io.opentelemetry.test.annotation.SpanAttributesWithCurrentSpan + +import static io.opentelemetry.api.trace.SpanKind.INTERNAL + +/** + * This test verifies that auto instrumentation supports the + * {@link io.opentelemetry.instrumentation.annotations.WithCurrentSpan} + * and + * {@link io.opentelemetry.instrumentation.annotations.SpanAttribute} + * annotations. + */ +class WithCurrentSpanInstrumentationTest extends AgentInstrumentationSpecification { + + def "should capture attributes in second span"() { + setup: + runWithSpan("external", + { new SpanAttributesWithCurrentSpan().withSpanAttributes("foo", "bar", null, "baz") }) + + expect: + assertTraces(1) { + trace(0, 2) { + span(0) { + name "external" + kind INTERNAL + hasNoParent() + } + span(1) { + name "SpanAttributesWithCurrentSpan.withSpanAttributes" + kind INTERNAL + childOf span(0) + attributes { + "$SemanticAttributes.CODE_NAMESPACE" SpanAttributesWithCurrentSpan.name + "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" + "implicitName" "foo" + "explicitName" "bar" + } + } + } + } + } + + def "should capture only attributes"() { + setup: + runWithSpan("external", + { new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz") }) + + expect: + assertTraces(1) { + trace(0, 1) { + span(0) { + name "external" + kind INTERNAL + hasNoParent() + attributes { + "implicitName" "foo" + "explicitName" "bar" + } + } + } + } + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java new file mode 100644 index 000000000000..6e32d6908918 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.test.annotation; + +import io.opentelemetry.instrumentation.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.WithCurrentSpan; +import io.opentelemetry.instrumentation.annotations.WithSpan; + +public class SpanAttributesWithCurrentSpan { + + @WithCurrentSpan + public String withCurrentSpanAttributes( + @SpanAttribute String implicitName, + @SpanAttribute("explicitName") String parameter, + @SpanAttribute("nullAttribute") String nullAttribute, + String notTraced) { + + return "hello!"; + } + + @WithSpan + @WithCurrentSpan + public String withSpanAttributes( + @SpanAttribute String implicitName, + @SpanAttribute("explicitName") String parameter, + @SpanAttribute("nullAttribute") String nullAttribute, + String notTraced) { + + return "hello!"; + } +} From 128531549298175025f880daf85f8d393dbf5d9b Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Mon, 13 Feb 2023 22:30:34 +0000 Subject: [PATCH 02/12] PR comments --- .../support/AttributeBindingFactory.java | 40 -- .../annotation/support/AttributeBindings.java | 40 ++ .../MethodSpanAttributesExtractor.java | 5 +- .../support/SpanAttributesExtractor.java | 7 +- .../javaagent/build.gradle.kts | 4 + .../WithSpanSingletons.java | 2 +- .../groovy/WithSpanInstrumentationTest.groovy | 441 -------------- .../WithSpanInstrumentationTest.java | 565 ++++++++++++++++++ .../javaagent/build.gradle.kts | 4 + .../AnnotationSingletons.java | 4 +- .../WithCurrentSpanInstrumentation.java | 7 +- .../WithCurrentSpanInstrumentationTest.groovy | 69 --- .../groovy/WithSpanInstrumentationTest.groovy | 441 -------------- .../WithCurrentSpanInstrumentationTest.java | 93 +++ .../WithSpanInstrumentationTest.java | 562 +++++++++++++++++ .../autoconfigure/aspects/WithSpanAspect.java | 2 +- 16 files changed, 1281 insertions(+), 1005 deletions(-) delete mode 100644 instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy create mode 100644 instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java delete mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy delete mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java create mode 100644 instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java index 91723aea729f..a959394ae5e8 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java @@ -6,8 +6,6 @@ package io.opentelemetry.instrumentation.api.annotation.support; import io.opentelemetry.api.common.AttributeKey; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.AbstractList; import java.util.Arrays; @@ -340,42 +338,4 @@ private static AttributeBinding defaultBinding(String name) { AttributeKey key = AttributeKey.stringKey(name); return (setter, arg) -> setter.put(key, arg.toString()); } - - /** - * Creates a binding of the parameters of the traced method to span attributes. - * - * @param method the traced method - * @return the bindings of the parameters - */ - static AttributeBindings bind( - Method method, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) { - AttributeBindings bindings = EmptyAttributeBindings.INSTANCE; - - Parameter[] parameters = method.getParameters(); - if (parameters.length == 0) { - return bindings; - } - - String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters); - if (attributeNames == null || attributeNames.length != parameters.length) { - return bindings; - } - - for (int i = 0; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - String attributeName = attributeNames[i]; - if (attributeName == null || attributeName.isEmpty()) { - continue; - } - - bindings = - new CombinedAttributeBindings( - bindings, - i, - AttributeBindingFactory.createBinding( - attributeName, parameter.getParameterizedType())); - } - - return bindings; - } } diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindings.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindings.java index aa309dc1a4f1..740555fe2406 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindings.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindings.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.api.annotation.support; import io.opentelemetry.api.common.AttributesBuilder; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; /** Represents the bindings of method parameters to attributes of a traced method. */ interface AttributeBindings { @@ -24,4 +26,42 @@ interface AttributeBindings { * @param args the method arguments */ void apply(AttributesBuilder target, Object[] args); + + /** + * Creates a binding of the parameters of the traced method to span attributes. + * + * @param method the traced method + * @return the bindings of the parameters + */ + static AttributeBindings bind( + Method method, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) { + AttributeBindings bindings = EmptyAttributeBindings.INSTANCE; + + Parameter[] parameters = method.getParameters(); + if (parameters.length == 0) { + return bindings; + } + + String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters); + if (attributeNames == null || attributeNames.length != parameters.length) { + return bindings; + } + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + String attributeName = attributeNames[i]; + if (attributeName == null || attributeName.isEmpty()) { + continue; + } + + bindings = + new CombinedAttributeBindings( + bindings, + i, + AttributeBindingFactory.createBinding( + attributeName, parameter.getParameterizedType())); + } + + return bindings; + } } diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java index c1be336c4204..6f7760b08f51 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java @@ -21,7 +21,7 @@ public final class MethodSpanAttributesExtractor private final Cache cache; private final ParameterAttributeNamesExtractor parameterAttributeNamesExtractor; - public static MethodSpanAttributesExtractor newInstance( + public static MethodSpanAttributesExtractor create( MethodExtractor methodExtractor, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor, MethodArgumentsExtractor methodArgumentsExtractor) { @@ -49,8 +49,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST Method method = methodExtractor.extract(request); AttributeBindings bindings = cache.computeIfAbsent( - method, - (Method m) -> AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor)); + method, (Method m) -> AttributeBindings.bind(m, parameterAttributeNamesExtractor)); if (!bindings.isEmpty()) { Object[] args = methodArgumentsExtractor.extract(request); bindings.apply(attributes, args); diff --git a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java index 975733a3d386..3e16e9c70ca7 100644 --- a/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java +++ b/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/SpanAttributesExtractor.java @@ -16,7 +16,7 @@ public final class SpanAttributesExtractor { private final Cache cache; private final ParameterAttributeNamesExtractor parameterAttributeNamesExtractor; - public static SpanAttributesExtractor newInstance( + public static SpanAttributesExtractor create( ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) { return new SpanAttributesExtractor(parameterAttributeNamesExtractor, new MethodCache<>()); } @@ -28,12 +28,11 @@ public static SpanAttributesExtractor newInstance( this.cache = cache; } - public Attributes getAttributes(Method method, Object[] args) { + public Attributes extract(Method method, Object[] args) { AttributesBuilder attributes = Attributes.builder(); AttributeBindings bindings = cache.computeIfAbsent( - method, - (Method m) -> AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor)); + method, (Method m) -> AttributeBindings.bind(m, parameterAttributeNamesExtractor)); if (!bindings.isEmpty()) { bindings.apply(attributes, args); } diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts index 9abbae283ebb..173c3bfaa5af 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts @@ -23,6 +23,10 @@ dependencies { // see the comment in opentelemetry-api-1.0.gradle for more details compileOnly(project(":opentelemetry-ext-annotations-shaded-for-instrumenting", configuration = "shadow")) + // Used by byte-buddy but not brought in as a transitive dependency. + compileOnly("com.google.code.findbugs:annotations") + testCompileOnly("com.google.code.findbugs:annotations") + testImplementation("io.opentelemetry:opentelemetry-extension-annotations") testImplementation(project(":instrumentation-annotations-support")) testImplementation("net.bytebuddy:byte-buddy") diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java index 836ae98f1344..86479423ce85 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java @@ -50,7 +50,7 @@ private static Instrumenter createInstrumenterWithAttribu .addAttributesExtractor( CodeAttributesExtractor.create(MethodRequestCodeAttributesGetter.INSTANCE)) .addAttributesExtractor( - MethodSpanAttributesExtractor.newInstance( + MethodSpanAttributesExtractor.create( MethodRequest::method, WithSpanParameterAttributeNamesExtractor.INSTANCE, MethodRequest::args)) diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy deleted file mode 100644 index fd5fd3264f0e..000000000000 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import io.opentelemetry.test.annotation.TracedWithSpan -import net.bytebuddy.ByteBuddy -import net.bytebuddy.ClassFileVersion -import net.bytebuddy.asm.MemberAttributeExtension -import net.bytebuddy.description.annotation.AnnotationDescription -import net.bytebuddy.implementation.MethodDelegation -import net.bytebuddy.implementation.bind.annotation.RuntimeType -import net.bytebuddy.implementation.bind.annotation.This -import net.bytebuddy.matcher.ElementMatchers - -import java.lang.reflect.Modifier -import java.util.concurrent.CompletableFuture - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.SpanKind.PRODUCER -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -/** - * This test verifies that auto instrumentation supports the - * {@link io.opentelemetry.extension.annotations.WithSpan} annotation. - */ -@SuppressWarnings("deprecation") // testing instrumentation of deprecated class -class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { - - def "should derive automatic name"() { - setup: - new TracedWithSpan().otel() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.otel" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "otel" - } - } - } - } - } - - def "should take span name from annotation"() { - setup: - new TracedWithSpan().namedOtel() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "manualName" - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "namedOtel" - } - } - } - } - } - - def "should take span kind from annotation"() { - setup: - new TracedWithSpan().someKind() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.someKind" - kind PRODUCER - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "someKind" - } - } - } - } - } - - def "should capture multiple spans"() { - setup: - new TracedWithSpan().server() - - expect: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "TracedWithSpan.server" - kind SERVER - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "server" - } - } - span(1) { - name "TracedWithSpan.otel" - childOf span(0) - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "otel" - } - } - } - } - } - - def "should ignore method excluded by trace.annotated.methods.exclude configuration"() { - setup: - new TracedWithSpan().ignored() - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - } - - def "should capture span for already completed CompletionStage"() { - setup: - def future = CompletableFuture.completedFuture("Done") - new TracedWithSpan().completionStage(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for eventually completed CompletionStage"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completionStage(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.complete("Done") - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for already exceptionally completed CompletionStage"() { - setup: - def future = new CompletableFuture() - future.completeExceptionally(new IllegalArgumentException("Boom")) - new TracedWithSpan().completionStage(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for eventually exceptionally completed CompletionStage"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completionStage(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.completeExceptionally(new IllegalArgumentException("Boom")) - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for null CompletionStage"() { - setup: - new TracedWithSpan().completionStage(null) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for already completed CompletableFuture"() { - setup: - def future = CompletableFuture.completedFuture("Done") - new TracedWithSpan().completableFuture(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for eventually completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completableFuture(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.complete("Done") - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for already exceptionally completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - future.completeExceptionally(new IllegalArgumentException("Boom")) - new TracedWithSpan().completableFuture(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for eventually exceptionally completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completableFuture(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.completeExceptionally(new IllegalArgumentException("Boom")) - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for null CompletableFuture"() { - setup: - new TracedWithSpan().completableFuture(null) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "instrument java6 class"() { - setup: - /* - class GeneratedJava6TestClass implements Runnable { - @WithSpan - public void run() { - runWithSpan("intercept", {}) - } - } - */ - Class generatedClass = new ByteBuddy(ClassFileVersion.JAVA_V6) - .subclass(Object) - .name("GeneratedJava6TestClass") - .implement(Runnable) - .defineMethod("run", void.class, Modifier.PUBLIC).intercept(MethodDelegation.to(new Object() { - @RuntimeType - void intercept(@This Object o) { - runWithSpan("intercept", {}) - } - })) - .visit(new MemberAttributeExtension.ForMethod() - .annotateMethod(AnnotationDescription.Builder.ofType(io.opentelemetry.extension.annotations.WithSpan).build()) - .on(ElementMatchers.named("run"))) - .make() - .load(getClass().getClassLoader()) - .getLoaded() - - Runnable runnable = (Runnable) generatedClass.getConstructor().newInstance() - runnable.run() - - expect: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GeneratedJava6TestClass.run" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" "GeneratedJava6TestClass" - "$SemanticAttributes.CODE_FUNCTION" "run" - } - } - span(1) { - name "intercept" - kind INTERNAL - childOf(span(0)) - attributes { - } - } - } - } - } - - def "should capture attributes"() { - setup: - new TracedWithSpan().withSpanAttributes("foo", "bar", null, "baz") - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.withSpanAttributes" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" - "implicitName" "foo" - "explicitName" "bar" - } - } - } - } - } -} diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java new file mode 100644 index 000000000000..195f801e6204 --- /dev/null +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java @@ -0,0 +1,565 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.test.annotation; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Modifier; +import java.util.concurrent.CompletableFuture; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.asm.MemberAttributeExtension; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.matcher.ElementMatchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class +class WithSpanInstrumentationTest { + + @RegisterExtension + public static final AgentInstrumentationExtension testing = + AgentInstrumentationExtension.create(); + + @Test + void deriveAutomaticName() throws Exception { + + new TracedWithSpan().otel(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.otel") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "otel"))))); + } + + @Test + void manualName() throws Exception { + + new TracedWithSpan().namedOtel(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("manualName") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "namedOtel"))))); + } + + @Test + void manualKind() throws Exception { + + new TracedWithSpan().someKind(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.someKind") + .hasKind(SpanKind.PRODUCER) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "someKind"))))); + } + + @Test + void multipleSpans() throws Exception { + + new TracedWithSpan().server(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.server") + .hasKind(SpanKind.SERVER) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "server"))), + span -> + assertThat(span) + .hasName("TracedWithSpan.otel") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "otel"))))); + } + + @Test + void excludedMethod() throws Exception { + + new TracedWithSpan().ignored(); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + } + + @Test + void completedCompletionStage() throws Exception { + + CompletableFuture future = CompletableFuture.completedFuture("Done"); + new TracedWithSpan().completionStage(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void exceptionallyCompletedCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new IllegalArgumentException("Boom")); + new TracedWithSpan().completionStage(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void nullCompletionStage() throws Exception { + + new TracedWithSpan().completionStage(null); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void completingCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completionStage(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.complete("Done"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void exceptionallyCompletingCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completionStage(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.completeExceptionally(new IllegalArgumentException("Boom")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void completedCompletableFuture() throws Exception { + + CompletableFuture future = CompletableFuture.completedFuture("Done"); + new TracedWithSpan().completableFuture(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void exceptionallyCompletedCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new IllegalArgumentException("Boom")); + new TracedWithSpan().completableFuture(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void nullCompletableFuture() throws Exception { + + new TracedWithSpan().completableFuture(null); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void completingCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completableFuture(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.complete("Done"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void exceptionallyCompletingCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completableFuture(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.completeExceptionally(new IllegalArgumentException("Boom")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void captureAttributes() throws Exception { + + new TracedWithSpan().withSpanAttributes("foo", "bar", null, "baz"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.withSpanAttributes") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "withSpanAttributes"), + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } + + // Needs to be public for ByteBuddy + public static class Intercept { + @RuntimeType + public void intercept(@This Object o) { + testing.runWithSpan("intercept", () -> {}); + } + } + + @Test + void java6Class() throws Exception { + /* + class GeneratedJava6TestClass implements Runnable { + @WithSpan + public void run() { + testing.runWithSpan("intercept", () -> {}); + } + } + */ + Class generatedClass = + new ByteBuddy(ClassFileVersion.JAVA_V6) + .subclass(Object.class) + .name("GeneratedJava6TestClass") + .implement(Runnable.class) + .defineMethod("run", void.class, Modifier.PUBLIC) + .intercept(MethodDelegation.to(new Intercept())) + .visit( + new MemberAttributeExtension.ForMethod() + .annotateMethod( + AnnotationDescription.Builder.ofType( + io.opentelemetry.extension.annotations.WithSpan.class) + .build()) + .on(ElementMatchers.named("run"))) + .make() + .load(getClass().getClassLoader()) + .getLoaded(); + + Runnable runnable = (Runnable) generatedClass.getConstructor().newInstance(); + runnable.run(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("GeneratedJava6TestClass.run") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + "GeneratedJava6TestClass"), + entry(SemanticAttributes.CODE_FUNCTION, "run"))), + span -> + assertThat(span) + .hasName("intercept") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying( + attributes -> assertThat(attributes).isEmpty()))); + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts index d6e18f44926f..6ea1548e4354 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts @@ -25,6 +25,10 @@ dependencies { // see the comment in opentelemetry-api-1.0.gradle for more details compileOnly(project(":opentelemetry-instrumentation-annotations-shaded-for-instrumenting", configuration = "shadow")) + // Used by byte-buddy but not brought in as a transitive dependency. + compileOnly("com.google.code.findbugs:annotations") + testCompileOnly("com.google.code.findbugs:annotations") + testImplementation(project(":instrumentation-annotations")) testImplementation(project(":instrumentation-annotations-support")) testImplementation("net.bytebuddy:byte-buddy") diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java index ade948bf563d..ce2061c4e4bf 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java @@ -58,7 +58,7 @@ private static Instrumenter createInstrumenterWithAttribu .addAttributesExtractor( CodeAttributesExtractor.create(MethodRequestCodeAttributesGetter.INSTANCE)) .addAttributesExtractor( - MethodSpanAttributesExtractor.newInstance( + MethodSpanAttributesExtractor.create( MethodRequest::method, WithSpanParameterAttributeNamesExtractor.INSTANCE, MethodRequest::args)) @@ -66,7 +66,7 @@ private static Instrumenter createInstrumenterWithAttribu } private static SpanAttributesExtractor createAttributesExtractor() { - return SpanAttributesExtractor.newInstance(WithSpanParameterAttributeNamesExtractor.INSTANCE); + return SpanAttributesExtractor.create(WithSpanParameterAttributeNamesExtractor.INSTANCE); } private static SpanKind spanKindFromMethodRequest(MethodRequest request) { diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java index 9262d6712b49..2ef4ee3ee78c 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java @@ -14,6 +14,7 @@ import static net.bytebuddy.matcher.ElementMatchers.whereAny; import io.opentelemetry.api.trace.Span; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.lang.reflect.Method; import net.bytebuddy.asm.Advice; @@ -70,9 +71,9 @@ public static class WithCurrentSpanAttributesAdvice { public static void onEnter( @Advice.Origin Method method, @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args) { - Span otelSpan = Span.current(); - if (otelSpan != null && otelSpan.isRecording() && otelSpan.getSpanContext().isValid()) { - otelSpan.setAllAttributes(attributes().getAttributes(method, args)); + Span otelSpan = Java8BytecodeBridge.currentSpan(); + if (otelSpan.isRecording() && otelSpan.getSpanContext().isValid()) { + otelSpan.setAllAttributes(attributes().extract(method, args)); } } } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy deleted file mode 100644 index 99907a16a432..000000000000 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithCurrentSpanInstrumentationTest.groovy +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import io.opentelemetry.test.annotation.SpanAttributesWithCurrentSpan - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL - -/** - * This test verifies that auto instrumentation supports the - * {@link io.opentelemetry.instrumentation.annotations.WithCurrentSpan} - * and - * {@link io.opentelemetry.instrumentation.annotations.SpanAttribute} - * annotations. - */ -class WithCurrentSpanInstrumentationTest extends AgentInstrumentationSpecification { - - def "should capture attributes in second span"() { - setup: - runWithSpan("external", - { new SpanAttributesWithCurrentSpan().withSpanAttributes("foo", "bar", null, "baz") }) - - expect: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "external" - kind INTERNAL - hasNoParent() - } - span(1) { - name "SpanAttributesWithCurrentSpan.withSpanAttributes" - kind INTERNAL - childOf span(0) - attributes { - "$SemanticAttributes.CODE_NAMESPACE" SpanAttributesWithCurrentSpan.name - "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" - "implicitName" "foo" - "explicitName" "bar" - } - } - } - } - } - - def "should capture only attributes"() { - setup: - runWithSpan("external", - { new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz") }) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "external" - kind INTERNAL - hasNoParent() - attributes { - "implicitName" "foo" - "explicitName" "bar" - } - } - } - } - } -} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy deleted file mode 100644 index 8fb31a938889..000000000000 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.annotations.WithSpan -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import io.opentelemetry.test.annotation.TracedWithSpan -import net.bytebuddy.ByteBuddy -import net.bytebuddy.ClassFileVersion -import net.bytebuddy.asm.MemberAttributeExtension -import net.bytebuddy.description.annotation.AnnotationDescription -import net.bytebuddy.implementation.MethodDelegation -import net.bytebuddy.implementation.bind.annotation.RuntimeType -import net.bytebuddy.implementation.bind.annotation.This -import net.bytebuddy.matcher.ElementMatchers - -import java.lang.reflect.Modifier -import java.util.concurrent.CompletableFuture - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.api.trace.SpanKind.PRODUCER -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -/** - * This test verifies that auto instrumentation supports the - * {@link io.opentelemetry.instrumentation.annotations.WithSpan} annotation. - */ -class WithSpanInstrumentationTest extends AgentInstrumentationSpecification { - - def "should derive automatic name"() { - setup: - new TracedWithSpan().otel() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.otel" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "otel" - } - } - } - } - } - - def "should take span name from annotation"() { - setup: - new TracedWithSpan().namedOtel() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "manualName" - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "namedOtel" - } - } - } - } - } - - def "should take span kind from annotation"() { - setup: - new TracedWithSpan().someKind() - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.someKind" - kind PRODUCER - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "someKind" - } - } - } - } - } - - def "should capture multiple spans"() { - setup: - new TracedWithSpan().server() - - expect: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "TracedWithSpan.server" - kind SERVER - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "server" - } - } - span(1) { - name "TracedWithSpan.otel" - childOf span(0) - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "otel" - } - } - } - } - } - - def "should ignore method excluded by trace.annotated.methods.exclude configuration"() { - setup: - new TracedWithSpan().ignored() - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - } - - def "should capture span for already completed CompletionStage"() { - setup: - def future = CompletableFuture.completedFuture("Done") - new TracedWithSpan().completionStage(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for eventually completed CompletionStage"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completionStage(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.complete("Done") - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for already exceptionally completed CompletionStage"() { - setup: - def future = new CompletableFuture() - future.completeExceptionally(new IllegalArgumentException("Boom")) - new TracedWithSpan().completionStage(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for eventually exceptionally completed CompletionStage"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completionStage(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.completeExceptionally(new IllegalArgumentException("Boom")) - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for null CompletionStage"() { - setup: - new TracedWithSpan().completionStage(null) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completionStage" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completionStage" - } - } - } - } - } - - def "should capture span for already completed CompletableFuture"() { - setup: - def future = CompletableFuture.completedFuture("Done") - new TracedWithSpan().completableFuture(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for eventually completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completableFuture(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.complete("Done") - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for already exceptionally completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - future.completeExceptionally(new IllegalArgumentException("Boom")) - new TracedWithSpan().completableFuture(future) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for eventually exceptionally completed CompletableFuture"() { - setup: - def future = new CompletableFuture() - new TracedWithSpan().completableFuture(future) - - expect: - Thread.sleep(500) // sleep a bit just to make sure no span is captured - assertTraces(0) {} - - future.completeExceptionally(new IllegalArgumentException("Boom")) - - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - status ERROR - errorEvent(IllegalArgumentException, "Boom") - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "should capture span for null CompletableFuture"() { - setup: - new TracedWithSpan().completableFuture(null) - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.completableFuture" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "completableFuture" - } - } - } - } - } - - def "instrument java6 class"() { - setup: - /* - class GeneratedJava6TestClass implements Runnable { - @WithSpan - public void run() { - runWithSpan("intercept", {}) - } - } - */ - Class generatedClass = new ByteBuddy(ClassFileVersion.JAVA_V6) - .subclass(Object) - .name("GeneratedJava6TestClass") - .implement(Runnable) - .defineMethod("run", void.class, Modifier.PUBLIC).intercept(MethodDelegation.to(new Object() { - @RuntimeType - void intercept(@This Object o) { - runWithSpan("intercept", {}) - } - })) - .visit(new MemberAttributeExtension.ForMethod() - .annotateMethod(AnnotationDescription.Builder.ofType(WithSpan).build()) - .on(ElementMatchers.named("run"))) - .make() - .load(getClass().getClassLoader()) - .getLoaded() - - Runnable runnable = (Runnable) generatedClass.getConstructor().newInstance() - runnable.run() - - expect: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GeneratedJava6TestClass.run" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" "GeneratedJava6TestClass" - "$SemanticAttributes.CODE_FUNCTION" "run" - } - } - span(1) { - name "intercept" - kind INTERNAL - childOf(span(0)) - attributes { - } - } - } - } - } - - def "should capture attributes"() { - setup: - new TracedWithSpan().withSpanAttributes("foo", "bar", null, "baz") - - expect: - assertTraces(1) { - trace(0, 1) { - span(0) { - name "TracedWithSpan.withSpanAttributes" - kind INTERNAL - hasNoParent() - attributes { - "$SemanticAttributes.CODE_NAMESPACE" TracedWithSpan.name - "$SemanticAttributes.CODE_FUNCTION" "withSpanAttributes" - "implicitName" "foo" - "explicitName" "bar" - } - } - } - } - } -} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java new file mode 100644 index 000000000000..a4fd5f226e67 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.test.annotation; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class WithCurrentSpanInstrumentationTest { + + @RegisterExtension + public static final AgentInstrumentationExtension testing = + AgentInstrumentationExtension.create(); + + @Test + void captureInWithSpan() throws Exception { + + testing.runWithSpan( + "external", + () -> new SpanAttributesWithCurrentSpan().withSpanAttributes("foo", "bar", null, "baz")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("external") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()), + span -> + assertThat(span) + .hasName("SpanAttributesWithCurrentSpan.withSpanAttributes") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + SpanAttributesWithCurrentSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "withSpanAttributes"), + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } + + @Test + void captureInCurrentSpan() throws Exception { + + testing.runWithSpan( + "external", + () -> + new SpanAttributesWithCurrentSpan() + .withCurrentSpanAttributes("foo", "bar", null, "baz")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("external") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java new file mode 100644 index 000000000000..2ec26e4c5d07 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java @@ -0,0 +1,562 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.test.annotation; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Modifier; +import java.util.concurrent.CompletableFuture; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.asm.MemberAttributeExtension; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.matcher.ElementMatchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class WithSpanInstrumentationTest { + + @RegisterExtension + public static final AgentInstrumentationExtension testing = + AgentInstrumentationExtension.create(); + + @Test + void deriveAutomaticName() throws Exception { + + new TracedWithSpan().otel(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.otel") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "otel"))))); + } + + @Test + void manualName() throws Exception { + + new TracedWithSpan().namedOtel(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("manualName") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "namedOtel"))))); + } + + @Test + void manualKind() throws Exception { + + new TracedWithSpan().someKind(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.someKind") + .hasKind(SpanKind.PRODUCER) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "someKind"))))); + } + + @Test + void multipleSpans() throws Exception { + + new TracedWithSpan().server(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.server") + .hasKind(SpanKind.SERVER) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "server"))), + span -> + assertThat(span) + .hasName("TracedWithSpan.otel") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry(SemanticAttributes.CODE_FUNCTION, "otel"))))); + } + + @Test + void excludedMethod() throws Exception { + + new TracedWithSpan().ignored(); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + } + + @Test + void completedCompletionStage() throws Exception { + + CompletableFuture future = CompletableFuture.completedFuture("Done"); + new TracedWithSpan().completionStage(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void exceptionallyCompletedCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new IllegalArgumentException("Boom")); + new TracedWithSpan().completionStage(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void nullCompletionStage() throws Exception { + + new TracedWithSpan().completionStage(null); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void completingCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completionStage(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.complete("Done"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void exceptionallyCompletingCompletionStage() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completionStage(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.completeExceptionally(new IllegalArgumentException("Boom")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completionStage") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completionStage"))))); + } + + @Test + void completedCompletableFuture() throws Exception { + + CompletableFuture future = CompletableFuture.completedFuture("Done"); + new TracedWithSpan().completableFuture(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void exceptionallyCompletedCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new IllegalArgumentException("Boom")); + new TracedWithSpan().completableFuture(future); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void nullCompletableFuture() throws Exception { + + new TracedWithSpan().completableFuture(null); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void completingCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completableFuture(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.complete("Done"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void exceptionallyCompletingCompletableFuture() throws Exception { + + CompletableFuture future = new CompletableFuture<>(); + new TracedWithSpan().completableFuture(future); + + Thread.sleep(500); // sleep a bit just to make sure no span is captured + assertThat(testing.waitForTraces(0)); + + future.completeExceptionally(new IllegalArgumentException("Boom")); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.completableFuture") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasStatus(StatusData.error()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "completableFuture"))))); + } + + @Test + void captureAttributes() throws Exception { + + new TracedWithSpan().withSpanAttributes("foo", "bar", null, "baz"); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("TracedWithSpan.withSpanAttributes") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + TracedWithSpan.class.getName()), + entry( + SemanticAttributes.CODE_FUNCTION, + "withSpanAttributes"), + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } + + // Needs to be public for ByteBuddy + public static class Intercept { + @RuntimeType + public void intercept(@This Object o) { + testing.runWithSpan("intercept", () -> {}); + } + } + + @Test + void java6Class() throws Exception { + /* + class GeneratedJava6TestClass implements Runnable { + @WithSpan + public void run() { + testing.runWithSpan("intercept", () -> {}); + } + } + */ + Class generatedClass = + new ByteBuddy(ClassFileVersion.JAVA_V6) + .subclass(Object.class) + .name("GeneratedJava6TestClass") + .implement(Runnable.class) + .defineMethod("run", void.class, Modifier.PUBLIC) + .intercept(MethodDelegation.to(new Intercept())) + .visit( + new MemberAttributeExtension.ForMethod() + .annotateMethod(AnnotationDescription.Builder.ofType(WithSpan.class).build()) + .on(ElementMatchers.named("run"))) + .make() + .load(getClass().getClassLoader()) + .getLoaded(); + + Runnable runnable = (Runnable) generatedClass.getConstructor().newInstance(); + runnable.run(); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("GeneratedJava6TestClass.run") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry( + SemanticAttributes.CODE_NAMESPACE, + "GeneratedJava6TestClass"), + entry(SemanticAttributes.CODE_FUNCTION, "run"))), + span -> + assertThat(span) + .hasName("intercept") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying( + attributes -> assertThat(attributes).isEmpty()))); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java index b81acc6be9d1..bd508c212c5f 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java @@ -50,7 +50,7 @@ abstract class WithSpanAspect { .addAttributesExtractor( CodeAttributesExtractor.create(JointPointCodeAttributesExtractor.INSTANCE)) .addAttributesExtractor( - MethodSpanAttributesExtractor.newInstance( + MethodSpanAttributesExtractor.create( JoinPointRequest::method, parameterAttributeNamesExtractor, JoinPointRequest::args)) From 068e3033d51e66b0149ad7e666714cb9694303d2 Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Mon, 13 Feb 2023 23:02:01 +0000 Subject: [PATCH 03/12] More PR comments --- ...otationInstrumentation.java => AnnotationExcludedMethods.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/{AnnotationInstrumentation.java => AnnotationExcludedMethods.java} (100%) diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java similarity index 100% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentation.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java From e9d24dcd8136e8f5988c24b80e1160814d2f432f Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Mon, 13 Feb 2023 23:02:11 +0000 Subject: [PATCH 04/12] More PR comments --- .../AnnotationExcludedMethods.java | 9 ++-- .../WithCurrentSpanInstrumentation.java | 5 +- .../WithSpanInstrumentation.java | 5 +- .../WithCurrentSpanInstrumentationTest.java | 50 +++++++++++++++++-- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java index ebaca6e4e5fe..264d83390246 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationExcludedMethods.java @@ -10,7 +10,6 @@ import static net.bytebuddy.matcher.ElementMatchers.none; import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser; import java.util.Map; import java.util.Set; @@ -19,16 +18,16 @@ import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; -public abstract class AnnotationInstrumentation implements TypeInstrumentation { +final class AnnotationExcludedMethods { - protected static final String TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG = + private static final String TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG = "otel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods"; /* Returns a matcher for all methods that should be excluded from auto-instrumentation by annotation-based advices. */ - protected static ElementMatcher.Junction configureExcludedMethods() { + static ElementMatcher.Junction configureExcludedMethods() { ElementMatcher.Junction result = none(); Map> excludedMethods = @@ -49,4 +48,6 @@ protected static ElementMatcher.Junction configureExcludedMet return result; } + + private AnnotationExcludedMethods() {} } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java index 2ef4ee3ee78c..bae30a5c2d5a 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java @@ -15,6 +15,7 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.lang.reflect.Method; import net.bytebuddy.asm.Advice; @@ -24,7 +25,7 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; -public class WithCurrentSpanInstrumentation extends AnnotationInstrumentation { +public class WithCurrentSpanInstrumentation implements TypeInstrumentation { private final ElementMatcher.Junction annotatedMethodMatcher; private final ElementMatcher.Junction annotatedParametersMatcher; @@ -47,7 +48,7 @@ public class WithCurrentSpanInstrumentation extends AnnotationInstrumentation { isAnnotatedWith( named( "application.io.opentelemetry.instrumentation.annotations.SpanAttribute")))); - excludedMethodsMatcher = configureExcludedMethods(); + excludedMethodsMatcher = AnnotationExcludedMethods.configureExcludedMethods(); } @Override diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java index e49c084127e3..a0d5afde6c7e 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java @@ -20,6 +20,7 @@ import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import java.lang.reflect.Method; import net.bytebuddy.asm.Advice; @@ -29,7 +30,7 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; -public class WithSpanInstrumentation extends AnnotationInstrumentation { +public class WithSpanInstrumentation implements TypeInstrumentation { private final ElementMatcher.Junction annotatedMethodMatcher; private final ElementMatcher.Junction annotatedParametersMatcher; @@ -45,7 +46,7 @@ public class WithSpanInstrumentation extends AnnotationInstrumentation { isAnnotatedWith( named( "application.io.opentelemetry.instrumentation.annotations.SpanAttribute")))); - excludedMethodsMatcher = configureExcludedMethods(); + excludedMethodsMatcher = AnnotationExcludedMethods.configureExcludedMethods(); } @Override diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java index a4fd5f226e67..c39801965cf0 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; @@ -26,7 +27,7 @@ class WithCurrentSpanInstrumentationTest { void captureInWithSpan() throws Exception { testing.runWithSpan( - "external", + "root", () -> new SpanAttributesWithCurrentSpan().withSpanAttributes("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) @@ -36,7 +37,7 @@ void captureInWithSpan() throws Exception { .satisfiesExactly( span -> assertThat(span) - .hasName("external") + .hasName("root") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(SpanId.getInvalid()), span -> @@ -65,7 +66,7 @@ void captureInWithSpan() throws Exception { void captureInCurrentSpan() throws Exception { testing.runWithSpan( - "external", + "root", () -> new SpanAttributesWithCurrentSpan() .withCurrentSpanAttributes("foo", "bar", null, "baz")); @@ -77,7 +78,7 @@ void captureInCurrentSpan() throws Exception { .satisfiesExactly( span -> assertThat(span) - .hasName("external") + .hasName("root") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(SpanId.getInvalid()) .hasAttributesSatisfying( @@ -90,4 +91,45 @@ void captureInCurrentSpan() throws Exception { AttributeKey.stringKey("explicitName"), "bar"))))); } + + @Test + void noExistingSpan() throws Exception { + + new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz"); + + assertThat(testing.waitForTraces(0)); + } + + @Test + void overwriteAttributes() throws Exception { + + testing.runWithSpan( + "root", + () -> { + Span.current().setAttribute("implicitName", "willbegone"); + Span.current().setAttribute("keep", "willbekept"); + new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz"); + }); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("root") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry(AttributeKey.stringKey("keep"), "willbekept"), + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } } From 4392bb5982de062ee499dbbc1c436ec75c4a77cb Mon Sep 17 00:00:00 2001 From: sfriberg Date: Mon, 13 Feb 2023 23:04:18 +0000 Subject: [PATCH 05/12] reduce order value Co-authored-by: Mateusz Rzeszutek --- .../AnnotationInstrumentationModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java index fac9d378a8ab..b62f265eb2cf 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -29,7 +29,7 @@ public AnnotationInstrumentationModule() { public int order() { // Run first to ensure other automatic intstrumentation is added after and therefore is executed // earlier in the instrumented method and create the span to attach attributes to. - return Integer.MIN_VALUE; + return -1000; } @Override From 34a7073548f9c54efe1c9b65f2946696f1e2636e Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Fri, 3 Mar 2023 11:04:35 -0600 Subject: [PATCH 06/12] Rename to WithSpanAttributes --- ...ntelemetry-instrumentation-annotations.txt | 2 +- ...rrentSpan.java => WithSpanAttributes.java} | 10 +++- .../AnnotationInstrumentationModule.java | 6 +- ...=> WithSpanAttributesInstrumentation.java} | 13 ++--- ... ExtractAttributesWithSpanAttributes.java} | 22 +++++-- ...ithSpanAttributesInstrumentationTest.java} | 57 +++++++++++++++---- 6 files changed, 81 insertions(+), 29 deletions(-) rename instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/{WithCurrentSpan.java => WithSpanAttributes.java} (70%) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/{WithCurrentSpanInstrumentation.java => WithSpanAttributesInstrumentation.java} (89%) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/{SpanAttributesWithCurrentSpan.java => ExtractAttributesWithSpanAttributes.java} (58%) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/{WithCurrentSpanInstrumentationTest.java => WithSpanAttributesInstrumentationTest.java} (67%) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt index 4ed40a0d4f12..66240242baa7 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt @@ -1,5 +1,5 @@ Comparing source compatibility of against -+++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.WithCurrentSpan (not serializable) ++++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.WithSpanAttributes (not serializable) +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. +++ NEW INTERFACE: java.lang.annotation.Annotation +++ NEW SUPERCLASS: java.lang.Object diff --git a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithSpanAttributes.java similarity index 70% rename from instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java rename to instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithSpanAttributes.java index dd81b0020f29..298e504987b2 100644 --- a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithCurrentSpan.java +++ b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithSpanAttributes.java @@ -19,9 +19,17 @@ * io.opentelemetry.instrumentation.annotations.SpanAttribute} should be detected and added to the * current span. * + *

If no span is currently active no new span will be created, and no attributes will be + * extracted. + * + *

Similar to {@link + * io.opentelemetry.api.trace.Span#setAttribute(io.opentelemetry.api.common.AttributeKey, + * java.lang.Object) Span.setAttribute() } methods, if the key is already mapped to a value the old + * value is replaced by the extracted value. + * *

If you are a library developer, then probably you should NOT use this annotation, because it * is non-functional without some form of auto-instrumentation. */ @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) -public @interface WithCurrentSpan {} +public @interface WithSpanAttributes {} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java index b62f265eb2cf..037280ef06c3 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -7,7 +7,7 @@ import static java.util.Arrays.asList; -import application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan; +import application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import application.io.opentelemetry.instrumentation.annotations.WithSpan; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; @@ -15,7 +15,7 @@ import java.util.List; /** - * Instrumentation for methods annotated with {@link WithSpan} and {@link WithCurrentSpan} + * Instrumentation for methods annotated with {@link WithSpan} and {@link WithSpanAttributes} * annotations. */ @AutoService(InstrumentationModule.class) @@ -34,6 +34,6 @@ public int order() { @Override public List typeInstrumentations() { - return asList(new WithSpanInstrumentation(), new WithCurrentSpanInstrumentation()); + return asList(new WithSpanInstrumentation(), new WithSpanAttributesInstrumentation()); } } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java similarity index 89% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java index bae30a5c2d5a..9c0839e18116 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithCurrentSpanInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java @@ -25,17 +25,17 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; -public class WithCurrentSpanInstrumentation implements TypeInstrumentation { +public class WithSpanAttributesInstrumentation implements TypeInstrumentation { private final ElementMatcher.Junction annotatedMethodMatcher; private final ElementMatcher.Junction annotatedParametersMatcher; // this matcher matches all methods that should be excluded from transformation private final ElementMatcher.Junction excludedMethodsMatcher; - WithCurrentSpanInstrumentation() { + WithSpanAttributesInstrumentation() { annotatedMethodMatcher = isAnnotatedWith( - named("application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan")) + named("application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes")) // Avoid repeat extraction if method is already annotation with WithSpan .and( not( @@ -61,12 +61,11 @@ public void transform(TypeTransformer transformer) { ElementMatcher.Junction tracedMethodsWithParameters = annotatedMethodMatcher.and(not(excludedMethodsMatcher)).and(annotatedParametersMatcher); - transformer.applyAdviceToMethod( - tracedMethodsWithParameters, - WithCurrentSpanInstrumentation.class.getName() + "$WithCurrentSpanAttributesAdvice"); + transformer.applyAdviceToMethod(tracedMethodsWithParameters, + WithSpanAttributesInstrumentation.class.getName() + "$WithSpanAttributesAdvice"); } - public static class WithCurrentSpanAttributesAdvice { + public static class WithSpanAttributesAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java similarity index 58% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java index 6e32d6908918..fc7678695b13 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/SpanAttributesWithCurrentSpan.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java @@ -6,13 +6,13 @@ package io.opentelemetry.test.annotation; import io.opentelemetry.instrumentation.annotations.SpanAttribute; -import io.opentelemetry.instrumentation.annotations.WithCurrentSpan; +import io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import io.opentelemetry.instrumentation.annotations.WithSpan; -public class SpanAttributesWithCurrentSpan { +public class ExtractAttributesWithSpanAttributes { - @WithCurrentSpan - public String withCurrentSpanAttributes( + @WithSpanAttributes + public String withSpanAttributes( @SpanAttribute String implicitName, @SpanAttribute("explicitName") String parameter, @SpanAttribute("nullAttribute") String nullAttribute, @@ -21,9 +21,19 @@ public String withCurrentSpanAttributes( return "hello!"; } + @WithSpanAttributes + public String withSpanAttributesParent( + @SpanAttribute String implicitName, + @SpanAttribute("explicitName") String parameter, + @SpanAttribute("nullAttribute") String nullAttribute, + String notTraced) { + + return withSpanAttributes("foo", "bar", null, "baz"); + } + @WithSpan - @WithCurrentSpan - public String withSpanAttributes( + @WithSpanAttributes + public String withSpanTakesPrecedence( @SpanAttribute String implicitName, @SpanAttribute("explicitName") String parameter, @SpanAttribute("nullAttribute") String nullAttribute, diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java similarity index 67% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java index c39801965cf0..7a7aaa929f1e 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithCurrentSpanInstrumentationTest.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java @@ -17,18 +17,18 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class WithCurrentSpanInstrumentationTest { +class WithSpanAttributesInstrumentationTest { @RegisterExtension public static final AgentInstrumentationExtension testing = AgentInstrumentationExtension.create(); @Test - void captureInWithSpan() throws Exception { + void captureAttributesInNewSpan() throws Exception { testing.runWithSpan( "root", - () -> new SpanAttributesWithCurrentSpan().withSpanAttributes("foo", "bar", null, "baz")); + () -> new ExtractAttributesWithSpanAttributes().withSpanTakesPrecedence("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) .satisfiesExactly( @@ -42,7 +42,7 @@ void captureInWithSpan() throws Exception { .hasParentSpanId(SpanId.getInvalid()), span -> assertThat(span) - .hasName("SpanAttributesWithCurrentSpan.withSpanAttributes") + .hasName("ExtractAttributesWithSpanAttributes.withSpanTakesPrecedence") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(trace.get(0).getSpanId()) .hasAttributesSatisfying( @@ -51,10 +51,10 @@ void captureInWithSpan() throws Exception { .containsOnly( entry( SemanticAttributes.CODE_NAMESPACE, - SpanAttributesWithCurrentSpan.class.getName()), + ExtractAttributesWithSpanAttributes.class.getName()), entry( SemanticAttributes.CODE_FUNCTION, - "withSpanAttributes"), + "withSpanTakesPrecedence"), entry( AttributeKey.stringKey("implicitName"), "foo"), entry( @@ -63,13 +63,13 @@ void captureInWithSpan() throws Exception { } @Test - void captureInCurrentSpan() throws Exception { + void captureAttributesInCurrentSpan() throws Exception { testing.runWithSpan( "root", () -> - new SpanAttributesWithCurrentSpan() - .withCurrentSpanAttributes("foo", "bar", null, "baz")); + new ExtractAttributesWithSpanAttributes() + .withSpanAttributes("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) .satisfiesExactly( @@ -95,7 +95,7 @@ void captureInCurrentSpan() throws Exception { @Test void noExistingSpan() throws Exception { - new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz"); + new ExtractAttributesWithSpanAttributes().withSpanAttributes("foo", "bar", null, "baz"); assertThat(testing.waitForTraces(0)); } @@ -108,7 +108,42 @@ void overwriteAttributes() throws Exception { () -> { Span.current().setAttribute("implicitName", "willbegone"); Span.current().setAttribute("keep", "willbekept"); - new SpanAttributesWithCurrentSpan().withCurrentSpanAttributes("foo", "bar", null, "baz"); + new ExtractAttributesWithSpanAttributes().withSpanAttributes("foo", "bar", null, "baz"); + }); + + assertThat(testing.waitForTraces(1)) + .satisfiesExactly( + trace -> + assertThat(trace) + .satisfiesExactly( + span -> + assertThat(span) + .hasName("root") + .hasKind(SpanKind.INTERNAL) + .hasParentSpanId(SpanId.getInvalid()) + .hasAttributesSatisfying( + attributes -> + assertThat(attributes) + .containsOnly( + entry(AttributeKey.stringKey("keep"), "willbekept"), + entry( + AttributeKey.stringKey("implicitName"), "foo"), + entry( + AttributeKey.stringKey("explicitName"), + "bar"))))); + } + + @Test + void multiMethodOverwriteAttributes() throws Exception { + + testing.runWithSpan( + "root", + () -> { + Span.current().setAttribute("implicitName", "willbegone"); + Span.current().setAttribute("keep", "willbekept"); + new ExtractAttributesWithSpanAttributes() + .withSpanAttributesParent( + "parentbegone", "parentbegone", null, "parentbegone"); }); assertThat(testing.waitForTraces(1)) From a7a79f9e9bd16c6c5a4885304127b10789d2354f Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Fri, 3 Mar 2023 11:12:28 -0600 Subject: [PATCH 07/12] spotless --- .../AnnotationInstrumentationModule.java | 2 +- .../WithSpanAttributesInstrumentation.java | 6 ++++-- .../ExtractAttributesWithSpanAttributes.java | 2 +- .../WithSpanAttributesInstrumentationTest.java | 13 ++++++++----- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java index 037280ef06c3..3f6bb00dd5e1 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -7,8 +7,8 @@ import static java.util.Arrays.asList; -import application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import application.io.opentelemetry.instrumentation.annotations.WithSpan; +import application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java index 9c0839e18116..754fba40238e 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java @@ -35,7 +35,8 @@ public class WithSpanAttributesInstrumentation implements TypeInstrumentation { WithSpanAttributesInstrumentation() { annotatedMethodMatcher = isAnnotatedWith( - named("application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes")) + named( + "application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes")) // Avoid repeat extraction if method is already annotation with WithSpan .and( not( @@ -61,7 +62,8 @@ public void transform(TypeTransformer transformer) { ElementMatcher.Junction tracedMethodsWithParameters = annotatedMethodMatcher.and(not(excludedMethodsMatcher)).and(annotatedParametersMatcher); - transformer.applyAdviceToMethod(tracedMethodsWithParameters, + transformer.applyAdviceToMethod( + tracedMethodsWithParameters, WithSpanAttributesInstrumentation.class.getName() + "$WithSpanAttributesAdvice"); } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java index fc7678695b13..92823f34c8c6 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java @@ -6,8 +6,8 @@ package io.opentelemetry.test.annotation; import io.opentelemetry.instrumentation.annotations.SpanAttribute; -import io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.instrumentation.annotations.WithSpanAttributes; public class ExtractAttributesWithSpanAttributes { diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java index 7a7aaa929f1e..497a5c03c507 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java @@ -28,7 +28,9 @@ void captureAttributesInNewSpan() throws Exception { testing.runWithSpan( "root", - () -> new ExtractAttributesWithSpanAttributes().withSpanTakesPrecedence("foo", "bar", null, "baz")); + () -> + new ExtractAttributesWithSpanAttributes() + .withSpanTakesPrecedence("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) .satisfiesExactly( @@ -42,7 +44,8 @@ void captureAttributesInNewSpan() throws Exception { .hasParentSpanId(SpanId.getInvalid()), span -> assertThat(span) - .hasName("ExtractAttributesWithSpanAttributes.withSpanTakesPrecedence") + .hasName( + "ExtractAttributesWithSpanAttributes.withSpanTakesPrecedence") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(trace.get(0).getSpanId()) .hasAttributesSatisfying( @@ -51,7 +54,8 @@ void captureAttributesInNewSpan() throws Exception { .containsOnly( entry( SemanticAttributes.CODE_NAMESPACE, - ExtractAttributesWithSpanAttributes.class.getName()), + ExtractAttributesWithSpanAttributes.class + .getName()), entry( SemanticAttributes.CODE_FUNCTION, "withSpanTakesPrecedence"), @@ -142,8 +146,7 @@ void multiMethodOverwriteAttributes() throws Exception { Span.current().setAttribute("implicitName", "willbegone"); Span.current().setAttribute("keep", "willbekept"); new ExtractAttributesWithSpanAttributes() - .withSpanAttributesParent( - "parentbegone", "parentbegone", null, "parentbegone"); + .withSpanAttributesParent("parentbegone", "parentbegone", null, "parentbegone"); }); assertThat(testing.waitForTraces(1)) From 177a5c3568a30c23b4363849d61314230b6dc55f Mon Sep 17 00:00:00 2001 From: sfriberg Date: Tue, 7 Mar 2023 09:00:28 -0600 Subject: [PATCH 08/12] Update instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java Co-authored-by: Mateusz Rzeszutek --- .../test/annotation/WithSpanInstrumentationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java index 195f801e6204..9b57e1c860e1 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java @@ -141,7 +141,7 @@ void multipleSpans() throws Exception { assertThat(span) .hasName("TracedWithSpan.otel") .hasKind(SpanKind.INTERNAL) - .hasParentSpanId(trace.get(0).getSpanId()) + .hasParent(trace.get(0)) .hasAttributesSatisfying( attributes -> assertThat(attributes) From f7bdf786db00eac4c35f07f63ac32fdb46b978ab Mon Sep 17 00:00:00 2001 From: sfriberg Date: Tue, 7 Mar 2023 09:00:53 -0600 Subject: [PATCH 09/12] Update instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java Co-authored-by: Mateusz Rzeszutek --- .../test/annotation/WithSpanInstrumentationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java index 9b57e1c860e1..9736f67c387a 100644 --- a/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java +++ b/instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java @@ -128,7 +128,7 @@ void multipleSpans() throws Exception { assertThat(span) .hasName("TracedWithSpan.server") .hasKind(SpanKind.SERVER) - .hasParentSpanId(SpanId.getInvalid()) + .hasNoParent() .hasAttributesSatisfying( attributes -> assertThat(attributes) From f7baa0358307d58dd2d202b76392091b9c7988a8 Mon Sep 17 00:00:00 2001 From: sfriberg Date: Tue, 7 Mar 2023 09:01:11 -0600 Subject: [PATCH 10/12] Update instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java Co-authored-by: Mateusz Rzeszutek --- .../AnnotationInstrumentationModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java index 3f6bb00dd5e1..a46d54f19541 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -27,7 +27,7 @@ public AnnotationInstrumentationModule() { @Override public int order() { - // Run first to ensure other automatic intstrumentation is added after and therefore is executed + // Run first to ensure other automatic instrumentation is added after and therefore is executed // earlier in the instrumented method and create the span to attach attributes to. return -1000; } From 5d1e3f1e64a8bf1655b8cafbf40a64725a816ced Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Sun, 23 Apr 2023 12:29:11 -0500 Subject: [PATCH 11/12] Rename annotation --- ...ributes.java => AddingSpanAttributes.java} | 0 .../AddingSpanAttributesUsageExamples.java | 47 +++++++++++++++++++ ... AddingSpanAttributesInstrumentation.java} | 0 ...ingSpanAttributesInstrumentationTest.java} | 0 ...tAttributesUsingAddingSpanAttributes.java} | 0 5 files changed, 47 insertions(+) rename instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/{WithSpanAttributes.java => AddingSpanAttributes.java} (100%) create mode 100644 instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/{WithSpanAttributesInstrumentation.java => AddingSpanAttributesInstrumentation.java} (100%) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/{WithSpanAttributesInstrumentationTest.java => AddingSpanAttributesInstrumentationTest.java} (100%) rename instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/{ExtractAttributesWithSpanAttributes.java => ExtractAttributesUsingAddingSpanAttributes.java} (100%) diff --git a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithSpanAttributes.java b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java similarity index 100% rename from instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/WithSpanAttributes.java rename to instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java diff --git a/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java b/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java new file mode 100644 index 000000000000..1e4a92388cf6 --- /dev/null +++ b/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.annotations; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; + +/** + * This class is not a classical test. It's just a demonstration of possible usages of {@link + * WithSpan} annotation together with some explanations. The goal of this class is to serve as an + * early detection system for inconvenient API and unintended API breakage. + */ +@SuppressWarnings("unused") +public class WithSpanUsageExamples { + + /** + * A new {@link Span} will be created for this method's execution. The span's name will be + * automatically generated by OpenTelemetry auto-instrumentation, probably as + * "WithSpanUsageExamples.method1". + */ + @WithSpan + public void method1() {} + + /** Name of the generated span will be "shinyName". */ + @WithSpan("shinyName") + public void method2() {} + + /** + * A {@link Span} with the default name, and a {@link SpanKind} of {@link SpanKind#CONSUMER} will + * be created for this method. + */ + @WithSpan(kind = SpanKind.CONSUMER) + public void consume() {} + + /** + * A {@link Span} with the default name and kind and with default span attributes. + * + * @param attribute1 A span attribute with the default name of {@code attribute1}. + * @param value A span attribute with the name "attribute2". + */ + @WithSpan + public void attributes( + @SpanAttribute String attribute1, @SpanAttribute("attribute2") long value) {} +} diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java similarity index 100% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanAttributesInstrumentation.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java similarity index 100% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/WithSpanAttributesInstrumentationTest.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java similarity index 100% rename from instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesWithSpanAttributes.java rename to instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java From 4b37a22611eabc8ff958d91d81ade806e38a31c7 Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Sun, 23 Apr 2023 12:29:17 -0500 Subject: [PATCH 12/12] Rename annotation --- ...ntelemetry-instrumentation-annotations.txt | 2 +- .../annotations/AddingSpanAttributes.java | 2 +- .../AddingSpanAttributesUsageExamples.java | 31 ++++--------------- .../AddingSpanAttributesInstrumentation.java | 10 +++--- .../AnnotationInstrumentationModule.java | 6 ++-- ...dingSpanAttributesInstrumentationTest.java | 17 +++++----- ...ctAttributesUsingAddingSpanAttributes.java | 10 +++--- 7 files changed, 30 insertions(+), 48 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt index 66240242baa7..df5fcf512827 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-annotations.txt @@ -1,5 +1,5 @@ Comparing source compatibility of against -+++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.WithSpanAttributes (not serializable) ++++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.AddingSpanAttributes (not serializable) +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. +++ NEW INTERFACE: java.lang.annotation.Annotation +++ NEW SUPERCLASS: java.lang.Object diff --git a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java index 298e504987b2..73fccf42df8f 100644 --- a/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java +++ b/instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributes.java @@ -32,4 +32,4 @@ */ @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) -public @interface WithSpanAttributes {} +public @interface AddingSpanAttributes {} diff --git a/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java b/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java index 1e4a92388cf6..48a25dde72e3 100644 --- a/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java +++ b/instrumentation-annotations/src/test/java/io/opentelemetry/instrumentation/annotations/AddingSpanAttributesUsageExamples.java @@ -6,42 +6,23 @@ package io.opentelemetry.instrumentation.annotations; import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanKind; /** * This class is not a classical test. It's just a demonstration of possible usages of {@link - * WithSpan} annotation together with some explanations. The goal of this class is to serve as an - * early detection system for inconvenient API and unintended API breakage. + * AddingSpanAttributes} annotation together with some explanations. The goal of this class is to + * serve as an early detection system for inconvenient API and unintended API breakage. */ @SuppressWarnings("unused") -public class WithSpanUsageExamples { +public class AddingSpanAttributesUsageExamples { /** - * A new {@link Span} will be created for this method's execution. The span's name will be - * automatically generated by OpenTelemetry auto-instrumentation, probably as - * "WithSpanUsageExamples.method1". - */ - @WithSpan - public void method1() {} - - /** Name of the generated span will be "shinyName". */ - @WithSpan("shinyName") - public void method2() {} - - /** - * A {@link Span} with the default name, and a {@link SpanKind} of {@link SpanKind#CONSUMER} will - * be created for this method. - */ - @WithSpan(kind = SpanKind.CONSUMER) - public void consume() {} - - /** - * A {@link Span} with the default name and kind and with default span attributes. + * The current {@link Span} will be updated to contain the annotated method parameters as + * attributes. * * @param attribute1 A span attribute with the default name of {@code attribute1}. * @param value A span attribute with the name "attribute2". */ - @WithSpan + @AddingSpanAttributes public void attributes( @SpanAttribute String attribute1, @SpanAttribute("attribute2") long value) {} } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java index 754fba40238e..e6b47276559a 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AddingSpanAttributesInstrumentation.java @@ -25,18 +25,18 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; -public class WithSpanAttributesInstrumentation implements TypeInstrumentation { +public class AddingSpanAttributesInstrumentation implements TypeInstrumentation { private final ElementMatcher.Junction annotatedMethodMatcher; private final ElementMatcher.Junction annotatedParametersMatcher; // this matcher matches all methods that should be excluded from transformation private final ElementMatcher.Junction excludedMethodsMatcher; - WithSpanAttributesInstrumentation() { + AddingSpanAttributesInstrumentation() { annotatedMethodMatcher = isAnnotatedWith( named( - "application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes")) + "application.io.opentelemetry.instrumentation.annotations.AddingSpanAttributes")) // Avoid repeat extraction if method is already annotation with WithSpan .and( not( @@ -64,10 +64,10 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( tracedMethodsWithParameters, - WithSpanAttributesInstrumentation.class.getName() + "$WithSpanAttributesAdvice"); + AddingSpanAttributesInstrumentation.class.getName() + "$AddingSpanAttributesAdvice"); } - public static class WithSpanAttributesAdvice { + public static class AddingSpanAttributesAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void onEnter( diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java index a46d54f19541..c497897d58d3 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java @@ -7,15 +7,15 @@ import static java.util.Arrays.asList; +import application.io.opentelemetry.instrumentation.annotations.AddingSpanAttributes; import application.io.opentelemetry.instrumentation.annotations.WithSpan; -import application.io.opentelemetry.instrumentation.annotations.WithSpanAttributes; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import java.util.List; /** - * Instrumentation for methods annotated with {@link WithSpan} and {@link WithSpanAttributes} + * Instrumentation for methods annotated with {@link WithSpan} and {@link AddingSpanAttributes} * annotations. */ @AutoService(InstrumentationModule.class) @@ -34,6 +34,6 @@ public int order() { @Override public List typeInstrumentations() { - return asList(new WithSpanInstrumentation(), new WithSpanAttributesInstrumentation()); + return asList(new WithSpanInstrumentation(), new AddingSpanAttributesInstrumentation()); } } diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java index 497a5c03c507..b52356e6bc8e 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/AddingSpanAttributesInstrumentationTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class WithSpanAttributesInstrumentationTest { +class AddingSpanAttributesInstrumentationTest { @RegisterExtension public static final AgentInstrumentationExtension testing = @@ -29,7 +29,7 @@ void captureAttributesInNewSpan() throws Exception { testing.runWithSpan( "root", () -> - new ExtractAttributesWithSpanAttributes() + new ExtractAttributesUsingAddingSpanAttributes() .withSpanTakesPrecedence("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) @@ -45,7 +45,7 @@ void captureAttributesInNewSpan() throws Exception { span -> assertThat(span) .hasName( - "ExtractAttributesWithSpanAttributes.withSpanTakesPrecedence") + "ExtractAttributesUsingAddingSpanAttributes.withSpanTakesPrecedence") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(trace.get(0).getSpanId()) .hasAttributesSatisfying( @@ -54,7 +54,7 @@ void captureAttributesInNewSpan() throws Exception { .containsOnly( entry( SemanticAttributes.CODE_NAMESPACE, - ExtractAttributesWithSpanAttributes.class + ExtractAttributesUsingAddingSpanAttributes.class .getName()), entry( SemanticAttributes.CODE_FUNCTION, @@ -72,7 +72,7 @@ void captureAttributesInCurrentSpan() throws Exception { testing.runWithSpan( "root", () -> - new ExtractAttributesWithSpanAttributes() + new ExtractAttributesUsingAddingSpanAttributes() .withSpanAttributes("foo", "bar", null, "baz")); assertThat(testing.waitForTraces(1)) @@ -99,7 +99,7 @@ void captureAttributesInCurrentSpan() throws Exception { @Test void noExistingSpan() throws Exception { - new ExtractAttributesWithSpanAttributes().withSpanAttributes("foo", "bar", null, "baz"); + new ExtractAttributesUsingAddingSpanAttributes().withSpanAttributes("foo", "bar", null, "baz"); assertThat(testing.waitForTraces(0)); } @@ -112,7 +112,8 @@ void overwriteAttributes() throws Exception { () -> { Span.current().setAttribute("implicitName", "willbegone"); Span.current().setAttribute("keep", "willbekept"); - new ExtractAttributesWithSpanAttributes().withSpanAttributes("foo", "bar", null, "baz"); + new ExtractAttributesUsingAddingSpanAttributes() + .withSpanAttributes("foo", "bar", null, "baz"); }); assertThat(testing.waitForTraces(1)) @@ -145,7 +146,7 @@ void multiMethodOverwriteAttributes() throws Exception { () -> { Span.current().setAttribute("implicitName", "willbegone"); Span.current().setAttribute("keep", "willbekept"); - new ExtractAttributesWithSpanAttributes() + new ExtractAttributesUsingAddingSpanAttributes() .withSpanAttributesParent("parentbegone", "parentbegone", null, "parentbegone"); }); diff --git a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java index 92823f34c8c6..9a8da88cdd36 100644 --- a/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java +++ b/instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/ExtractAttributesUsingAddingSpanAttributes.java @@ -5,13 +5,13 @@ package io.opentelemetry.test.annotation; +import io.opentelemetry.instrumentation.annotations.AddingSpanAttributes; import io.opentelemetry.instrumentation.annotations.SpanAttribute; import io.opentelemetry.instrumentation.annotations.WithSpan; -import io.opentelemetry.instrumentation.annotations.WithSpanAttributes; -public class ExtractAttributesWithSpanAttributes { +public class ExtractAttributesUsingAddingSpanAttributes { - @WithSpanAttributes + @AddingSpanAttributes public String withSpanAttributes( @SpanAttribute String implicitName, @SpanAttribute("explicitName") String parameter, @@ -21,7 +21,7 @@ public String withSpanAttributes( return "hello!"; } - @WithSpanAttributes + @AddingSpanAttributes public String withSpanAttributesParent( @SpanAttribute String implicitName, @SpanAttribute("explicitName") String parameter, @@ -32,7 +32,7 @@ public String withSpanAttributesParent( } @WithSpan - @WithSpanAttributes + @AddingSpanAttributes public String withSpanTakesPrecedence( @SpanAttribute String implicitName, @SpanAttribute("explicitName") String parameter,