Skip to content

Commit 6fe72c7

Browse files
add metric annotation instrumentation
1 parent 8b4e6d2 commit 6fe72c7

File tree

6 files changed

+229
-15
lines changed

6 files changed

+229
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ out/
4848

4949
# Others #
5050
##########
51+
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/annotations/
5152
/logs/*
5253
/bin
5354
/out

instrumentation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/Timed.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
*
6060
* <p>Default is millis seconds.
6161
*/
62-
TimeUnit unit() default TimeUnit.MILLISECONDS;
62+
String unit() default "ms";
6363

6464
/**
6565
* List of key-value pairs to supply additional attributes.
@@ -74,7 +74,7 @@
7474
* })
7575
* </pre>
7676
*/
77-
String[] attributes() default {};
77+
String[] additionalAttributes() default {};
7878

7979
/**
8080
* Attribute name for the return value.

instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationSingletons.java

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77

88
import static java.util.logging.Level.FINE;
99

10-
import application.io.opentelemetry.instrumentation.annotations.WithSpan;
1110
import application.io.opentelemetry.instrumentation.annotations.Counted;
1211
import application.io.opentelemetry.instrumentation.annotations.MetricAttribute;
1312
import application.io.opentelemetry.instrumentation.annotations.Timed;
13+
import application.io.opentelemetry.instrumentation.annotations.WithSpan;
14+
//import io.opentelemetry.javaagent.instrumentation.instrumentationannotations.annotations.Counted;
15+
//import io.opentelemetry.javaagent.instrumentation.instrumentationannotations.annotations.MetricAttribute;
16+
//import io.opentelemetry.javaagent.instrumentation.instrumentationannotations.annotations.Timed;
1417
import com.google.common.base.Stopwatch;
1518
import io.opentelemetry.api.GlobalOpenTelemetry;
1619
import io.opentelemetry.api.common.Attributes;
@@ -66,7 +69,7 @@ public static void recordHistogramWithAttributes(
6669
Timed timedAnnotation = methodRequest.method().getAnnotation(Timed.class);
6770
AttributesBuilder attributesBuilder = Attributes.builder();
6871
extractMetricAttributes(methodRequest, attributesBuilder);
69-
extractAdditionAttributes(timedAnnotation, attributesBuilder);
72+
extractAdditionAttributes(timedAnnotation.additionalAttributes(), attributesBuilder);
7073
getHistogram(timedAnnotation)
7174
.record(stopwatch.stop().elapsed().toMillis(), attributesBuilder.build());
7275
}
@@ -90,26 +93,41 @@ private static void extractMetricAttributes(
9093
}
9194
}
9295

96+
public static void recordHistogram(Method method, Stopwatch stopwatch) {
97+
Timed timedAnnotation = method.getAnnotation(Timed.class);
98+
AttributesBuilder attributesBuilder = Attributes.builder();
99+
extractAdditionAttributes(timedAnnotation.additionalAttributes(), attributesBuilder);
100+
getHistogram(timedAnnotation)
101+
.record(stopwatch.stop().elapsed().toMillis(), attributesBuilder.build());
102+
}
103+
93104
private static void extractAdditionAttributes(
94-
Timed timedAnnotation, AttributesBuilder attributesBuilder) {
95-
int length = timedAnnotation.attributes().length;
105+
String[] attributes, AttributesBuilder attributesBuilder) {
106+
int length = attributes.length;
96107
for (int i = 0; i < length / 2; i++) {
97108
attributesBuilder.put(
98-
timedAnnotation.attributes()[i],
99-
i + 1 > length ? "" : timedAnnotation.attributes()[i + 1]);
109+
attributes[i],
110+
i + 1 > length ? "" : attributes[i + 1]);
100111
}
101112
}
102113

103-
public static void recordHistogram(Method method, Stopwatch stopwatch) {
104-
Timed timedAnnotation = method.getAnnotation(Timed.class);
114+
public static void recordCountWithAttributes(MethodRequest methodRequest) {
115+
Counted countedAnnotation = methodRequest.method().getAnnotation(Counted.class);
105116
AttributesBuilder attributesBuilder = Attributes.builder();
106-
extractAdditionAttributes(timedAnnotation, attributesBuilder);
107-
getHistogram(timedAnnotation)
108-
.record(stopwatch.stop().elapsed().toMillis(), attributesBuilder.build());
117+
extractMetricAttributes(methodRequest, attributesBuilder);
118+
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
119+
getCounter(methodRequest.method()).add(1, attributesBuilder.build());
109120
}
110121

111-
private static LongCounter getCounter(MethodRequest methodRequest) {
112-
Counted countedAnnotation = methodRequest.method().getAnnotation(Counted.class);
122+
public static void recordCount(Method method) {
123+
Counted countedAnnotation = method.getAnnotation(Counted.class);
124+
AttributesBuilder attributesBuilder = Attributes.builder();
125+
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
126+
getCounter(method).add(1, attributesBuilder.build());
127+
}
128+
129+
private static LongCounter getCounter(Method method) {
130+
Counted countedAnnotation = method.getAnnotation(Counted.class);
113131
if (!COUNTERS.containsKey(countedAnnotation.value())) {
114132
synchronized (countedAnnotation.value()) {
115133
if (!COUNTERS.containsKey(countedAnnotation.value())) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
2+
3+
import net.bytebuddy.description.annotation.AnnotationSource;
4+
import net.bytebuddy.description.method.MethodDescription;
5+
import net.bytebuddy.matcher.ElementMatcher;
6+
7+
import static io.opentelemetry.javaagent.instrumentation.instrumentationannotations.KotlinCoroutineUtil.isKotlinSuspendMethod;
8+
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
9+
import static net.bytebuddy.matcher.ElementMatchers.hasParameters;
10+
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
11+
import static net.bytebuddy.matcher.ElementMatchers.named;
12+
import static net.bytebuddy.matcher.ElementMatchers.not;
13+
import static net.bytebuddy.matcher.ElementMatchers.whereAny;
14+
15+
import com.google.common.base.Stopwatch;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
17+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
18+
import java.lang.reflect.Method;
19+
import net.bytebuddy.asm.Advice;
20+
import net.bytebuddy.description.type.TypeDescription;
21+
import net.bytebuddy.implementation.bytecode.assign.Assigner;
22+
23+
24+
public class CountedInstrumentation implements TypeInstrumentation {
25+
26+
private final ElementMatcher.Junction<AnnotationSource> annotatedMethodMatcher;
27+
private final ElementMatcher.Junction<MethodDescription> annotatedParametersMatcher;
28+
// this matcher matches all methods that should be excluded from transformation
29+
private final ElementMatcher.Junction<MethodDescription> excludedMethodsMatcher;
30+
31+
CountedInstrumentation() {
32+
annotatedMethodMatcher =
33+
isAnnotatedWith(named("application.io.opentelemetry.instrumentation.annotations.Counted"));
34+
annotatedParametersMatcher =
35+
hasParameters(
36+
whereAny(
37+
isAnnotatedWith(
38+
named(
39+
"application.io.opentelemetry.instrumentation.annotations.MetricAttribute"))));
40+
// exclude all kotlin suspend methods, these are handle in kotlinx-coroutines instrumentation
41+
excludedMethodsMatcher =
42+
AnnotationExcludedMethods.configureExcludedMethods().or(isKotlinSuspendMethod());
43+
}
44+
45+
@Override
46+
public ElementMatcher<TypeDescription> typeMatcher() {
47+
return declaresMethod(annotatedMethodMatcher);
48+
}
49+
50+
@Override
51+
public void transform(TypeTransformer transformer) {
52+
ElementMatcher.Junction<MethodDescription> countedMethods =
53+
annotatedMethodMatcher.and(not(excludedMethodsMatcher));
54+
55+
ElementMatcher.Junction<MethodDescription> timedMethodsWithParameters =
56+
countedMethods.and(annotatedParametersMatcher);
57+
58+
ElementMatcher.Junction<MethodDescription> timedMethodsWithoutParameters =
59+
countedMethods.and(not(annotatedParametersMatcher));
60+
61+
transformer.applyAdviceToMethod(
62+
timedMethodsWithoutParameters, CountedInstrumentation.class.getName() + "$CountedAdvice");
63+
64+
// Only apply advice for tracing parameters as attributes if any of the parameters are annotated
65+
// with @MetricsAttribute to avoid unnecessarily copying the arguments into an array.
66+
transformer.applyAdviceToMethod(
67+
timedMethodsWithParameters,
68+
CountedInstrumentation.class.getName() + "$CountedAttributesAdvice");
69+
}
70+
71+
@SuppressWarnings("unused")
72+
public static class CountedAttributesAdvice {
73+
74+
@Advice.OnMethodEnter(suppress = Throwable.class)
75+
public static void onEnter(
76+
@Advice.Origin Method originMethod,
77+
@Advice.Local("otelMethod") Method method,
78+
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
79+
@Advice.Local("otelRequest") MethodRequest request,
80+
@Advice.Local("stopwatch") Stopwatch stopwatch) {
81+
82+
// Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
83+
// to local variable so that there would be only one call to Class.getMethod.
84+
method = originMethod;
85+
request = new MethodRequest(method, args);
86+
}
87+
88+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
89+
public static void onExit(
90+
@Advice.Local("otelMethod") Method method,
91+
@Advice.Local("otelRequest") MethodRequest request,
92+
@Advice.Local("stopwatch") Stopwatch stopwatch,
93+
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
94+
@Advice.Thrown Throwable throwable) {
95+
AnnotationSingletons.recordCountWithAttributes(request);
96+
}
97+
}
98+
99+
@SuppressWarnings("unused")
100+
public static class CountedAdvice {
101+
102+
@Advice.OnMethodEnter(suppress = Throwable.class)
103+
public static void onEnter(
104+
@Advice.Origin Method originMethod,
105+
@Advice.Local("otelMethod") Method method,
106+
@Advice.Local("stopwatch") Stopwatch stopwatch) {
107+
// Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
108+
// to local variable so that there would be only one call to Class.getMethod.
109+
method = originMethod;
110+
}
111+
112+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
113+
public static void stopSpan(
114+
@Advice.Local("otelMethod") Method method,
115+
@Advice.Local("stopwatch") Stopwatch stopwatch,
116+
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
117+
@Advice.Thrown Throwable throwable) {
118+
AnnotationSingletons.recordCount(method);
119+
}
120+
}
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
2+
3+
import com.google.auto.service.AutoService;
4+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
5+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
6+
import net.bytebuddy.matcher.ElementMatcher;
7+
import java.util.List;
8+
9+
10+
import application.io.opentelemetry.instrumentation.annotations.Counted;
11+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
12+
import static java.util.Arrays.asList;
13+
14+
@AutoService(CountedInstrumentationsModule.class)
15+
public class CountedInstrumentationsModule extends InstrumentationModule {
16+
17+
public CountedInstrumentationsModule() {
18+
super("opentelemetry-instrumentation-annotations", "counted");
19+
}
20+
21+
@Override
22+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
23+
return hasClassesNamed("application.io.opentelemetry.instrumentation.annotations.Counted");
24+
}
25+
26+
@Override
27+
public List<TypeInstrumentation> typeInstrumentations() {
28+
return asList(new CountedInstrumentation());
29+
}
30+
31+
@Override
32+
public int order() {
33+
// Run first to ensure other automatic instrumentation is added after and therefore is executed
34+
// earlier in the instrumented method and create the span to attach attributes to.
35+
return -1000;
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.opentelemetry.javaagent.instrumentation.instrumentationannotations;
2+
3+
import com.google.auto.service.AutoService;
4+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
5+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
6+
import net.bytebuddy.matcher.ElementMatcher;
7+
import java.util.List;
8+
9+
import application.io.opentelemetry.instrumentation.annotations.Timed;
10+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
11+
import static java.util.Arrays.asList;
12+
13+
@AutoService(TimedInstrumentationsModule.class)
14+
public class TimedInstrumentationsModule extends InstrumentationModule {
15+
16+
public TimedInstrumentationsModule() {
17+
super("opentelemetry-instrumentation-annotations", "timed");
18+
}
19+
20+
@Override
21+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
22+
return hasClassesNamed("application.io.opentelemetry.instrumentation.annotations.Timed");
23+
}
24+
25+
@Override
26+
public List<TypeInstrumentation> typeInstrumentations() {
27+
return asList(new TimedInstrumentation());
28+
}
29+
30+
@Override
31+
public int order() {
32+
// Run first to ensure other automatic instrumentation is added after and therefore is executed
33+
// earlier in the instrumented method and create the span to attach attributes to.
34+
return -1000;
35+
}
36+
}

0 commit comments

Comments
 (0)