Support new annotations (and change of instrumentation name for opentelemetry-annotations) (#6296)
* Support new annotations * Consistency * Simplify * Annotation * oops
This commit is contained in:
parent
8adebaadca
commit
fb784aa877
|
@ -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"))
|
||||
|
|
|
@ -4,6 +4,4 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
api(project(":testing-common"))
|
||||
|
||||
implementation("io.opentelemetry:opentelemetry-extension-annotations")
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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. |
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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. |
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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++) {
|
|
@ -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 +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. |
|
|
@ -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]")
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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 ->
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue