Skip to content

Commit 54866bf

Browse files
support returnValueAttribute and exception attribtue for Timed and Counted
1 parent 3e94d78 commit 54866bf

File tree

14 files changed

+246
-39
lines changed

14 files changed

+246
-39
lines changed

.gitignore

-7
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,5 @@ hs_err_pid*
5858
replay_pid*
5959
.attach_pid*
6060

61-
# add this so that it can be easy to switch to the annotation class for coding.
62-
# it would be removed before being merged to main
63-
instrumentation/opentelemetry-instrumentation-annotations-1.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/annotations/
64-
instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/counted/annotations/
65-
instrumentation/opentelemetry-instrumentation-annotation-timed/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/timed/annotations/
66-
67-
6861
!java-agent/benchmark/releases/*.jar
6962

instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ tasks {
4040
}
4141
test {
4242
jvmArgs(
43-
"-Dotel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods=io.opentelemetry.test.annotation.CountedExample[exampleIgnore]"
43+
"-Dotel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods=io.opentelemetry.test.annotations.counted.CountedExample[exampleIgnore]"
4444
)
4545
}
4646
}
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import java.util.concurrent.ConcurrentHashMap;
1919
import java.util.concurrent.ConcurrentMap;
2020

21-
public final class CountedSingletons {
21+
public final class CountedHelper {
2222

2323
private static final String INSTRUMENTATION_NAME =
2424
"io.opentelemetry.opentelemetry-instrumentation-annotation-counted";
@@ -53,19 +53,43 @@ private static void extractAdditionAttributes(
5353
}
5454
}
5555

56-
public static void recordCountWithAttributes(MethodRequest methodRequest) {
56+
public static void recordCountWithAttributes(
57+
MethodRequest methodRequest, Object returnValue, Throwable throwable) {
5758
Counted countedAnnotation = methodRequest.method().getAnnotation(Counted.class);
58-
AttributesBuilder attributesBuilder = Attributes.builder();
59+
AttributesBuilder attributesBuilder =
60+
getCommonAttributesBuilder(countedAnnotation, returnValue, throwable);
5961
extractMetricAttributes(methodRequest, attributesBuilder);
60-
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
6162
getCounter(methodRequest.method()).add(1, attributesBuilder.build());
6263
}
6364

64-
public static void recordCount(Method method) {
65+
public static void recordCount(Method method, Object returnValue, Throwable throwable) {
6566
Counted countedAnnotation = method.getAnnotation(Counted.class);
67+
AttributesBuilder attributesBuilder =
68+
getCommonAttributesBuilder(countedAnnotation, returnValue, throwable);
69+
getCounter(method).add(1, attributesBuilder.build());
70+
}
71+
72+
private static AttributesBuilder getCommonAttributesBuilder(
73+
Counted countedAnnotation, Object returnValue, Throwable throwable) {
6674
AttributesBuilder attributesBuilder = Attributes.builder();
6775
extractAdditionAttributes(countedAnnotation.additionalAttributes(), attributesBuilder);
68-
getCounter(method).add(1, attributesBuilder.build());
76+
extractReturnValue(countedAnnotation, returnValue, attributesBuilder);
77+
extractException(throwable, attributesBuilder);
78+
return attributesBuilder;
79+
}
80+
81+
private static void extractException(Throwable throwable, AttributesBuilder attributesBuilder) {
82+
if (null != throwable) {
83+
attributesBuilder.put("exception", throwable.getClass().getName());
84+
}
85+
}
86+
87+
private static void extractReturnValue(
88+
Counted countedAnnotation, Object returnValue, AttributesBuilder attributesBuilder) {
89+
if (null != countedAnnotation.returnValueAttribute()
90+
&& !countedAnnotation.returnValueAttribute().isEmpty()) {
91+
attributesBuilder.put(countedAnnotation.returnValueAttribute(), returnValue.toString());
92+
}
6993
}
7094

7195
private static LongCounter getCounter(Method method) {
@@ -95,5 +119,5 @@ private static LongCounter getCounter(Method method) {
95119
return COUNTERS.get(metricName);
96120
}
97121

98-
private CountedSingletons() {}
122+
private CountedHelper() {}
99123
}

instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/counted/CountedInstrumentation.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,12 @@ 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-
CountedSingletons.recordCountWithAttributes(request);
95+
CountedHelper.recordCountWithAttributes(request, returnValue, throwable);
9696
}
9797
}
9898

9999
@SuppressWarnings("unused")
100100
public static class CountedAdvice {
101-
102101
@Advice.OnMethodEnter(suppress = Throwable.class)
103102
public static void onEnter(
104103
@Advice.Origin Method originMethod, @Advice.Local("otelMethod") Method method) {
@@ -108,11 +107,11 @@ public static void onEnter(
108107
}
109108

110109
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
111-
public static void stopSpan(
110+
public static void onExit(
112111
@Advice.Local("otelMethod") Method method,
113112
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
114113
@Advice.Thrown Throwable throwable) {
115-
CountedSingletons.recordCount(method);
114+
CountedHelper.recordCount(method, returnValue, throwable);
116115
}
117116
}
118117
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class CountedExample {
1212
public static final String ANOTHER_NAME_COUNT = "another.name.count";
1313
public static final String METRIC_DESCRIPTION = "I am the description.";
1414
public static final String METRIC_UNIT = "ms";
15+
public static final String RETURN_STRING = "I am a return string.";
1516

1617
@Counted
1718
public void defaultExample() {}
@@ -37,6 +38,23 @@ public void exampleWithAdditionalAttributes1() {}
3738
@Counted(additionalAttributes = {"key1", "value1", "key2", "value2", "key3"})
3839
public void exampleWithAdditionalAttributes2() {}
3940

41+
@Counted(returnValueAttribute = "returnValue")
42+
public ReturnObject exampleWithReturnValueAttribute() {
43+
return new ReturnObject();
44+
}
45+
46+
@Counted
47+
public void exampleWithException() {
48+
throw new IllegalStateException("test exception.");
49+
}
50+
4051
@Counted
4152
public void exampleIgnore() {}
53+
54+
public static class ReturnObject {
55+
@Override
56+
public String toString() {
57+
return RETURN_STRING;
58+
}
59+
}
4260
}

instrumentation/opentelemetry-instrumentation-annotation-counted/javaagent/src/test/java/io/opentelemetry/test/annotations/counted/CountedInstrumentationTest.java

+51
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,55 @@ void testExampleWithAdditionalAttributes2() {
117117
== p.getAttributes().get(AttributeKey.stringKey("key3")));
118118
}));
119119
}
120+
121+
@Test
122+
void testExampleWithReturnAttribute() {
123+
new CountedExample().exampleWithReturnValueAttribute();
124+
testing.waitAndAssertMetrics(
125+
INSTRUMENTATION_NAME,
126+
metric ->
127+
metric
128+
.hasName(COUNTED_DEFAULT_NAME)
129+
.satisfies(
130+
metricData -> {
131+
assertThat(metricData.getData().getPoints())
132+
.allMatch(
133+
p ->
134+
CountedExample.RETURN_STRING.equals(
135+
p.getAttributes()
136+
.get(AttributeKey.stringKey("returnValue"))));
137+
}));
138+
}
139+
140+
@Test
141+
void testExampleWithException() {
142+
try {
143+
new CountedExample().exampleWithException();
144+
} catch (IllegalStateException e) {
145+
// noop
146+
}
147+
testing.waitAndAssertMetrics(
148+
INSTRUMENTATION_NAME,
149+
metric ->
150+
metric
151+
.hasName(COUNTED_DEFAULT_NAME)
152+
.satisfies(
153+
metricData -> {
154+
assertThat(metricData.getData().getPoints())
155+
.allMatch(
156+
p ->
157+
IllegalStateException.class
158+
.getName()
159+
.equals(
160+
p.getAttributes()
161+
.get(AttributeKey.stringKey("exception"))));
162+
}));
163+
}
164+
165+
@Test
166+
void testExampleIgnore() throws Exception {
167+
new CountedExample().exampleIgnore();
168+
Thread.sleep(500); // sleep a bit just to make sure no metric is captured
169+
assertThat(testing.metrics()).isEmpty();
170+
}
120171
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Settings for the OpenTelemetry Timed Instrumentation Annotations integration
2+
3+
| Environment variable | Type | Default | Description |
4+
|-----------------------------------------------------------------------------------------| ------ | ------- |-----------------------------------------------------------------------------------|
5+
| `otel.instrumentation.opentelemetry-instrumentation-annotation-Timed.exclude-methods` | String | | All methods to be excluded from auto-instrumentation by Timed annotation advices. |

instrumentation/opentelemetry-instrumentation-annotation-timed/javaagent/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ tasks {
4040
}
4141
test {
4242
jvmArgs(
43-
"-Dotel.instrumentation.opentelemetry-instrumentation-annotation-timed.exclude-methods=io.opentelemetry.test.annotation.TimedExample[exampleIgnore]"
43+
"-Dotel.instrumentation.opentelemetry-instrumentation-annotation-timed.exclude-methods=io.opentelemetry.test.annotations.timed.TimedExample[exampleIgnore]"
4444
)
4545
}
4646
}

instrumentation/opentelemetry-instrumentation-annotation-timed/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/timed/AnnotationExcludedMethods.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
public final class AnnotationExcludedMethods {
2222

2323
private static final String TIMED_METHODS_EXCLUDE_CONFIG =
24-
"otel.instrumentation.opentelemetry-instrumentation-annotation-counted.exclude-methods";
24+
"otel.instrumentation.opentelemetry-instrumentation-annotation-timed.exclude-methods";
2525

2626
/*
2727
Returns a matcher for all methods that should be excluded from auto-instrumentation by
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55

66
package io.opentelemetry.javaagent.instrumentation.timed;
77

8+
import static java.util.concurrent.TimeUnit.MICROSECONDS;
9+
import static java.util.concurrent.TimeUnit.NANOSECONDS;
10+
import static java.util.concurrent.TimeUnit.SECONDS;
11+
812
import application.io.opentelemetry.instrumentation.annotations.MetricAttribute;
913
import application.io.opentelemetry.instrumentation.annotations.Timed;
14+
// import io.opentelemetry.javaagent.instrumentation.timed.annotations.Timed;
15+
// import io.opentelemetry.javaagent.instrumentation.timed.annotations.MetricAttribute;
1016
import io.opentelemetry.api.GlobalOpenTelemetry;
1117
import io.opentelemetry.api.common.Attributes;
1218
import io.opentelemetry.api.common.AttributesBuilder;
@@ -19,7 +25,7 @@
1925
import java.util.concurrent.ConcurrentMap;
2026
import java.util.concurrent.TimeUnit;
2127

22-
public final class TimedSingletons {
28+
public final class TimedHelper {
2329

2430
private static final String INSTRUMENTATION_NAME =
2531
"io.opentelemetry.opentelemetry-instrumentation-annotation-timed";
@@ -58,24 +64,52 @@ private static void extractAdditionAttributes(
5864
}
5965

6066
public static void recordHistogramWithAttributes(
61-
MethodRequest methodRequest, long startNanoTime) {
67+
MethodRequest methodRequest, Throwable throwable, Object returnValue, long startNanoTime) {
6268
Timed timedAnnotation = methodRequest.method().getAnnotation(Timed.class);
63-
AttributesBuilder attributesBuilder = Attributes.builder();
69+
AttributesBuilder attributesBuilder =
70+
getCommonAttributeBuilder(throwable, returnValue, timedAnnotation);
71+
double duration = getTransformedDuration(startNanoTime, timedAnnotation);
6472
extractMetricAttributes(methodRequest, attributesBuilder);
65-
extractAdditionAttributes(timedAnnotation.additionalAttributes(), attributesBuilder);
66-
long nanoTimeDelta = System.nanoTime() - startNanoTime;
67-
double duration = ((double) nanoTimeDelta) / 1000_000_000;
6873
getHistogram(timedAnnotation).record(duration, attributesBuilder.build());
6974
}
7075

71-
public static void recordHistogram(Method method, long startNanoTime) {
76+
public static void recordHistogram(
77+
Method method, Throwable throwable, Object returnValue, long startNanoTime) {
7278
Timed timedAnnotation = method.getAnnotation(Timed.class);
79+
AttributesBuilder attributesBuilder =
80+
getCommonAttributeBuilder(throwable, returnValue, timedAnnotation);
81+
double duration = getTransformedDuration(startNanoTime, timedAnnotation);
82+
getHistogram(timedAnnotation).record(duration, attributesBuilder.build());
83+
}
84+
85+
private static AttributesBuilder getCommonAttributeBuilder(
86+
Throwable throwable, Object returnValue, Timed timedAnnotation) {
7387
AttributesBuilder attributesBuilder = Attributes.builder();
7488
extractAdditionAttributes(timedAnnotation.additionalAttributes(), attributesBuilder);
89+
extractReturnValue(timedAnnotation, returnValue, attributesBuilder);
90+
extractException(throwable, attributesBuilder);
91+
return attributesBuilder;
92+
}
93+
94+
private static double getTransformedDuration(long startNanoTime, Timed timedAnnotation) {
7595
TimeUnit unit = extractTimeUnit(timedAnnotation);
7696
long nanoDelta = System.nanoTime() - startNanoTime;
77-
double duration = unit.convert(nanoDelta, TimeUnit.NANOSECONDS);
78-
getHistogram(timedAnnotation).record(duration, attributesBuilder.build());
97+
double duration = unit.convert(nanoDelta, NANOSECONDS);
98+
return duration;
99+
}
100+
101+
private static void extractException(Throwable throwable, AttributesBuilder attributesBuilder) {
102+
if (null != throwable) {
103+
attributesBuilder.put("exception", throwable.getClass().getName());
104+
}
105+
}
106+
107+
private static void extractReturnValue(
108+
Timed countedAnnotation, Object returnValue, AttributesBuilder attributesBuilder) {
109+
if (null != countedAnnotation.returnValueAttribute()
110+
&& !countedAnnotation.returnValueAttribute().isEmpty()) {
111+
attributesBuilder.put(countedAnnotation.returnValueAttribute(), returnValue.toString());
112+
}
79113
}
80114

81115
private static DoubleHistogram getHistogram(Timed timedAnnotation) {
@@ -125,5 +159,5 @@ private static String extractUnitStr(Timed timedAnnotation) {
125159
}
126160
}
127161

128-
private TimedSingletons() {}
162+
private TimedHelper() {}
129163
}

instrumentation/opentelemetry-instrumentation-annotation-timed/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/timed/TimedInstrumentation.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static void onExit(
9595
@Advice.Local("startNanoTime") long startNanoTime,
9696
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
9797
@Advice.Thrown Throwable throwable) {
98-
TimedSingletons.recordHistogramWithAttributes(request, startNanoTime);
98+
TimedHelper.recordHistogramWithAttributes(request, throwable, returnValue, startNanoTime);
9999
}
100100
}
101101

@@ -114,12 +114,12 @@ public static void onEnter(
114114
}
115115

116116
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
117-
public static void stopSpan(
117+
public static void onExit(
118118
@Advice.Local("otelMethod") Method method,
119119
@Advice.Local("startNanoTime") long startNanoTime,
120120
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
121121
@Advice.Thrown Throwable throwable) {
122-
TimedSingletons.recordHistogram(method, startNanoTime);
122+
TimedHelper.recordHistogram(method, throwable, returnValue, startNanoTime);
123123
}
124124
}
125125
}

instrumentation/opentelemetry-instrumentation-annotation-timed/javaagent/src/test/java/io/opentelemetry/test/annotations/timed/TimedExample.java

+24-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class TimedExample {
1212
public static final String ANOTHER_NAME_HISTOGRAM = "another.name.duration";
1313
public static final String METRIC_DESCRIPTION = "I am the description.";
14-
public static final String METRIC_UNIT = "ms";
14+
public static final String RETURN_STRING = "I am a return string.";
1515

1616
@Timed
1717
public void defaultExample() {}
@@ -29,11 +29,33 @@ public void exampleWithUnitUSAndDefaultValue() {}
2929
public void exampleWithDescription() {}
3030

3131
@Timed(value = "example.with.unit.duration", unit = TimeUnit.SECONDS)
32-
public void exampleWithUnitS() {}
32+
public void exampleWithUnitSecondAnd2SecondLatency() throws InterruptedException {
33+
Thread.sleep(2000);
34+
}
3335

3436
@Timed(additionalAttributes = {"key1", "value1", "key2", "value2"})
3537
public void exampleWithAdditionalAttributes1() {}
3638

3739
@Timed(additionalAttributes = {"key1", "value1", "key2", "value2", "key3"})
3840
public void exampleWithAdditionalAttributes2() {}
41+
42+
@Timed
43+
public void exampleIgnore() {}
44+
45+
@Timed
46+
public void exampleWithException() {
47+
throw new IllegalStateException("test");
48+
}
49+
50+
@Timed(returnValueAttribute = "returnValue")
51+
public ReturnObject exampleWithReturnValueAttribute() {
52+
return new ReturnObject();
53+
}
54+
55+
public static class ReturnObject {
56+
@Override
57+
public String toString() {
58+
return RETURN_STRING;
59+
}
60+
}
3961
}

0 commit comments

Comments
 (0)