Skip to content

Commit f7cc3e5

Browse files
create modules for counted and timed.
1 parent 5c39fdc commit f7cc3e5

File tree

24 files changed

+783
-182
lines changed

24 files changed

+783
-182
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Settings for the OpenTelemetry Counted Instrumentation Annotations integration
2+
3+
| Environment variable | Type | Default | Description |
4+
|-----------------------------------------------------------------------------------------| ------ | ------- |-------------------------------------------------------------------------------------|
5+
| `otel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods` | String | | All methods to be excluded from auto-instrumentation by Counted annotation advices. |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
// note that muzzle is not run against the current SNAPSHOT instrumentation-annotations, but this is
6+
// ok because the tests are run against the current SNAPSHOT instrumentation-annotations which will
7+
// catch any muzzle issues in SNAPSHOT instrumentation-annotations
8+
9+
muzzle {
10+
pass {
11+
group.set("io.opentelemetry")
12+
module.set("opentelemetry-instrumentation-annotations")
13+
versions.set("(,)")
14+
}
15+
}
16+
17+
dependencies {
18+
compileOnly(project(":instrumentation-annotations-support"))
19+
20+
compileOnly(project(":javaagent-tooling"))
21+
22+
// this instrumentation needs to do similar shading dance as opentelemetry-api-1.0 because
23+
// the @WithSpan annotation references the OpenTelemetry API's SpanKind class
24+
//
25+
// see the comment in opentelemetry-api-1.0.gradle for more details
26+
compileOnly(project(":opentelemetry-instrumentation-annotations-shaded-for-instrumenting", configuration = "shadow"))
27+
28+
// Used by byte-buddy but not brought in as a transitive dependency.
29+
compileOnly("com.google.code.findbugs:annotations")
30+
testCompileOnly("com.google.code.findbugs:annotations")
31+
32+
testImplementation(project(":instrumentation-annotations"))
33+
testImplementation(project(":instrumentation-annotations-support"))
34+
testImplementation("net.bytebuddy:byte-buddy")
35+
}
36+
37+
tasks {
38+
compileTestJava {
39+
options.compilerArgs.add("-parameters")
40+
}
41+
test {
42+
jvmArgs(
43+
"-Dotel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods=io.opentelemetry.test.annotation.CountedExample[exampleIgnore]"
44+
)
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.counted;
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.tooling.config.MethodsConfigurationParser;
14+
import java.util.Map;
15+
import java.util.Set;
16+
import net.bytebuddy.description.ByteCodeElement;
17+
import net.bytebuddy.description.method.MethodDescription;
18+
import net.bytebuddy.matcher.ElementMatcher;
19+
import net.bytebuddy.matcher.ElementMatchers;
20+
21+
public final class AnnotationExcludedMethods {
22+
23+
private static final String COUNTED_METHODS_EXCLUDE_CONFIG =
24+
"otel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods";
25+
26+
/*
27+
Returns a matcher for all methods that should be excluded from auto-instrumentation by
28+
annotation-based advices.
29+
*/
30+
public static ElementMatcher.Junction<MethodDescription> configureExcludedMethods() {
31+
ElementMatcher.Junction<MethodDescription> result = none();
32+
33+
Map<String, Set<String>> excludedMethods =
34+
MethodsConfigurationParser.parse(
35+
InstrumentationConfig.get().getString(COUNTED_METHODS_EXCLUDE_CONFIG));
36+
for (Map.Entry<String, Set<String>> entry : excludedMethods.entrySet()) {
37+
String className = entry.getKey();
38+
ElementMatcher.Junction<ByteCodeElement> matcher =
39+
isDeclaredBy(ElementMatchers.named(className));
40+
41+
Set<String> methodNames = entry.getValue();
42+
if (!methodNames.isEmpty()) {
43+
matcher = matcher.and(namedOneOf(methodNames.toArray(new String[0])));
44+
}
45+
46+
result = result.or(matcher);
47+
}
48+
49+
return result;
50+
}
51+
52+
private AnnotationExcludedMethods() {}
53+
}

instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/CountedInstrumentation.java renamed to instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/counted/CountedInstrumentation.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
6+
package io.opentelemetry.javaagent.instrumentation.counted;
77

8-
import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.KotlinCoroutineUtil.isKotlinSuspendMethod;
8+
import static io.opentelemetry.javaagent.instrumentation.counted.KotlinCoroutineUtil.isKotlinSuspendMethod;
99
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
1010
import static net.bytebuddy.matcher.ElementMatchers.hasParameters;
1111
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
@@ -92,7 +92,7 @@ public static void onExit(
9292
@Advice.Local("otelRequest") MethodRequest request,
9393
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
9494
@Advice.Thrown Throwable throwable) {
95-
AnnotationSingletons.recordCountWithAttributes(request);
95+
CountedSingletons.recordCountWithAttributes(request);
9696
}
9797
}
9898

@@ -112,7 +112,7 @@ public static void stopSpan(
112112
@Advice.Local("otelMethod") Method method,
113113
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
114114
@Advice.Thrown Throwable throwable) {
115-
AnnotationSingletons.recordCount(method);
115+
CountedSingletons.recordCount(method);
116116
}
117117
}
118118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.counted;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static java.util.Arrays.asList;
10+
11+
import com.google.auto.service.AutoService;
12+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
14+
import java.util.List;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
17+
/**
18+
* Instrumentation for methods annotated with {@link Counted} or {@link MetricAttribute}
19+
* annotations.
20+
*/
21+
@AutoService(InstrumentationModule.class)
22+
public class CountedInstrumentationModule extends InstrumentationModule {
23+
24+
public CountedInstrumentationModule() {
25+
super("opentelemetry-instrumentation-annotation-counted", "counted");
26+
}
27+
28+
@Override
29+
public int order() {
30+
// Run first to ensure other automatic instrumentation is added after and therefore is executed
31+
// earlier in the instrumented method and create the span to attach attributes to.
32+
return -1000;
33+
}
34+
35+
@Override
36+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
37+
return hasClassesNamed("application.io.opentelemetry.instrumentation.annotations.Counted");
38+
}
39+
40+
@Override
41+
public List<TypeInstrumentation> typeInstrumentations() {
42+
return asList(new CountedInstrumentation());
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.counted;
7+
8+
import application.io.opentelemetry.instrumentation.annotations.Counted;
9+
import application.io.opentelemetry.instrumentation.annotations.MetricAttribute;
10+
import io.opentelemetry.api.GlobalOpenTelemetry;
11+
import io.opentelemetry.api.common.Attributes;
12+
import io.opentelemetry.api.common.AttributesBuilder;
13+
import io.opentelemetry.api.internal.StringUtils;
14+
import io.opentelemetry.api.metrics.LongCounter;
15+
import io.opentelemetry.api.metrics.Meter;
16+
import java.lang.reflect.Method;
17+
import java.lang.reflect.Parameter;
18+
import java.util.concurrent.ConcurrentHashMap;
19+
import java.util.concurrent.ConcurrentMap;
20+
21+
public final class CountedSingletons {
22+
23+
private static final String INSTRUMENTATION_NAME =
24+
"io.opentelemetry.opentelemetry-instrumentation-annotation-counted";
25+
private static final String COUNTED_DEFAULT_NAME = "method.invocation.count";
26+
private static final ConcurrentMap<String, LongCounter> COUNTERS = new ConcurrentHashMap<>();
27+
private static final Meter METER = GlobalOpenTelemetry.get().getMeter(INSTRUMENTATION_NAME);
28+
29+
private static void extractMetricAttributes(
30+
MethodRequest methodRequest, AttributesBuilder attributesBuilder) {
31+
Parameter[] parameters = methodRequest.method().getParameters();
32+
for (int i = 0; i < parameters.length; i++) {
33+
if (parameters[i].isAnnotationPresent(MetricAttribute.class)) {
34+
MetricAttribute annotation = parameters[i].getAnnotation(MetricAttribute.class);
35+
String attributeKey = "";
36+
if (!StringUtils.isNullOrEmpty(annotation.value())) {
37+
attributeKey = annotation.value();
38+
} else if (!StringUtils.isNullOrEmpty(parameters[i].getName())) {
39+
attributeKey = parameters[i].getName();
40+
} else {
41+
continue;
42+
}
43+
attributesBuilder.put(attributeKey, methodRequest.args()[i].toString());
44+
}
45+
}
46+
}
47+
48+
private static void extractAdditionAttributes(
49+
String[] attributes, AttributesBuilder attributesBuilder) {
50+
int length = attributes.length;
51+
for (int i = 0; i + 1 < length; i += 2) {
52+
attributesBuilder.put(attributes[i], attributes[i + 1]);
53+
}
54+
}
55+
56+
public static void recordCountWithAttributes(MethodRequest methodRequest) {
57+
Counted countedAnnotation = methodRequest.method().getAnnotation(Counted.class);
58+
AttributesBuilder attributesBuilder = Attributes.builder();
59+
extractMetricAttributes(methodRequest, attributesBuilder);
60+
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
61+
getCounter(methodRequest.method()).add(1, attributesBuilder.build());
62+
}
63+
64+
public static void recordCount(Method method) {
65+
Counted countedAnnotation = method.getAnnotation(Counted.class);
66+
AttributesBuilder attributesBuilder = Attributes.builder();
67+
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
68+
getCounter(method).add(1, attributesBuilder.build());
69+
}
70+
71+
private static LongCounter getCounter(Method method) {
72+
Counted countedAnnotation = method.getAnnotation(Counted.class);
73+
String metricName =
74+
(null == countedAnnotation.value() || countedAnnotation.value().isEmpty())
75+
? COUNTED_DEFAULT_NAME
76+
: countedAnnotation.value();
77+
if (!COUNTERS.containsKey(metricName)) {
78+
synchronized (metricName) {
79+
if (!COUNTERS.containsKey(metricName)) {
80+
LongCounter longCounter = null;
81+
if (COUNTED_DEFAULT_NAME.equals(metricName)) {
82+
longCounter = METER.counterBuilder(metricName).build();
83+
} else {
84+
longCounter =
85+
METER
86+
.counterBuilder(metricName)
87+
.setDescription(countedAnnotation.description())
88+
.setUnit(countedAnnotation.unit())
89+
.build();
90+
}
91+
COUNTERS.put(metricName, longCounter);
92+
}
93+
}
94+
}
95+
return COUNTERS.get(metricName);
96+
}
97+
98+
private CountedSingletons() {}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.counted;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.returns;
9+
10+
import net.bytebuddy.description.method.MethodDescription;
11+
import net.bytebuddy.description.method.ParameterList;
12+
import net.bytebuddy.matcher.ElementMatcher;
13+
14+
public final class KotlinCoroutineUtil {
15+
16+
private KotlinCoroutineUtil() {}
17+
18+
public static ElementMatcher<MethodDescription> isKotlinSuspendMethod() {
19+
// kotlin suspend methods return Object and take kotlin.coroutines.Continuation as last argument
20+
return returns(Object.class)
21+
.and(
22+
target -> {
23+
ParameterList<?> parameterList = target.getParameters();
24+
if (!parameterList.isEmpty()) {
25+
String lastParameter =
26+
parameterList.get(parameterList.size() - 1).getType().asErasure().getName();
27+
return "kotlin.coroutines.Continuation".equals(lastParameter);
28+
}
29+
return false;
30+
});
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.counted;
7+
8+
import java.lang.reflect.Method;
9+
10+
public final class MethodRequest {
11+
private final Method method;
12+
private final Object[] args;
13+
14+
public MethodRequest(Method method, Object[] args) {
15+
this.method = method;
16+
this.args = args;
17+
}
18+
19+
public Method method() {
20+
return this.method;
21+
}
22+
23+
public Object[] args() {
24+
return this.args;
25+
}
26+
}

instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/test/java/io/opentelemetry/test/annotation/CountedExample.java renamed to instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/src/test/java/io/opentelemetry/test/annotations/counted/CountedExample.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.test.annotation;
6+
package io.opentelemetry.test.annotations.counted;
77

88
import io.opentelemetry.instrumentation.annotations.Counted;
99

@@ -36,4 +36,7 @@ public void exampleWithAdditionalAttributes1() {}
3636

3737
@Counted(additionalAttributes = {"key1", "value1", "key2", "value2", "key3"})
3838
public void exampleWithAdditionalAttributes2() {}
39+
40+
@Counted
41+
public void exampleIgnore() {}
3942
}

0 commit comments

Comments
 (0)