Skip to content

Commit 1144ffc

Browse files
committed
WithCurrentSpan annotation
1 parent 3d571c3 commit 1144ffc

File tree

15 files changed

+474
-158
lines changed

15 files changed

+474
-158
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
Comparing source compatibility of against
2-
No changes.
2+
+++ NEW ANNOTATION: PUBLIC(+) ABSTRACT(+) io.opentelemetry.instrumentation.annotations.WithCurrentSpan (not serializable)
3+
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
4+
+++ NEW INTERFACE: java.lang.annotation.Annotation
5+
+++ NEW SUPERCLASS: java.lang.Object
6+
+++ NEW ANNOTATION: java.lang.annotation.Target
7+
+++ NEW ELEMENT: value=java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.CONSTRUCTOR (+)
8+
+++ NEW ANNOTATION: java.lang.annotation.Retention
9+
+++ NEW ELEMENT: value=java.lang.annotation.RetentionPolicy.RUNTIME (+)

instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java

+40
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package io.opentelemetry.instrumentation.api.annotation.support;
77

88
import io.opentelemetry.api.common.AttributeKey;
9+
import java.lang.reflect.Method;
10+
import java.lang.reflect.Parameter;
911
import java.lang.reflect.Type;
1012
import java.util.AbstractList;
1113
import java.util.Arrays;
@@ -338,4 +340,42 @@ private static AttributeBinding defaultBinding(String name) {
338340
AttributeKey<String> key = AttributeKey.stringKey(name);
339341
return (setter, arg) -> setter.put(key, arg.toString());
340342
}
343+
344+
/**
345+
* Creates a binding of the parameters of the traced method to span attributes.
346+
*
347+
* @param method the traced method
348+
* @return the bindings of the parameters
349+
*/
350+
static AttributeBindings bind(
351+
Method method, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) {
352+
AttributeBindings bindings = EmptyAttributeBindings.INSTANCE;
353+
354+
Parameter[] parameters = method.getParameters();
355+
if (parameters.length == 0) {
356+
return bindings;
357+
}
358+
359+
String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters);
360+
if (attributeNames == null || attributeNames.length != parameters.length) {
361+
return bindings;
362+
}
363+
364+
for (int i = 0; i < parameters.length; i++) {
365+
Parameter parameter = parameters[i];
366+
String attributeName = attributeNames[i];
367+
if (attributeName == null || attributeName.isEmpty()) {
368+
continue;
369+
}
370+
371+
bindings =
372+
new CombinedAttributeBindings(
373+
bindings,
374+
i,
375+
AttributeBindingFactory.createBinding(
376+
attributeName, parameter.getParameterizedType()));
377+
}
378+
379+
return bindings;
380+
}
341381
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.annotation.support;
7+
8+
import io.opentelemetry.api.common.AttributesBuilder;
9+
10+
/** AttributeBindings implementation that is able to chain multiple AttributeBindings. */
11+
final class CombinedAttributeBindings implements AttributeBindings {
12+
private final AttributeBindings parent;
13+
private final int index;
14+
private final AttributeBinding binding;
15+
16+
public CombinedAttributeBindings(AttributeBindings parent, int index, AttributeBinding binding) {
17+
this.parent = parent;
18+
this.index = index;
19+
this.binding = binding;
20+
}
21+
22+
@Override
23+
public boolean isEmpty() {
24+
return false;
25+
}
26+
27+
@Override
28+
public void apply(AttributesBuilder target, Object[] args) {
29+
parent.apply(target, args);
30+
if (args != null && args.length > index) {
31+
Object arg = args[index];
32+
if (arg != null) {
33+
binding.apply(target, arg);
34+
}
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.annotation.support;
7+
8+
import io.opentelemetry.api.common.AttributesBuilder;
9+
10+
/** Singleton empty implementation of AttributeBindings. */
11+
enum EmptyAttributeBindings implements AttributeBindings {
12+
INSTANCE;
13+
14+
@Override
15+
public boolean isEmpty() {
16+
return true;
17+
}
18+
19+
@Override
20+
public void apply(AttributesBuilder target, Object[] args) {}
21+
}

instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/MethodSpanAttributesExtractor.java

+4-80
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
1111
import io.opentelemetry.instrumentation.api.internal.cache.Cache;
1212
import java.lang.reflect.Method;
13-
import java.lang.reflect.Parameter;
1413
import javax.annotation.Nullable;
1514

1615
/** Extractor of {@link io.opentelemetry.api.common.Attributes} for a traced method. */
@@ -48,7 +47,10 @@ public static <REQUEST, RESPONSE> MethodSpanAttributesExtractor<REQUEST, RESPONS
4847
@Override
4948
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
5049
Method method = methodExtractor.extract(request);
51-
AttributeBindings bindings = cache.computeIfAbsent(method, this::bind);
50+
AttributeBindings bindings =
51+
cache.computeIfAbsent(
52+
method,
53+
(Method m) -> AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor));
5254
if (!bindings.isEmpty()) {
5355
Object[] args = methodArgumentsExtractor.extract(request);
5456
bindings.apply(attributes, args);
@@ -62,82 +64,4 @@ public void onEnd(
6264
REQUEST request,
6365
@Nullable RESPONSE response,
6466
@Nullable Throwable error) {}
65-
66-
/**
67-
* Creates a binding of the parameters of the traced method to span attributes.
68-
*
69-
* @param method the traced method
70-
* @return the bindings of the parameters
71-
*/
72-
private AttributeBindings bind(Method method) {
73-
AttributeBindings bindings = EmptyAttributeBindings.INSTANCE;
74-
75-
Parameter[] parameters = method.getParameters();
76-
if (parameters.length == 0) {
77-
return bindings;
78-
}
79-
80-
String[] attributeNames = parameterAttributeNamesExtractor.extract(method, parameters);
81-
if (attributeNames.length != parameters.length) {
82-
return bindings;
83-
}
84-
85-
for (int i = 0; i < parameters.length; i++) {
86-
Parameter parameter = parameters[i];
87-
String attributeName = attributeNames[i];
88-
if (attributeName == null || attributeName.isEmpty()) {
89-
continue;
90-
}
91-
92-
bindings =
93-
new CombinedAttributeBindings(
94-
bindings,
95-
i,
96-
AttributeBindingFactory.createBinding(
97-
attributeName, parameter.getParameterizedType()));
98-
}
99-
100-
return bindings;
101-
}
102-
103-
protected enum EmptyAttributeBindings implements AttributeBindings {
104-
INSTANCE;
105-
106-
@Override
107-
public boolean isEmpty() {
108-
return true;
109-
}
110-
111-
@Override
112-
public void apply(AttributesBuilder target, Object[] args) {}
113-
}
114-
115-
private static final class CombinedAttributeBindings implements AttributeBindings {
116-
private final AttributeBindings parent;
117-
private final int index;
118-
private final AttributeBinding binding;
119-
120-
public CombinedAttributeBindings(
121-
AttributeBindings parent, int index, AttributeBinding binding) {
122-
this.parent = parent;
123-
this.index = index;
124-
this.binding = binding;
125-
}
126-
127-
@Override
128-
public boolean isEmpty() {
129-
return false;
130-
}
131-
132-
@Override
133-
public void apply(AttributesBuilder target, Object[] args) {
134-
parent.apply(target, args);
135-
if (args != null && args.length > index) {
136-
Object arg = args[index];
137-
if (arg != null) {
138-
binding.apply(target, arg);
139-
}
140-
}
141-
}
142-
}
14367
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.annotation.support;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.common.AttributesBuilder;
10+
import io.opentelemetry.instrumentation.api.internal.cache.Cache;
11+
import java.lang.reflect.Method;
12+
13+
/** Extractor of {@link io.opentelemetry.api.common.Attributes} for a traced method. */
14+
public final class SpanAttributesExtractor {
15+
16+
private final Cache<Method, AttributeBindings> cache;
17+
private final ParameterAttributeNamesExtractor parameterAttributeNamesExtractor;
18+
19+
public static SpanAttributesExtractor newInstance(
20+
ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) {
21+
return new SpanAttributesExtractor(parameterAttributeNamesExtractor, new MethodCache<>());
22+
}
23+
24+
SpanAttributesExtractor(
25+
ParameterAttributeNamesExtractor parameterAttributeNamesExtractor,
26+
Cache<Method, AttributeBindings> cache) {
27+
this.parameterAttributeNamesExtractor = parameterAttributeNamesExtractor;
28+
this.cache = cache;
29+
}
30+
31+
public Attributes getAttributes(Method method, Object[] args) {
32+
AttributesBuilder attributes = Attributes.builder();
33+
AttributeBindings bindings =
34+
cache.computeIfAbsent(
35+
method,
36+
(Method m) -> AttributeBindingFactory.bind(m, parameterAttributeNamesExtractor));
37+
if (!bindings.isEmpty()) {
38+
bindings.apply(attributes, args);
39+
}
40+
return attributes.build();
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.annotations;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
import java.lang.annotation.Target;
12+
13+
/**
14+
* This annotation marks that an execution of this method or constructor is able to add attributes
15+
* to the current span {@link io.opentelemetry.api.trace.Span}.
16+
*
17+
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
18+
* that attributes annotated with the {@link
19+
* io.opentelemetry.instrumentation.annotations.SpanAttribute} should be detected and added to the
20+
* current span.
21+
*
22+
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
23+
* is non-functional without some form of auto-instrumentation.
24+
*/
25+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
26+
@Retention(RetentionPolicy.RUNTIME)
27+
public @interface WithCurrentSpan {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
9+
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
10+
import static net.bytebuddy.matcher.ElementMatchers.none;
11+
12+
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
14+
import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser;
15+
import java.util.Map;
16+
import java.util.Set;
17+
import net.bytebuddy.description.ByteCodeElement;
18+
import net.bytebuddy.description.method.MethodDescription;
19+
import net.bytebuddy.matcher.ElementMatcher;
20+
import net.bytebuddy.matcher.ElementMatchers;
21+
22+
public abstract class AnnotationInstrumentation implements TypeInstrumentation {
23+
24+
protected static final String TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG =
25+
"otel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods";
26+
27+
/*
28+
Returns a matcher for all methods that should be excluded from auto-instrumentation by
29+
annotation-based advices.
30+
*/
31+
protected static ElementMatcher.Junction<MethodDescription> configureExcludedMethods() {
32+
ElementMatcher.Junction<MethodDescription> result = none();
33+
34+
Map<String, Set<String>> excludedMethods =
35+
MethodsConfigurationParser.parse(
36+
InstrumentationConfig.get().getString(TRACE_ANNOTATED_METHODS_EXCLUDE_CONFIG));
37+
for (Map.Entry<String, Set<String>> entry : excludedMethods.entrySet()) {
38+
String className = entry.getKey();
39+
ElementMatcher.Junction<ByteCodeElement> matcher =
40+
isDeclaredBy(ElementMatchers.named(className));
41+
42+
Set<String> methodNames = entry.getValue();
43+
if (!methodNames.isEmpty()) {
44+
matcher = matcher.and(namedOneOf(methodNames.toArray(new String[0])));
45+
}
46+
47+
result = result.or(matcher);
48+
}
49+
50+
return result;
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
7+
8+
import static java.util.Arrays.asList;
9+
10+
import application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan;
11+
import application.io.opentelemetry.instrumentation.annotations.WithSpan;
12+
import com.google.auto.service.AutoService;
13+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
15+
import java.util.List;
16+
17+
/**
18+
* Instrumentation for methods annotated with {@link WithSpan} and {@link WithCurrentSpan}
19+
* annotations.
20+
*/
21+
@AutoService(InstrumentationModule.class)
22+
public class AnnotationInstrumentationModule extends InstrumentationModule {
23+
24+
public AnnotationInstrumentationModule() {
25+
super("opentelemetry-instrumentation-annotations");
26+
}
27+
28+
@Override
29+
public int order() {
30+
// Run first to ensure other automatic intstrumentation is added after and therefore is executed
31+
// earlier in the instrumented method and create the span to attach attributes to.
32+
return Integer.MIN_VALUE;
33+
}
34+
35+
@Override
36+
public List<TypeInstrumentation> typeInstrumentations() {
37+
return asList(new WithSpanInstrumentation(), new WithCurrentSpanInstrumentation());
38+
}
39+
}

0 commit comments

Comments
 (0)