Browse Source

Support new annotations (and change of instrumentation name for opentelemetry-annotations) (#6296)

* Support new annotations

* Consistency

* Simplify

* Annotation

* oops
Trask Stalnaker 2 years ago
parent
commit
fb784aa877
40 changed files with 1166 additions and 139 deletions
  1. 1 0
      conventions/src/main/kotlin/otel.java-conventions.gradle.kts
  2. 0 2
      instrumentation-annotations-support-testing/build.gradle.kts
  3. 0 0
      instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractTraced.java
  4. 0 0
      instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java
  5. 1 1
      instrumentation/guava-10.0/javaagent/build.gradle.kts
  6. 1 30
      instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/BaseGuavaWithSpanTest.java
  7. 42 0
      instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java
  8. 0 5
      instrumentation/opentelemetry-annotations-1.0/README.md
  9. 1 1
      instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts
  10. 5 0
      instrumentation/opentelemetry-extension-annotations-1.0/README.md
  11. 0 0
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts
  12. 1 1
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodRequest.java
  13. 200 0
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentation.java
  14. 2 2
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java
  15. 1 2
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java
  16. 1 1
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java
  17. 0 0
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy
  18. 0 0
      instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java
  19. 5 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/README.md
  20. 42 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts
  21. 26 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodRequest.java
  22. 12 10
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java
  23. 28 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java
  24. 69 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java
  25. 89 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java
  26. 405 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy
  27. 60 0
      instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java
  28. 1 1
      instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts
  29. 4 30
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java
  30. 4 31
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java
  31. 7 3
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java
  32. 45 0
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java
  33. 46 0
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java
  34. 31 0
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java
  35. 4 14
      instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/TracedWithSpan.java
  36. 1 1
      instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts
  37. 2 1
      javaagent/build.gradle.kts
  38. 2 1
      opentelemetry-ext-annotations-shaded-for-instrumenting/build.gradle.kts
  39. 23 0
      opentelemetry-instrumentation-annotations-shaded-for-instrumenting/build.gradle.kts
  40. 4 2
      settings.gradle.kts

+ 1 - 0
conventions/src/main/kotlin/otel.java-conventions.gradle.kts

@@ -376,6 +376,7 @@ configurations.configureEach {
     dependencySubstitution {
       substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")).using(project(":instrumentation-api"))
       substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv")).using(project(":instrumentation-api-semconv"))
+      substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")).using(project(":instrumentation-annotations"))
       substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support")).using(project(":instrumentation-annotations-support"))
       substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-appender-api-internal")).using(project(":instrumentation-appender-api-internal"))
       substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap")).using(project(":javaagent-bootstrap"))

+ 0 - 2
instrumentation/opentelemetry-annotations-1.0/testing/build.gradle.kts → instrumentation-annotations-support-testing/build.gradle.kts

@@ -4,6 +4,4 @@ plugins {
 
 dependencies {
   api(project(":testing-common"))
-
-  implementation("io.opentelemetry:opentelemetry-extension-annotations")
 }

+ 0 - 0
instrumentation/opentelemetry-annotations-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractTraced.java → instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractTraced.java


+ 0 - 0
instrumentation/opentelemetry-annotations-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java → instrumentation-annotations-support-testing/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/AbstractWithSpanTest.java


+ 1 - 1
instrumentation/guava-10.0/javaagent/build.gradle.kts

@@ -24,6 +24,6 @@ dependencies {
 
   implementation(project(":instrumentation:guava-10.0:library"))
 
-  testImplementation(project(":instrumentation:opentelemetry-annotations-1.0:testing"))
+  testImplementation(project(":instrumentation-annotations-support-testing"))
   testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
 }

+ 1 - 30
instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/GuavaWithSpanTest.java → instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/BaseGuavaWithSpanTest.java

@@ -8,19 +8,12 @@ package io.opentelemetry.javaagent.instrumentation.guava;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
-import io.opentelemetry.extension.annotations.WithSpan;
-import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
 import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractWithSpanTest;
 import org.testcontainers.shaded.com.google.common.base.Throwables;
 
-class GuavaWithSpanTest
+abstract class BaseGuavaWithSpanTest
     extends AbstractWithSpanTest<SettableFuture<String>, ListenableFuture<String>> {
 
-  @Override
-  protected AbstractTraced<SettableFuture<String>, ListenableFuture<String>> newTraced() {
-    return new Traced();
-  }
-
   @Override
   protected void complete(SettableFuture<String> future, String value) {
     future.set(value);
@@ -50,26 +43,4 @@ class GuavaWithSpanTest
   protected String canceledKey() {
     return "guava.canceled";
   }
-
-  static final class Traced
-      extends AbstractTraced<SettableFuture<String>, ListenableFuture<String>> {
-
-    @Override
-    @WithSpan
-    protected SettableFuture<String> completable() {
-      return SettableFuture.create();
-    }
-
-    @Override
-    @WithSpan
-    protected ListenableFuture<String> alreadySucceeded() {
-      return Futures.immediateFuture("Value");
-    }
-
-    @Override
-    @WithSpan
-    protected ListenableFuture<String> alreadyFailed() {
-      return Futures.immediateFailedFuture(FAILURE);
-    }
-  }
 }

+ 42 - 0
instrumentation/guava-10.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/guava/ExtensionAnnotationsGuavaWithSpanTest.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.guava;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import io.opentelemetry.extension.annotations.WithSpan;
+import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
+
+class ExtensionAnnotationsGuavaWithSpanTest extends BaseGuavaWithSpanTest {
+
+  @Override
+  protected AbstractTraced<SettableFuture<String>, ListenableFuture<String>> newTraced() {
+    return new Traced();
+  }
+
+  static final class Traced
+      extends AbstractTraced<SettableFuture<String>, ListenableFuture<String>> {
+
+    @Override
+    @WithSpan
+    protected SettableFuture<String> completable() {
+      return SettableFuture.create();
+    }
+
+    @Override
+    @WithSpan
+    protected ListenableFuture<String> alreadySucceeded() {
+      return Futures.immediateFuture("Value");
+    }
+
+    @Override
+    @WithSpan
+    protected ListenableFuture<String> alreadyFailed() {
+      return Futures.immediateFailedFuture(FAILURE);
+    }
+  }
+}

+ 0 - 5
instrumentation/opentelemetry-annotations-1.0/README.md

@@ -1,5 +0,0 @@
-# Settings for the OpenTelemetry Annotations integration
-
-| Environment variable 	| Type 	| Default 	| Description 	|
-|-----------------	|------	|---------	|-------------	|
-| `otel.instrumentation.external-annotations.exclude-methods` | String |  | All methods to be excluded from auto-instrumentation by annotation-based advices. |

+ 1 - 1
instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/build.gradle.kts

@@ -41,5 +41,5 @@ dependencies {
 
   // @WithSpan annotation is used to generate spans in ContextBridgeTest
   testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
-  testInstrumentation(project(":instrumentation:opentelemetry-annotations-1.0:javaagent"))
+  testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent"))
 }

+ 5 - 0
instrumentation/opentelemetry-extension-annotations-1.0/README.md

@@ -0,0 +1,5 @@
+# Settings for the OpenTelemetry Extension Annotations integration
+
+| Environment variable 	| Type 	| Default 	| Description 	|
+|-----------------	|------	|---------	|-------------	|
+| `otel.instrumentation.opentelemetry-annotations.exclude-methods` | String |  | All methods to be excluded from auto-instrumentation by annotation-based advices. |

+ 0 - 0
instrumentation/opentelemetry-annotations-1.0/javaagent/build.gradle.kts → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/build.gradle.kts


+ 1 - 1
instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/MethodRequest.java → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/MethodRequest.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.instrumentation.otelannotations;
+package io.opentelemetry.javaagent.instrumentation.extensionannotations;
 
 import java.lang.reflect.Method;
 

+ 200 - 0
instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentation.java

@@ -0,0 +1,200 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.extensionannotations;
+
+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;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+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-annotations.exclude-methods";
+
+  private final ElementMatcher.Junction<AnnotationSource> annotatedMethodMatcher;
+  private final ElementMatcher.Junction<MethodDescription> annotatedParametersMatcher;
+  // this matcher matches all methods that should be excluded from transformation
+  private final ElementMatcher.Junction<MethodDescription> excludedMethodsMatcher;
+
+  WithSpanInstrumentation() {
+    annotatedMethodMatcher =
+        isAnnotatedWith(named("application.io.opentelemetry.extension.annotations.WithSpan"));
+    annotatedParametersMatcher =
+        hasParameters(
+            whereAny(
+                isAnnotatedWith(
+                    named("application.io.opentelemetry.extension.annotations.SpanAttribute"))));
+    excludedMethodsMatcher = configureExcludedMethods();
+  }
+
+  @Override
+  public ElementMatcher<TypeDescription> typeMatcher() {
+    return declaresMethod(annotatedMethodMatcher);
+  }
+
+  @Override
+  public void transform(TypeTransformer transformer) {
+    ElementMatcher.Junction<MethodDescription> tracedMethods =
+        annotatedMethodMatcher.and(not(excludedMethodsMatcher));
+
+    ElementMatcher.Junction<MethodDescription> tracedMethodsWithParameters =
+        tracedMethods.and(annotatedParametersMatcher);
+    ElementMatcher.Junction<MethodDescription> tracedMethodsWithoutParameters =
+        tracedMethods.and(not(annotatedParametersMatcher));
+
+    transformer.applyAdviceToMethod(
+        tracedMethodsWithoutParameters,
+        WithSpanInstrumentation.class.getName() + "$WithSpanAdvice");
+
+    // Only apply advice for tracing parameters as attributes if any of the parameters are annotated
+    // with @SpanAttribute to avoid unnecessarily copying the arguments into an array.
+    transformer.applyAdviceToMethod(
+        tracedMethodsWithParameters,
+        WithSpanInstrumentation.class.getName() + "$WithSpanAttributesAdvice");
+  }
+
+  /*
+  Returns a matcher for all methods that should be excluded from auto-instrumentation by
+  annotation-based advices.
+  */
+  static ElementMatcher.Junction<MethodDescription> configureExcludedMethods() {
+    ElementMatcher.Junction<MethodDescription> result = none();
+
+    Map<String, Set<String>> excludedMethods =
+        MethodsConfigurationParser.parse(
+            InstrumentationConfig.get().getString(TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG));
+    for (Map.Entry<String, Set<String>> entry : excludedMethods.entrySet()) {
+      String className = entry.getKey();
+      ElementMatcher.Junction<ByteCodeElement> matcher =
+          isDeclaredBy(ElementMatchers.named(className));
+
+      Set<String> 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 {
+
+    @Advice.OnMethodEnter(suppress = Throwable.class)
+    public static void onEnter(
+        @Advice.Origin Method originMethod,
+        @Advice.Local("otelMethod") Method method,
+        @Advice.Local("otelContext") Context context,
+        @Advice.Local("otelScope") Scope scope) {
+      // Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
+      // to local variable so that there would be only one call to Class.getMethod.
+      method = originMethod;
+
+      Instrumenter<Method, Object> instrumenter = WithSpanSingletons.instrumenter();
+      Context current = Java8BytecodeBridge.currentContext();
+
+      if (instrumenter.shouldStart(current, method)) {
+        context = instrumenter.start(current, method);
+        scope = context.makeCurrent();
+      }
+    }
+
+    @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+    public static void stopSpan(
+        @Advice.Local("otelMethod") Method method,
+        @Advice.Local("otelContext") Context context,
+        @Advice.Local("otelScope") Scope scope,
+        @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
+        @Advice.Thrown Throwable throwable) {
+      if (scope == null) {
+        return;
+      }
+      scope.close();
+
+      AsyncOperationEndSupport<Method, Object> operationEndSupport =
+          AsyncOperationEndSupport.create(
+              WithSpanSingletons.instrumenter(), Object.class, method.getReturnType());
+      returnValue = operationEndSupport.asyncEnd(context, method, returnValue, throwable);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  public static class WithSpanAttributesAdvice {
+
+    @Advice.OnMethodEnter(suppress = Throwable.class)
+    public static void onEnter(
+        @Advice.Origin Method originMethod,
+        @Advice.Local("otelMethod") Method method,
+        @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
+        @Advice.Local("otelRequest") MethodRequest request,
+        @Advice.Local("otelContext") Context context,
+        @Advice.Local("otelScope") Scope scope) {
+
+      // Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
+      // to local variable so that there would be only one call to Class.getMethod.
+      method = originMethod;
+
+      Instrumenter<MethodRequest, Object> instrumenter =
+          WithSpanSingletons.instrumenterWithAttributes();
+      Context current = Java8BytecodeBridge.currentContext();
+      request = new MethodRequest(method, args);
+
+      if (instrumenter.shouldStart(current, request)) {
+        context = instrumenter.start(current, request);
+        scope = context.makeCurrent();
+      }
+    }
+
+    @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+    public static void stopSpan(
+        @Advice.Local("otelMethod") Method method,
+        @Advice.Local("otelRequest") MethodRequest request,
+        @Advice.Local("otelContext") Context context,
+        @Advice.Local("otelScope") Scope scope,
+        @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
+        @Advice.Thrown Throwable throwable) {
+      if (scope == null) {
+        return;
+      }
+      scope.close();
+      AsyncOperationEndSupport<MethodRequest, Object> operationEndSupport =
+          AsyncOperationEndSupport.create(
+              WithSpanSingletons.instrumenterWithAttributes(),
+              Object.class,
+              method.getReturnType());
+      returnValue = operationEndSupport.asyncEnd(context, request, returnValue, throwable);
+    }
+  }
+}

+ 2 - 2
instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentationModule.java → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanInstrumentationModule.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.instrumentation.otelannotations;
+package io.opentelemetry.javaagent.instrumentation.extensionannotations;
 
 import static java.util.Collections.singletonList;
 
@@ -18,7 +18,7 @@ import java.util.List;
 public class WithSpanInstrumentationModule extends InstrumentationModule {
 
   public WithSpanInstrumentationModule() {
-    super("opentelemetry-annotations");
+    super("opentelemetry-extension-annotations");
   }
 
   @Override

+ 1 - 2
instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanParameterAttributeNamesExtractor.java → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanParameterAttributeNamesExtractor.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.instrumentation.otelannotations;
+package io.opentelemetry.javaagent.instrumentation.extensionannotations;
 
 import io.opentelemetry.instrumentation.api.annotation.support.AnnotationReflectionHelper;
 import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor;
@@ -43,7 +43,6 @@ public enum WithSpanParameterAttributeNamesExtractor implements ParameterAttribu
   }
 
   @Override
-  @Nullable
   public String[] extract(Method method, Parameter[] parameters) {
     String[] attributeNames = new String[parameters.length];
     for (int i = 0; i < parameters.length; i++) {

+ 1 - 1
instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanSingletons.java → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/extensionannotations/WithSpanSingletons.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.instrumentation.otelannotations;
+package io.opentelemetry.javaagent.instrumentation.extensionannotations;
 
 import static java.util.logging.Level.FINE;
 

+ 0 - 0
instrumentation/opentelemetry-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy


+ 0 - 0
instrumentation/opentelemetry-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java → instrumentation/opentelemetry-extension-annotations-1.0/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java


+ 5 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/README.md

@@ -0,0 +1,5 @@
+# Settings for the OpenTelemetry Instrumentation Annotations integration
+
+| Environment variable 	| Type 	| Default 	| Description 	|
+|-----------------	|------	|---------	|-------------	|
+| `otel.instrumentation.opentelemetry-annotations.exclude-methods` | String |  | All methods to be excluded from auto-instrumentation by annotation-based advices. |

+ 42 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/build.gradle.kts

@@ -0,0 +1,42 @@
+plugins {
+  id("otel.javaagent-instrumentation")
+}
+
+// note that muzzle is not run against the current SNAPSHOT instrumentation-annotations, but this is
+// ok because the tests are run against the current SNAPSHOT instrumentation-annotations which will
+// catch any muzzle issues in SNAPSHOT instrumentation-annotations
+
+muzzle {
+  pass {
+    group.set("io.opentelemetry")
+    module.set("opentelemetry-instrumentation-annotations")
+    versions.set("(,)")
+  }
+}
+
+val versions: Map<String, String> by project
+
+dependencies {
+  compileOnly(project(":instrumentation-annotations-support"))
+
+  compileOnly(project(":javaagent-tooling"))
+
+  // this instrumentation needs to do similar shading dance as opentelemetry-api-1.0 because
+  // the @WithSpan annotation references the OpenTelemetry API's SpanKind class
+  //
+  // see the comment in opentelemetry-api-1.0.gradle for more details
+  compileOnly(project(":opentelemetry-instrumentation-annotations-shaded-for-instrumenting", configuration = "shadow"))
+
+  testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
+  testImplementation(project(":instrumentation-annotations-support"))
+  testImplementation("net.bytebuddy:byte-buddy")
+}
+
+tasks {
+  compileTestJava {
+    options.compilerArgs.add("-parameters")
+  }
+  test {
+    jvmArgs("-Dotel.instrumentation.opentelemetry-annotations.exclude-methods=io.opentelemetry.test.annotation.TracedWithSpan[ignored]")
+  }
+}

+ 26 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/MethodRequest.java

@@ -0,0 +1,26 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
+
+import java.lang.reflect.Method;
+
+public final class MethodRequest {
+  private final Method method;
+  private final Object[] args;
+
+  public MethodRequest(Method method, Object[] args) {
+    this.method = method;
+    this.args = args;
+  }
+
+  public Method method() {
+    return this.method;
+  }
+
+  public Object[] args() {
+    return this.args;
+  }
+}

+ 12 - 10
instrumentation/opentelemetry-annotations-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/otelannotations/WithSpanInstrumentation.java → instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentation.java

@@ -3,15 +3,16 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.instrumentation.otelannotations;
+package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
 
-import static io.opentelemetry.javaagent.instrumentation.otelannotations.WithSpanSingletons.instrumenter;
-import static io.opentelemetry.javaagent.instrumentation.otelannotations.WithSpanSingletons.instrumenterWithAttributes;
+import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.WithSpanSingletons.instrumenter;
+import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.WithSpanSingletons.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;
@@ -49,12 +50,13 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
 
   WithSpanInstrumentation() {
     annotatedMethodMatcher =
-        isAnnotatedWith(named("application.io.opentelemetry.extension.annotations.WithSpan"));
+        isAnnotatedWith(named("application.io.opentelemetry.instrumentation.annotations.WithSpan"));
     annotatedParametersMatcher =
         hasParameters(
             whereAny(
                 isAnnotatedWith(
-                    named("application.io.opentelemetry.extension.annotations.SpanAttribute"))));
+                    named(
+                        "application.io.opentelemetry.instrumentation.annotations.SpanAttribute"))));
     excludedMethodsMatcher = configureExcludedMethods();
   }
 
@@ -96,15 +98,15 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
             InstrumentationConfig.get().getString(TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG));
     for (Map.Entry<String, Set<String>> entry : excludedMethods.entrySet()) {
       String className = entry.getKey();
-      ElementMatcher.Junction<ByteCodeElement> classMather =
+      ElementMatcher.Junction<ByteCodeElement> matcher =
           isDeclaredBy(ElementMatchers.named(className));
 
-      ElementMatcher.Junction<MethodDescription> excludedMethodsMatcher = none();
-      for (String methodName : entry.getValue()) {
-        excludedMethodsMatcher = excludedMethodsMatcher.or(ElementMatchers.named(methodName));
+      Set<String> methodNames = entry.getValue();
+      if (!methodNames.isEmpty()) {
+        matcher = matcher.and(namedOneOf(methodNames.toArray(new String[0])));
       }
 
-      result = result.or(classMather.and(excludedMethodsMatcher));
+      result = result.or(matcher);
     }
 
     return result;

+ 28 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanInstrumentationModule.java

@@ -0,0 +1,28 @@
+/*
+ * 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<TypeInstrumentation> typeInstrumentations() {
+    return singletonList(new WithSpanInstrumentation());
+  }
+}

+ 69 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanParameterAttributeNamesExtractor.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
+
+import io.opentelemetry.instrumentation.api.annotation.support.AnnotationReflectionHelper;
+import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor;
+import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+
+public enum WithSpanParameterAttributeNamesExtractor implements ParameterAttributeNamesExtractor {
+  INSTANCE;
+
+  private static final Class<? extends Annotation> spanAttributeAnnotation;
+  private static final Function<Annotation, String> spanAttributeValueFunction;
+
+  static {
+    ClassLoader classLoader = WithSpanParameterAttributeNamesExtractor.class.getClassLoader();
+    spanAttributeAnnotation =
+        AnnotationReflectionHelper.forNameOrNull(
+            classLoader, "io.opentelemetry.instrumentation.annotations.SpanAttribute");
+    if (spanAttributeAnnotation != null) {
+      spanAttributeValueFunction = resolveSpanAttributeValue(spanAttributeAnnotation);
+    } else {
+      spanAttributeValueFunction = null;
+    }
+  }
+
+  private static Function<Annotation, String> resolveSpanAttributeValue(
+      Class<? extends Annotation> spanAttributeAnnotation) {
+    try {
+      return AnnotationReflectionHelper.bindAnnotationElementMethod(
+          MethodHandles.lookup(), spanAttributeAnnotation, "value", String.class);
+    } catch (Throwable exception) {
+      return annotation -> "";
+    }
+  }
+
+  @Override
+  public String[] extract(Method method, Parameter[] parameters) {
+    String[] attributeNames = new String[parameters.length];
+    for (int i = 0; i < parameters.length; i++) {
+      attributeNames[i] = attributeName(parameters[i]);
+    }
+    return attributeNames;
+  }
+
+  @Nullable
+  private static String attributeName(Parameter parameter) {
+    Annotation annotation = parameter.getDeclaredAnnotation(spanAttributeAnnotation);
+    if (annotation == null) {
+      return null;
+    }
+    String value = spanAttributeValueFunction.apply(annotation);
+    if (!value.isEmpty()) {
+      return value;
+    } else if (parameter.isNamePresent()) {
+      return parameter.getName();
+    } else {
+      return null;
+    }
+  }
+}

+ 89 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/WithSpanSingletons.java

@@ -0,0 +1,89 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
+
+import static java.util.logging.Level.FINE;
+
+import application.io.opentelemetry.instrumentation.annotations.WithSpan;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.util.SpanNames;
+import java.lang.reflect.Method;
+import java.util.logging.Logger;
+
+public final class WithSpanSingletons {
+  private static final String INSTRUMENTATION_NAME =
+      "io.opentelemetry.opentelemetry-annotations-1.0";
+
+  private static final Logger logger = Logger.getLogger(WithSpanSingletons.class.getName());
+  private static final Instrumenter<Method, Object> INSTRUMENTER = createInstrumenter();
+  private static final Instrumenter<MethodRequest, Object> INSTRUMENTER_WITH_ATTRIBUTES =
+      createInstrumenterWithAttributes();
+
+  public static Instrumenter<Method, Object> instrumenter() {
+    return INSTRUMENTER;
+  }
+
+  public static Instrumenter<MethodRequest, Object> instrumenterWithAttributes() {
+    return INSTRUMENTER_WITH_ATTRIBUTES;
+  }
+
+  private static Instrumenter<Method, Object> createInstrumenter() {
+    return Instrumenter.builder(
+            GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, WithSpanSingletons::spanNameFromMethod)
+        .newInstrumenter(WithSpanSingletons::spanKindFromMethod);
+  }
+
+  private static Instrumenter<MethodRequest, Object> createInstrumenterWithAttributes() {
+    return Instrumenter.builder(
+            GlobalOpenTelemetry.get(),
+            INSTRUMENTATION_NAME,
+            WithSpanSingletons::spanNameFromMethodRequest)
+        .addAttributesExtractor(
+            MethodSpanAttributesExtractor.newInstance(
+                MethodRequest::method,
+                WithSpanParameterAttributeNamesExtractor.INSTANCE,
+                MethodRequest::args))
+        .newInstrumenter(WithSpanSingletons::spanKindFromMethodRequest);
+  }
+
+  private static SpanKind spanKindFromMethodRequest(MethodRequest request) {
+    return spanKindFromMethod(request.method());
+  }
+
+  private static SpanKind spanKindFromMethod(Method method) {
+    WithSpan annotation = method.getDeclaredAnnotation(WithSpan.class);
+    if (annotation == null) {
+      return SpanKind.INTERNAL;
+    }
+    return toAgentOrNull(annotation.kind());
+  }
+
+  private static SpanKind toAgentOrNull(
+      application.io.opentelemetry.api.trace.SpanKind applicationSpanKind) {
+    try {
+      return SpanKind.valueOf(applicationSpanKind.name());
+    } catch (IllegalArgumentException e) {
+      logger.log(FINE, "unexpected span kind: {0}", applicationSpanKind.name());
+      return SpanKind.INTERNAL;
+    }
+  }
+
+  private static String spanNameFromMethodRequest(MethodRequest request) {
+    return spanNameFromMethod(request.method());
+  }
+
+  private static String spanNameFromMethod(Method method) {
+    WithSpan annotation = method.getDeclaredAnnotation(WithSpan.class);
+    String spanName = annotation.value();
+    if (spanName.isEmpty()) {
+      spanName = SpanNames.fromMethod(method);
+    }
+    return spanName;
+  }
+}

+ 405 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/groovy/WithSpanInstrumentationTest.groovy

@@ -0,0 +1,405 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import io.opentelemetry.extension.annotations.WithSpan
+import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
+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 {@link io.opentelemetry.extension.annotations.WithSpan} contrib 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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should take span name from annotation"() {
+    setup:
+    new TracedWithSpan().namedOtel()
+
+    expect:
+    assertTraces(1) {
+      trace(0, 1) {
+        span(0) {
+          name "manualName"
+          hasNoParent()
+          attributes {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture multiple spans"() {
+    setup:
+    new TracedWithSpan().server()
+
+    expect:
+    assertTraces(1) {
+      trace(0, 2) {
+        span(0) {
+          name "TracedWithSpan.server"
+          kind SERVER
+          hasNoParent()
+          attributes {
+          }
+        }
+        span(1) {
+          name "TracedWithSpan.otel"
+          childOf span(0)
+          attributes {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for eventually completed CompletionStage"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for already exceptionally completed CompletionStage"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for eventually exceptionally completed CompletionStage"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for eventually completed CompletableFuture"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for already exceptionally completed CompletableFuture"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  def "should capture span for eventually exceptionally completed CompletableFuture"() {
+    setup:
+    def future = new CompletableFuture<String>()
+    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 {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+      }
+    }
+  }
+
+  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 {
+          }
+        }
+        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 {
+            "implicitName" "foo"
+            "explicitName" "bar"
+          }
+        }
+      }
+    }
+  }
+}

+ 60 - 0
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/TracedWithSpan.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.test.annotation;
+
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.extension.annotations.SpanAttribute;
+import io.opentelemetry.extension.annotations.WithSpan;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+public class TracedWithSpan {
+
+  @WithSpan
+  public String otel() {
+    return "hello!";
+  }
+
+  @WithSpan("manualName")
+  public String namedOtel() {
+    return "hello!";
+  }
+
+  @WithSpan
+  public String ignored() {
+    return "hello!";
+  }
+
+  @WithSpan(kind = SpanKind.PRODUCER)
+  public String someKind() {
+    return "hello!";
+  }
+
+  @WithSpan(kind = SpanKind.SERVER)
+  public String server() {
+    return otel();
+  }
+
+  @WithSpan
+  public String withSpanAttributes(
+      @SpanAttribute String implicitName,
+      @SpanAttribute("explicitName") String parameter,
+      @SpanAttribute("nullAttribute") String nullAttribute,
+      String notTraced) {
+
+    return "hello!";
+  }
+
+  @WithSpan
+  public CompletionStage<String> completionStage(CompletableFuture<String> future) {
+    return future;
+  }
+
+  @WithSpan
+  public CompletableFuture<String> completableFuture(CompletableFuture<String> future) {
+    return future;
+  }
+}

+ 1 - 1
instrumentation/reactor/reactor-3.1/javaagent/build.gradle.kts

@@ -29,7 +29,7 @@ dependencies {
   compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "shadow"))
 
   testLibrary("io.projectreactor:reactor-test:3.1.0.RELEASE")
-  testImplementation(project(":instrumentation:opentelemetry-annotations-1.0:testing"))
+  testImplementation(project(":instrumentation-annotations-support-testing"))
   testImplementation(project(":instrumentation:reactor:reactor-3.1:testing"))
   testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
 

+ 4 - 30
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/FluxWithSpanTest.java → instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseFluxWithSpanTest.java

@@ -7,8 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.reactor;
 
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.trace.SpanKind;
-import io.opentelemetry.extension.annotations.WithSpan;
-import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
 import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractWithSpanTest;
 import org.junit.jupiter.api.Test;
 import reactor.core.Scannable;
@@ -16,12 +14,7 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.UnicastProcessor;
 import reactor.test.StepVerifier;
 
-class FluxWithSpanTest extends AbstractWithSpanTest<Flux<String>, Flux<String>> {
-
-  @Override
-  protected AbstractTraced<Flux<String>, Flux<String>> newTraced() {
-    return new Traced();
-  }
+abstract class BaseFluxWithSpanTest extends AbstractWithSpanTest<Flux<String>, Flux<String>> {
 
   @Override
   protected void complete(Flux<String> future, String value) {
@@ -65,7 +58,7 @@ class FluxWithSpanTest extends AbstractWithSpanTest<Flux<String>, Flux<String>>
               return Flux.just("Value");
             });
 
-    Flux<String> result = new TracedWithSpan().flux(flux);
+    Flux<String> result = newTracedWithSpan().flux(flux);
 
     StepVerifier.create(result).expectNext("Value").verifyComplete();
 
@@ -92,7 +85,7 @@ class FluxWithSpanTest extends AbstractWithSpanTest<Flux<String>, Flux<String>>
             "parent",
             () -> {
               Flux<String> result =
-                  new TracedWithSpan()
+                  newTracedWithSpan()
                       .flux(
                           Flux.defer(
                               () -> {
@@ -139,24 +132,5 @@ class FluxWithSpanTest extends AbstractWithSpanTest<Flux<String>, Flux<String>>
         .get();
   }
 
-  static class Traced extends AbstractTraced<Flux<String>, Flux<String>> {
-
-    @Override
-    @WithSpan
-    protected Flux<String> completable() {
-      return UnicastProcessor.create();
-    }
-
-    @Override
-    @WithSpan
-    protected Flux<String> alreadySucceeded() {
-      return Flux.just(SUCCESS_VALUE);
-    }
-
-    @Override
-    @WithSpan
-    protected Flux<String> alreadyFailed() {
-      return Flux.error(FAILURE);
-    }
-  }
+  abstract TracedWithSpan newTracedWithSpan();
 }

+ 4 - 31
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/MonoWithSpanTest.java → instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/BaseMonoWithSpanTest.java

@@ -7,8 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.reactor;
 
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.trace.SpanKind;
-import io.opentelemetry.extension.annotations.WithSpan;
-import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
 import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractWithSpanTest;
 import org.junit.jupiter.api.Test;
 import reactor.core.Scannable;
@@ -16,12 +14,7 @@ import reactor.core.publisher.Mono;
 import reactor.core.publisher.UnicastProcessor;
 import reactor.test.StepVerifier;
 
-class MonoWithSpanTest extends AbstractWithSpanTest<Mono<String>, Mono<String>> {
-
-  @Override
-  protected AbstractTraced<Mono<String>, Mono<String>> newTraced() {
-    return new Traced();
-  }
+abstract class BaseMonoWithSpanTest extends AbstractWithSpanTest<Mono<String>, Mono<String>> {
 
   @Override
   protected void complete(Mono<String> future, String value) {
@@ -65,7 +58,7 @@ class MonoWithSpanTest extends AbstractWithSpanTest<Mono<String>, Mono<String>>
               return Mono.just("Value");
             });
 
-    Mono<String> result = new TracedWithSpan().outer(mono);
+    Mono<String> result = newTracedWithSpan().outer(mono);
 
     StepVerifier.create(result).expectNext("Value").verifyComplete();
 
@@ -97,7 +90,7 @@ class MonoWithSpanTest extends AbstractWithSpanTest<Mono<String>, Mono<String>>
             "parent",
             () -> {
               Mono<String> result =
-                  new TracedWithSpan()
+                  newTracedWithSpan()
                       .mono(
                           Mono.defer(
                               () -> {
@@ -141,25 +134,5 @@ class MonoWithSpanTest extends AbstractWithSpanTest<Mono<String>, Mono<String>>
         .get();
   }
 
-  static class Traced extends AbstractTraced<Mono<String>, Mono<String>> {
-
-    @Override
-    @WithSpan
-    protected Mono<String> completable() {
-      UnicastProcessor<String> source = UnicastProcessor.create();
-      return source.singleOrEmpty();
-    }
-
-    @Override
-    @WithSpan
-    protected Mono<String> alreadySucceeded() {
-      return Mono.just(SUCCESS_VALUE);
-    }
-
-    @Override
-    @WithSpan
-    protected Mono<String> alreadyFailed() {
-      return Mono.error(FAILURE);
-    }
-  }
+  abstract TracedWithSpan newTracedWithSpan();
 }

+ 7 - 3
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ContextPropagationOperatorInstrumentationTest.java

@@ -83,7 +83,9 @@ class ContextPropagationOperatorInstrumentationTest {
             () -> {
               Span span =
                   testing.getOpenTelemetry().getTracer("test").spanBuilder("parent").startSpan();
-              Mono<String> outer = Mono.defer(() -> new TracedWithSpan().mono(Mono.just("Value")));
+              Mono<String> outer =
+                  Mono.defer(
+                      () -> new ExtensionAnnotationsTracedWithSpan().mono(Mono.just("Value")));
               return ContextPropagationOperator.runWithContext(outer, Context.current().with(span))
                   .doFinally(unused -> span.end());
             });
@@ -107,7 +109,9 @@ class ContextPropagationOperatorInstrumentationTest {
             () -> {
               Span span =
                   testing.getOpenTelemetry().getTracer("test").spanBuilder("parent").startSpan();
-              Flux<String> outer = Flux.defer(() -> new TracedWithSpan().flux(Flux.just("Value")));
+              Flux<String> outer =
+                  Flux.defer(
+                      () -> new ExtensionAnnotationsTracedWithSpan().flux(Flux.just("Value")));
               return ContextPropagationOperator.runWithContext(outer, Context.current().with(span))
                   .doFinally(unused -> span.end());
             });
@@ -137,7 +141,7 @@ class ContextPropagationOperatorInstrumentationTest {
                       unused -> {
                         // usual trick to force this to run under new TracingSubscriber with context
                         // written in the next call
-                        return new TracedWithSpan().mono(Mono.just("Value"));
+                        return new ExtensionAnnotationsTracedWithSpan().mono(Mono.just("Value"));
                       })
                   .subscriberContext(
                       ctx ->

+ 45 - 0
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsFluxWithSpanTest.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.reactor;
+
+import io.opentelemetry.extension.annotations.WithSpan;
+import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.UnicastProcessor;
+
+class ExtensionAnnotationsFluxWithSpanTest extends BaseFluxWithSpanTest {
+
+  @Override
+  protected AbstractTraced<Flux<String>, Flux<String>> newTraced() {
+    return new Traced();
+  }
+
+  @Override
+  TracedWithSpan newTracedWithSpan() {
+    return new ExtensionAnnotationsTracedWithSpan();
+  }
+
+  static class Traced extends AbstractTraced<Flux<String>, Flux<String>> {
+
+    @Override
+    @WithSpan
+    protected Flux<String> completable() {
+      return UnicastProcessor.create();
+    }
+
+    @Override
+    @WithSpan
+    protected Flux<String> alreadySucceeded() {
+      return Flux.just(SUCCESS_VALUE);
+    }
+
+    @Override
+    @WithSpan
+    protected Flux<String> alreadyFailed() {
+      return Flux.error(FAILURE);
+    }
+  }
+}

+ 46 - 0
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsMonoWithSpanTest.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.reactor;
+
+import io.opentelemetry.extension.annotations.WithSpan;
+import io.opentelemetry.javaagent.instrumentation.otelannotations.AbstractTraced;
+import reactor.core.publisher.Mono;
+import reactor.core.publisher.UnicastProcessor;
+
+class ExtensionAnnotationsMonoWithSpanTest extends BaseMonoWithSpanTest {
+
+  @Override
+  protected AbstractTraced<Mono<String>, Mono<String>> newTraced() {
+    return new Traced();
+  }
+
+  @Override
+  TracedWithSpan newTracedWithSpan() {
+    return new ExtensionAnnotationsTracedWithSpan();
+  }
+
+  static class Traced extends AbstractTraced<Mono<String>, Mono<String>> {
+
+    @Override
+    @WithSpan
+    protected Mono<String> completable() {
+      UnicastProcessor<String> source = UnicastProcessor.create();
+      return source.singleOrEmpty();
+    }
+
+    @Override
+    @WithSpan
+    protected Mono<String> alreadySucceeded() {
+      return Mono.just(SUCCESS_VALUE);
+    }
+
+    @Override
+    @WithSpan
+    protected Mono<String> alreadyFailed() {
+      return Mono.error(FAILURE);
+    }
+  }
+}

+ 31 - 0
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/ExtensionAnnotationsTracedWithSpan.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.reactor;
+
+import io.opentelemetry.extension.annotations.WithSpan;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class ExtensionAnnotationsTracedWithSpan implements TracedWithSpan {
+
+  @Override
+  @WithSpan("TracedWithSpan.mono")
+  public Mono<String> mono(Mono<String> mono) {
+    return mono;
+  }
+
+  @Override
+  @WithSpan("TracedWithSpan.outer")
+  public Mono<String> outer(Mono<String> inner) {
+    return mono(inner);
+  }
+
+  @Override
+  @WithSpan("TracedWithSpan.flux")
+  public Flux<String> flux(Flux<String> flux) {
+    return flux;
+  }
+}

+ 4 - 14
instrumentation/reactor/reactor-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/reactor/TracedWithSpan.java

@@ -5,23 +5,13 @@
 
 package io.opentelemetry.javaagent.instrumentation.reactor;
 
-import io.opentelemetry.extension.annotations.WithSpan;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
-public class TracedWithSpan {
-  @WithSpan
-  public Mono<String> mono(Mono<String> mono) {
-    return mono;
-  }
+public interface TracedWithSpan {
+  Mono<String> mono(Mono<String> mono);
 
-  @WithSpan
-  public Mono<String> outer(Mono<String> inner) {
-    return mono(inner);
-  }
+  Mono<String> outer(Mono<String> inner);
 
-  @WithSpan
-  public Flux<String> flux(Flux<String> flux) {
-    return flux;
-  }
+  Flux<String> flux(Flux<String> flux);
 }

+ 1 - 1
instrumentation/reactor/reactor-netty/reactor-netty-1.0/javaagent/build.gradle.kts

@@ -34,7 +34,7 @@ dependencies {
 
   testLibrary("io.projectreactor:reactor-test:3.1.0.RELEASE")
   testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
-  testInstrumentation(project(":instrumentation:opentelemetry-annotations-1.0:javaagent"))
+  testInstrumentation(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent"))
 }
 
 tasks {

+ 2 - 1
javaagent/build.gradle.kts

@@ -64,7 +64,8 @@ dependencies {
 
   baseJavaagentLibs(project(":javaagent-tooling"))
   baseJavaagentLibs(project(":muzzle"))
-  baseJavaagentLibs(project(":instrumentation:opentelemetry-annotations-1.0:javaagent"))
+  // TODO (trask) replace with opentelemetry-instrumentation-annotations
+  baseJavaagentLibs(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent"))
   baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent"))
   baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent"))
   baseJavaagentLibs(project(":instrumentation:opentelemetry-instrumentation-api:javaagent"))

+ 2 - 1
opentelemetry-ext-annotations-shaded-for-instrumenting/build.gradle.kts

@@ -17,6 +17,7 @@ dependencies {
 // (see more explanation in opentelemetry-api-1.0.gradle)
 tasks {
   shadowJar {
-    relocate("io.opentelemetry", "application.io.opentelemetry")
+    relocate("io.opentelemetry.extension.annotations", "application.io.opentelemetry.extension.annotations")
+    relocate("io.opentelemetry.api.trace.SpanKind", "application.io.opentelemetry.api.trace.SpanKind")
   }
 }

+ 23 - 0
opentelemetry-instrumentation-annotations-shaded-for-instrumenting/build.gradle.kts

@@ -0,0 +1,23 @@
+plugins {
+  id("com.github.johnrengelman.shadow")
+
+  id("otel.java-conventions")
+}
+
+description = "opentelemetry-instrumentation-annotations shaded for internal javaagent usage"
+group = "io.opentelemetry.javaagent"
+
+dependencies {
+  implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")
+}
+
+// OpenTelemetry Instrumentation Annotations shaded so that it can be used in instrumentation of
+// OpenTelemetry Instrumentation Annotations itself,
+// and then its usage can be unshaded after OpenTelemetry Instrumentation Annotations is shaded
+// (see more explanation in opentelemetry-api-1.0.gradle)
+tasks {
+  shadowJar {
+    relocate("io.opentelemetry.instrumentation.annotations", "application.io.opentelemetry.instrumentation.annotations")
+    relocate("io.opentelemetry.api.trace.SpanKind", "application.io.opentelemetry.api.trace.SpanKind")
+  }
+}

+ 4 - 2
settings.gradle.kts

@@ -98,6 +98,7 @@ include(":muzzle")
 // agent projects
 include(":opentelemetry-api-shaded-for-instrumenting")
 include(":opentelemetry-ext-annotations-shaded-for-instrumenting")
+include(":opentelemetry-instrumentation-annotations-shaded-for-instrumenting")
 include(":opentelemetry-instrumentation-api-shaded-for-instrumenting")
 include(":javaagent-bootstrap")
 include(":javaagent-extension-api")
@@ -112,6 +113,7 @@ include(":instrumentation-appender-api-internal")
 include(":instrumentation-appender-sdk-internal")
 include(":instrumentation-annotations")
 include(":instrumentation-annotations-support")
+include(":instrumentation-annotations-support-testing")
 
 // misc
 include(":dependencyManagement")
@@ -350,11 +352,11 @@ include(":instrumentation:okhttp:okhttp-2.2:javaagent")
 include(":instrumentation:okhttp:okhttp-3.0:javaagent")
 include(":instrumentation:okhttp:okhttp-3.0:library")
 include(":instrumentation:okhttp:okhttp-3.0:testing")
-include(":instrumentation:opentelemetry-annotations-1.0:javaagent")
-include(":instrumentation:opentelemetry-annotations-1.0:testing")
 include(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")
 include(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent")
 include(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent")
+include(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")
+include(":instrumentation:opentelemetry-instrumentation-annotations-1.16:javaagent")
 include(":instrumentation:opentelemetry-instrumentation-api:javaagent")
 include(":instrumentation:opentelemetry-instrumentation-api:testing")
 include(":instrumentation:oracle-ucp-11.2:javaagent")