Skip to content

Commit 5ea01db

Browse files
committed
test with all metric types
1 parent c35e1e0 commit 5ea01db

File tree

2 files changed

+82
-26
lines changed

2 files changed

+82
-26
lines changed

instrumentation/jmx-metrics/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ When two or more MBean parameters are used, it is also possible to perform a par
396396
- parameters mapped as metric attributes with `param(<mbeanParam>)` are preserved
397397
- values are aggregated with mapped metric attributes
398398

399+
The applied aggregation depends on the metric type:
400+
- `counter` or `updowncounter`: sum aggregation
401+
- `gauge`: last-value aggregation
402+
403+
As a consequence, it is not recommended to use it for `gauge` metrics when querying more than one MBean instance as it would produce unpredictable results.
404+
405+
When there is only a single MBean instance, using a `gauge` metric produces the expected value, hence allowing to avoid mapping all the MBean parameters
406+
to metric attributes.
407+
399408
### General Syntax
400409

401410
Here is the general description of the accepted configuration file syntax. The whole contents of the file is case-sensitive, with exception for `type` as described in the table below.

instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/engine/MetricAggregationTest.java

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
99
import static org.assertj.core.groups.Tuple.tuple;
10+
import static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;
1011

1112
import io.opentelemetry.api.common.AttributeKey;
1213
import io.opentelemetry.api.common.Attributes;
@@ -17,6 +18,7 @@
1718
import io.opentelemetry.sdk.metrics.data.PointData;
1819
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
1920
import java.util.ArrayList;
21+
import java.util.Arrays;
2022
import java.util.Collection;
2123
import java.util.Collections;
2224
import java.util.List;
@@ -31,7 +33,8 @@
3133
import org.junit.jupiter.api.AfterEach;
3234
import org.junit.jupiter.api.BeforeAll;
3335
import org.junit.jupiter.api.BeforeEach;
34-
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.MethodSource;
3538

3639
public class MetricAggregationTest {
3740

@@ -116,39 +119,61 @@ private static ObjectName getObjectName(@Nullable String a, @Nullable String b)
116119
}
117120
}
118121

119-
@Test
120-
void singleInstance() throws Exception {
122+
static List<MetricInfo.Type> metricTypes() {
123+
return Arrays.asList(
124+
MetricInfo.Type.COUNTER, MetricInfo.Type.UPDOWNCOUNTER, MetricInfo.Type.GAUGE);
125+
}
126+
127+
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
128+
@MethodSource("metricTypes")
129+
void singleInstance(MetricInfo.Type metricType) throws Exception {
121130
ObjectName bean = getObjectName(null, null);
122131
theServer.registerMBean(new Hello(42), bean);
123132

124-
Collection<MetricData> data = testMetric(bean.toString(), Collections.emptyList());
125-
checkSingleValue(data, 42);
133+
Collection<MetricData> data = testMetric(bean.toString(), Collections.emptyList(), metricType);
134+
checkSingleValue(data, 42, metricType);
126135
}
127136

128-
@Test
129-
void aggregateOneParam() throws Exception {
137+
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
138+
@MethodSource("metricTypes")
139+
void aggregateOneParam(MetricInfo.Type metricType) throws Exception {
130140
theServer.registerMBean(new Hello(42), getObjectName("value1", null));
131141
theServer.registerMBean(new Hello(37), getObjectName("value2", null));
132142

133143
String bean = getObjectName("*", null).toString();
134-
Collection<MetricData> data = testMetric(bean, Collections.emptyList());
135-
checkSingleValue(data, 79);
144+
Collection<MetricData> data = testMetric(bean, Collections.emptyList(), metricType);
145+
int expected = 79;
146+
if (metricType == MetricInfo.Type.GAUGE) {
147+
// last-value aggregation produces unpredictable result unless a single mbean instance is used
148+
// test here is only used as a way to document behavior and should not be considered a feature
149+
expected = 37;
150+
}
151+
checkSingleValue(data, expected, metricType);
136152
}
137153

138-
@Test
139-
void aggregateMultipleParams() throws Exception {
154+
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
155+
@MethodSource("metricTypes")
156+
void aggregateMultipleParams(MetricInfo.Type metricType) throws Exception {
140157
theServer.registerMBean(new Hello(1), getObjectName("1", "x"));
141158
theServer.registerMBean(new Hello(2), getObjectName("2", "y"));
142159
theServer.registerMBean(new Hello(3), getObjectName("3", "x"));
143160
theServer.registerMBean(new Hello(4), getObjectName("4", "y"));
144161

145162
String bean = getObjectName("*", "*").toString();
146-
Collection<MetricData> data = testMetric(bean, Collections.emptyList());
147-
checkSingleValue(data, 10);
163+
Collection<MetricData> data = testMetric(bean, Collections.emptyList(), metricType);
164+
165+
int expected = 10;
166+
if (metricType == MetricInfo.Type.GAUGE) {
167+
// last-value aggregation produces unpredictable result unless a single mbean instance is used
168+
// test here is only used as a way to document behavior and should not be considered a feature
169+
expected = 1;
170+
}
171+
checkSingleValue(data, expected, metricType);
148172
}
149173

150-
@Test
151-
void partialAggregateMultipleParams() throws Exception {
174+
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
175+
@MethodSource("metricTypes")
176+
void partialAggregateMultipleParams(MetricInfo.Type metricType) throws Exception {
152177
theServer.registerMBean(new Hello(1), getObjectName("1", "x"));
153178
theServer.registerMBean(new Hello(2), getObjectName("2", "y"));
154179
theServer.registerMBean(new Hello(3), getObjectName("3", "x"));
@@ -160,30 +185,44 @@ void partialAggregateMultipleParams() throws Exception {
160185
Collections.singletonList(
161186
new MetricAttribute(
162187
"test.metric.param", MetricAttributeExtractor.fromObjectNameParameter("b")));
163-
Collection<MetricData> data = testMetric(bean, attributes);
188+
Collection<MetricData> data = testMetric(bean, attributes, metricType);
164189

165190
assertThat(data)
166191
.isNotEmpty()
167192
.satisfiesExactly(
168193
metric -> {
169194
assertThat(metric.getName()).isEqualTo("test.metric");
170195
AttributeKey<String> metricAttribute = AttributeKey.stringKey("test.metric.param");
171-
assertThat(metric.getLongSumData().getPoints())
172-
.extracting(LongPointData::getValue, PointData::getAttributes)
173-
.containsExactlyInAnyOrder(
174-
tuple(6L, Attributes.of(metricAttribute, "y")),
175-
tuple(4L, Attributes.of(metricAttribute, "x")));
196+
197+
if (metricType == MetricInfo.Type.GAUGE) {
198+
// last-value aggregation produces unpredictable result unless a single mbean
199+
// instance is used
200+
// test here is only used as a way to document behavior and should not be considered
201+
// a feature
202+
assertThat(metric.getLongGaugeData().getPoints())
203+
.extracting(LongPointData::getValue, PointData::getAttributes)
204+
.containsExactlyInAnyOrder(
205+
tuple(4L, Attributes.of(metricAttribute, "y")),
206+
tuple(1L, Attributes.of(metricAttribute, "x")));
207+
} else {
208+
// sum aggregation
209+
assertThat(metric.getLongSumData().getPoints())
210+
.extracting(LongPointData::getValue, PointData::getAttributes)
211+
.containsExactlyInAnyOrder(
212+
tuple(6L, Attributes.of(metricAttribute, "y")),
213+
tuple(4L, Attributes.of(metricAttribute, "x")));
214+
}
176215
});
177216
}
178217

179-
private Collection<MetricData> testMetric(String mbean, List<MetricAttribute> attributes)
218+
private Collection<MetricData> testMetric(
219+
String mbean, List<MetricAttribute> attributes, MetricInfo.Type metricType)
180220
throws MalformedObjectNameException {
181221
JmxMetricInsight metricInsight = JmxMetricInsight.createService(sdk, 0);
182222
MetricConfiguration metricConfiguration = new MetricConfiguration();
183223
List<MetricExtractor> extractors = new ArrayList<>();
184224

185-
MetricInfo metricInfo =
186-
new MetricInfo("test.metric", "description", null, "1", MetricInfo.Type.COUNTER);
225+
MetricInfo metricInfo = new MetricInfo("test.metric", "description", null, "1", metricType);
187226
BeanAttributeExtractor beanExtractor = BeanAttributeExtractor.fromName("Value");
188227
MetricExtractor extractor = new MetricExtractor(beanExtractor, metricInfo, attributes);
189228
extractors.add(extractor);
@@ -196,13 +235,21 @@ private Collection<MetricData> testMetric(String mbean, List<MetricAttribute> at
196235
return waitMetricsReceived();
197236
}
198237

199-
private static void checkSingleValue(Collection<MetricData> data, long expectedValue) {
238+
private static void checkSingleValue(
239+
Collection<MetricData> data, long expectedValue, MetricInfo.Type metricType) {
200240
assertThat(data)
201241
.isNotEmpty()
202242
.satisfiesExactlyInAnyOrder(
203243
metric -> {
204244
assertThat(metric.getName()).isEqualTo("test.metric");
205-
assertThat(metric.getLongSumData().getPoints())
245+
246+
Collection<LongPointData> points;
247+
if (metricType == MetricInfo.Type.GAUGE) {
248+
points = metric.getLongGaugeData().getPoints();
249+
} else {
250+
points = metric.getLongSumData().getPoints();
251+
}
252+
assertThat(points)
206253
.extracting(LongPointData::getValue, PointData::getAttributes)
207254
.containsExactlyInAnyOrder(tuple(expectedValue, Attributes.empty()));
208255
});

0 commit comments

Comments
 (0)