Migrate JMX metrics last test to Java (#106)
* Migrate JMX metrics last test to Java * DisplayName
This commit is contained in:
parent
d132dc4cfb
commit
2edfb184a0
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.contrib.jmxmetrics
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey
|
||||
import static io.opentelemetry.sdk.metrics.data.MetricDataType.DOUBLE_GAUGE
|
||||
import static io.opentelemetry.sdk.metrics.data.MetricDataType.DOUBLE_SUM
|
||||
import static io.opentelemetry.sdk.metrics.data.MetricDataType.HISTOGRAM
|
||||
import static io.opentelemetry.sdk.metrics.data.MetricDataType.LONG_GAUGE
|
||||
import static io.opentelemetry.sdk.metrics.data.MetricDataType.LONG_SUM
|
||||
import static java.lang.management.ManagementFactory.getPlatformMBeanServer
|
||||
|
||||
import io.opentelemetry.api.common.Attributes
|
||||
import io.opentelemetry.api.metrics.GlobalMeterProvider
|
||||
import javax.management.MBeanServer
|
||||
import javax.management.ObjectName
|
||||
import javax.management.remote.JMXConnectorServer
|
||||
import javax.management.remote.JMXConnectorServerFactory
|
||||
import javax.management.remote.JMXServiceURL
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TestName
|
||||
import org.junit.rules.TestRule
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class InstrumentHelperTest extends Specification {
|
||||
|
||||
@Rule
|
||||
public final TestRule name = new TestName()
|
||||
|
||||
@Shared
|
||||
MBeanServer mBeanServer
|
||||
|
||||
@Shared
|
||||
JMXConnectorServer jmxServer
|
||||
|
||||
@Shared
|
||||
JmxClient jmxClient
|
||||
|
||||
@Shared
|
||||
OtelHelper otel
|
||||
|
||||
def setup() {
|
||||
mBeanServer = getPlatformMBeanServer()
|
||||
|
||||
def serviceUrl = new JMXServiceURL('rmi', 'localhost', 0)
|
||||
jmxServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, [:], mBeanServer)
|
||||
jmxServer.start()
|
||||
def completeAddress = jmxServer.getAddress()
|
||||
|
||||
def jmxConfig = new JmxConfig(new Properties().tap {
|
||||
it.setProperty(JmxConfig.METRICS_EXPORTER_TYPE, 'inmemory')
|
||||
it.setProperty(JmxConfig.SERVICE_URL, "${completeAddress}")
|
||||
})
|
||||
|
||||
jmxClient = new JmxClient(jmxConfig)
|
||||
|
||||
// Set up a MeterSdk per test to be able to collect its metrics alone
|
||||
def gme = new GroovyMetricEnvironment(jmxConfig, name.methodName, '')
|
||||
otel = new OtelHelper(jmxClient, gme)
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
jmxServer.stop()
|
||||
}
|
||||
|
||||
interface ThingMBean {
|
||||
double getDouble()
|
||||
|
||||
long getLong()
|
||||
}
|
||||
|
||||
static class Thing implements ThingMBean {
|
||||
@Override
|
||||
double getDouble() {
|
||||
return 123.456
|
||||
}
|
||||
|
||||
@Override
|
||||
long getLong() {
|
||||
return 234
|
||||
}
|
||||
}
|
||||
|
||||
def exportMetrics() {
|
||||
def provider = GlobalMeterProvider.get().get(name.methodName, '', null)
|
||||
return provider.collectAll(0).sort { md1, md2 ->
|
||||
def p1 = md1.data.points[0]
|
||||
def p2 = md2.data.points[0]
|
||||
def s1 = p1.startEpochNanos
|
||||
def s2 = p2.startEpochNanos
|
||||
if (s1 == s2) {
|
||||
if (md1.type == HISTOGRAM) {
|
||||
return p1.counts[0] <=> p2.counts[0]
|
||||
}
|
||||
return p1.value <=> p2.value
|
||||
}
|
||||
s1 <=> s2
|
||||
}
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "#instrumentMethod via #quantity MBeanHelper"() {
|
||||
setup:
|
||||
"Create and register four Things and create ${quantity} MBeanHelper"
|
||||
def thingName = "${quantity}:type=${instrumentMethod}.Thing"
|
||||
def things = (0..3).collect { new Thing() }
|
||||
things.eachWithIndex { thing, i ->
|
||||
def name = "${thingName},thing=${i}"
|
||||
mBeanServer.registerMBean(thing, new ObjectName(name))
|
||||
}
|
||||
def mbeanHelper = new MBeanHelper(jmxClient, "${thingName},*", isSingle)
|
||||
mbeanHelper.fetch()
|
||||
|
||||
expect:
|
||||
when:
|
||||
def instrumentName = "${quantity}.${instrumentMethod}.counter"
|
||||
def description = "${quantity} double counter description"
|
||||
def instrument = otel.&"${instrumentMethod}"
|
||||
def instrumentHelper = new InstrumentHelper(
|
||||
mbeanHelper, instrumentName, description, "1",
|
||||
["labelOne": { "labelOneValue" }, "labelTwo": { mbean -> mbean.name().getKeyProperty("thing") }],
|
||||
attribute, instrument)
|
||||
instrumentHelper.update()
|
||||
|
||||
then:
|
||||
def metrics = exportMetrics()
|
||||
metrics.size() == 1
|
||||
|
||||
metrics.each { metric ->
|
||||
assert metric.name == instrumentName
|
||||
assert metric.description == description
|
||||
assert metric.unit == "1"
|
||||
assert metric.type == metricType
|
||||
assert metric.data.points.size() == isSingle ? 1 : 4
|
||||
metric.data.points.sort { a, b -> String.compare(a.attributes.get(stringKey("labelTwo")), b.attributes.get(stringKey("labelTwo"))) }
|
||||
metric.data.points.eachWithIndex { point, i ->
|
||||
assert point.attributes == Attributes.of(stringKey("labelOne"), "labelOneValue", stringKey("labelTwo"), "${i}".toString())
|
||||
|
||||
if (metricType == HISTOGRAM) {
|
||||
assert point.count == 1
|
||||
assert point.sum == value
|
||||
assert point.counts[6].value == 1
|
||||
} else {
|
||||
assert point.value == value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
isSingle | quantity | attribute | instrumentMethod | metricType | value
|
||||
true | "single" | "Double" | "doubleCounter" | DOUBLE_SUM | 123.456
|
||||
false | "multiple" | "Double" | "doubleCounter" | DOUBLE_SUM | 123.456
|
||||
true | "single" | "Double" | "doubleUpDownCounter" | DOUBLE_SUM | 123.456
|
||||
false | "multiple" | "Double" | "doubleUpDownCounter" | DOUBLE_SUM | 123.456
|
||||
true | "single" | "Long" | "longCounter" | LONG_SUM | 234
|
||||
false | "multiple" | "Long" | "longCounter" | LONG_SUM | 234
|
||||
true | "single" | "Long" | "longUpDownCounter" | LONG_SUM | 234
|
||||
false | "multiple" | "Long" | "longUpDownCounter" | LONG_SUM | 234
|
||||
true | "single" | "Double" | "doubleHistogram" | HISTOGRAM | 123.456
|
||||
false | "multiple" | "Double" | "doubleHistogram" | HISTOGRAM | 123.456
|
||||
true | "single" | "Long" | "longHistogram" | HISTOGRAM | 234
|
||||
false | "multiple" | "Long" | "longHistogram" | HISTOGRAM | 234
|
||||
true | "single" | "Double" | "doubleCounterCallback" | DOUBLE_SUM | 123.456
|
||||
false | "multiple" | "Double" | "doubleCounterCallback" | DOUBLE_SUM | 123.456
|
||||
true | "single" | "Double" | "doubleUpDownCounterCallback" | DOUBLE_SUM | 123.456
|
||||
false | "multiple" | "Double" | "doubleUpDownCounterCallback" | DOUBLE_SUM | 123.456
|
||||
true | "single" | "Long" | "longCounterCallback" | LONG_SUM | 234
|
||||
false | "multiple" | "Long" | "longCounterCallback" | LONG_SUM | 234
|
||||
true | "single" | "Long" | "longUpDownCounterCallback" | LONG_SUM | 234
|
||||
false | "multiple" | "Long" | "longUpDownCounterCallback" | LONG_SUM | 234
|
||||
true | "single" | "Double" | "doubleValueCallback" | DOUBLE_GAUGE | 123.456
|
||||
false | "multiple" | "Double" | "doubleValueCallback" | DOUBLE_GAUGE | 123.456
|
||||
true | "single" | "Long" | "longValueCallback" | LONG_GAUGE | 234
|
||||
false | "multiple" | "Long" | "longValueCallback" | LONG_GAUGE | 234
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "handles nulls returned from MBeanHelper"() {
|
||||
setup:
|
||||
"Create and register four Things and create ${quantity} MBeanHelper"
|
||||
def thingName = "${quantity}:type=${instrumentMethod}.${attribute}.Thing"
|
||||
def things = (0..3).collect { new Thing() }
|
||||
things.eachWithIndex { thing, i ->
|
||||
def name = "${thingName},thing=${i}"
|
||||
mBeanServer.registerMBean(thing, new ObjectName(name))
|
||||
}
|
||||
def mbeanHelper = new MBeanHelper(jmxClient, "${thingName},*", isSingle)
|
||||
mbeanHelper.fetch()
|
||||
|
||||
expect:
|
||||
when:
|
||||
def instrumentName = "${quantity}.${instrumentMethod}.counter"
|
||||
def description = "${quantity} double counter description"
|
||||
def instrument = otel.&"${instrumentMethod}"
|
||||
def instrumentHelper = new InstrumentHelper(
|
||||
mbeanHelper, instrumentName, description, "1",
|
||||
["labelOne": { "labelOneValue" }, "labelTwo": { mbean -> mbean.name().getKeyProperty("thing") }],
|
||||
attribute, instrument)
|
||||
instrumentHelper.update()
|
||||
|
||||
then:
|
||||
def metrics = exportMetrics()
|
||||
metrics.size() == 0
|
||||
|
||||
where:
|
||||
isSingle | quantity | attribute | instrumentMethod | metricType | value
|
||||
true | "single" | "Missing" | "longValueCallback" | LONG_GAUGE | null
|
||||
false | "multiple" | "Missing" | "longValueCallback" | LONG_GAUGE | null
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "#instrumentMethod correctly classified"() {
|
||||
expect:
|
||||
def instrument = otel.&"${instrumentMethod}"
|
||||
assert InstrumentHelper.instrumentIsObserver(instrument) == isObserver
|
||||
assert InstrumentHelper.instrumentIsCounter(instrument) == isCounter
|
||||
|
||||
where:
|
||||
instrumentMethod | isObserver | isCounter
|
||||
"doubleCounter" | false | true
|
||||
"longCounter" | false | true
|
||||
"doubleCounterCallback" | true | false
|
||||
"longCounterCallback" | true | false
|
||||
"doubleUpDownCounter" | false | true
|
||||
"longUpDownCounter" | false | true
|
||||
"doubleUpDownCounterCallback" | true | false
|
||||
"longUpDownCounterCallback" | true | false
|
||||
"doubleValueCallback" | true | false
|
||||
"longValueCallback" | true | false
|
||||
"doubleHistogram" | false | false
|
||||
"longHistogram" | false | false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.contrib.jmxmetrics;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
import static io.opentelemetry.sdk.testing.assertj.metrics.MetricAssertions.assertThat;
|
||||
import static java.lang.management.ManagementFactory.getPlatformMBeanServer;
|
||||
|
||||
import groovy.lang.Closure;
|
||||
import groovy.util.Eval;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.metrics.data.DoublePointData;
|
||||
import io.opentelemetry.sdk.metrics.data.LongPointData;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
class InstrumenterHelperTest {
|
||||
|
||||
private static final MBeanServer mbeanServer = getPlatformMBeanServer();
|
||||
|
||||
private static final Set<ObjectInstance> registeredBeans = new HashSet<>();
|
||||
|
||||
private static JMXConnectorServer jmxServer;
|
||||
private static JmxClient jmxClient;
|
||||
|
||||
// Will eventually be replaced with Jupiter extension in sdk-testing
|
||||
private SdkMeterProvider meterProvider;
|
||||
|
||||
private OtelHelper otel;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
JMXServiceURL serviceUrl = new JMXServiceURL("rmi", "localhost", 0);
|
||||
jmxServer =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(
|
||||
serviceUrl, Collections.emptyMap(), mbeanServer);
|
||||
jmxServer.start();
|
||||
|
||||
JMXServiceURL completeAddress = jmxServer.getAddress();
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty(JmxConfig.SERVICE_URL, completeAddress.toString());
|
||||
|
||||
jmxClient = new JmxClient(new JmxConfig(props));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() throws Exception {
|
||||
jmxServer.stop();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setupOtel() {
|
||||
meterProvider = SdkMeterProvider.builder().build();
|
||||
|
||||
otel = new OtelHelper(jmxClient, new GroovyMetricEnvironment(meterProvider, "otel.test"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void unregisterBeans() throws Exception {
|
||||
for (ObjectInstance bean : registeredBeans) {
|
||||
mbeanServer.unregisterMBean(bean.getObjectName());
|
||||
}
|
||||
registeredBeans.clear();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("InstrumenterHelperTest - Single Metric")
|
||||
class Single {
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
"doubleCounter",
|
||||
"doubleUpDownCounter",
|
||||
"doubleCounterCallback",
|
||||
"doubleUpDownCounterCallback"
|
||||
})
|
||||
void doubleSum(String instrumentMethod) throws Exception {
|
||||
String thingName = "single:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "single." + instrumentMethod + ".counter";
|
||||
String description = "single double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Double");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(this::assertDoublePoint));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
"longCounter",
|
||||
"longUpDownCounter",
|
||||
"longCounterCallback",
|
||||
"longUpDownCounterCallback"
|
||||
})
|
||||
void longSum(String instrumentMethod) throws Exception {
|
||||
String thingName = "single:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "single." + instrumentMethod + ".counter";
|
||||
String description = "single long counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(this::assertLongPoint));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"doubleHistogram", "longHistogram"})
|
||||
void histogram(String instrumentMethod) throws Exception {
|
||||
String thingName = "single:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "single." + instrumentMethod + ".counter";
|
||||
String description = "single long counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSum(234)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "0"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleValueCallback() throws Exception {
|
||||
String instrumentMethod = "doubleValueCallback";
|
||||
String thingName = "single:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "single." + instrumentMethod + ".counter";
|
||||
String description = "single double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Double");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleGauge()
|
||||
.points()
|
||||
.satisfiesExactly(this::assertDoublePoint));
|
||||
}
|
||||
|
||||
@Test
|
||||
void longValueCallback() throws Exception {
|
||||
String instrumentMethod = "longValueCallback";
|
||||
String thingName = "single:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "single." + instrumentMethod + ".counter";
|
||||
String description = "single double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasLongGauge()
|
||||
.points()
|
||||
.satisfiesExactly(this::assertLongPoint));
|
||||
}
|
||||
|
||||
private void assertDoublePoint(DoublePointData point) {
|
||||
assertThat(point)
|
||||
.hasValue(123.456)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"), attributeEntry("labelTwo", "0"));
|
||||
}
|
||||
|
||||
private void assertLongPoint(LongPointData point) {
|
||||
assertThat(point)
|
||||
.hasValue(234)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"), attributeEntry("labelTwo", "0"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("InstrumenterHelperTest - Multiple Metrics")
|
||||
class Multiple {
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
"doubleCounter",
|
||||
"doubleUpDownCounter",
|
||||
"doubleCounterCallback",
|
||||
"doubleUpDownCounterCallback"
|
||||
})
|
||||
void doubleSum(String instrumentMethod) throws Exception {
|
||||
String thingName = "multiple:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "multiple." + instrumentMethod + ".counter";
|
||||
String description = "multiple double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Double");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactlyInAnyOrder(assertDoublePoints()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
"longCounter",
|
||||
"longUpDownCounter",
|
||||
"longCounterCallback",
|
||||
"longUpDownCounterCallback"
|
||||
})
|
||||
void longSum(String instrumentMethod) throws Exception {
|
||||
String thingName = "multiple:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "multiple." + instrumentMethod + ".counter";
|
||||
String description = "multiple long counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactlyInAnyOrder(assertLongPoints()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"doubleHistogram", "longHistogram"})
|
||||
void histogram(String instrumentMethod) throws Exception {
|
||||
String thingName = "multiple:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "multiple." + instrumentMethod + ".counter";
|
||||
String description = "multiple long counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSum(234)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "0")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSum(234)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "1")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSum(234)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "2")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSum(234)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "3"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doubleValueCallback() throws Exception {
|
||||
String instrumentMethod = "doubleValueCallback";
|
||||
String thingName = "multiple:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "multiple." + instrumentMethod + ".counter";
|
||||
String description = "multiple double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Double");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasDoubleGauge()
|
||||
.points()
|
||||
.satisfiesExactlyInAnyOrder(assertDoublePoints()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void longValueCallback() throws Exception {
|
||||
String instrumentMethod = "longValueCallback";
|
||||
String thingName = "multiple:type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = "multiple." + instrumentMethod + ".counter";
|
||||
String description = "multiple double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Long");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName(instrumentName)
|
||||
.hasDescription(description)
|
||||
.hasUnit("1")
|
||||
.hasLongGauge()
|
||||
.points()
|
||||
.satisfiesExactlyInAnyOrder(assertLongPoints()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Consumer<DoublePointData>[] assertDoublePoints() {
|
||||
return Stream.<Consumer<DoublePointData>>of(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(123.456)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "0")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(123.456)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "1")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(123.456)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "2")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(123.456)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "3")))
|
||||
.toArray(Consumer[]::new);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Consumer<LongPointData>[] assertLongPoints() {
|
||||
return Stream.<Consumer<LongPointData>>of(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(234)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "0")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(234)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "1")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(234)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "2")),
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(234)
|
||||
.attributes()
|
||||
.containsOnly(
|
||||
attributeEntry("labelOne", "labelOneValue"),
|
||||
attributeEntry("labelTwo", "3")))
|
||||
.toArray(Consumer[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void handlesNulls(boolean isSingle) throws Exception {
|
||||
String instrumentMethod = "longValueCallback";
|
||||
String quantity = isSingle ? "single" : "multiple";
|
||||
String thingName = quantity + ":type=" + instrumentMethod + ".Thing";
|
||||
MBeanHelper mBeanHelper = registerThings(thingName);
|
||||
|
||||
String instrumentName = quantity + "." + instrumentMethod + ".counter";
|
||||
String description = quantity + " double counter description";
|
||||
|
||||
updateWithHelper(mBeanHelper, instrumentMethod, instrumentName, description, "Missing");
|
||||
|
||||
assertThat(meterProvider.collectAllMetrics()).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"doubleCounter,false,true",
|
||||
"longCounter,false,true",
|
||||
"doubleCounterCallback,true,false",
|
||||
"longCounterCallback,true,false",
|
||||
"doubleUpDownCounter,false,true",
|
||||
"longUpDownCounter,false,true",
|
||||
"doubleUpDownCounterCallback,true,false",
|
||||
"longUpDownCounterCallback,true,false",
|
||||
"doubleValueCallback,true,false",
|
||||
"longValueCallback,true,false",
|
||||
"doubleHistogram,false,false",
|
||||
"longHistogram,false,false",
|
||||
})
|
||||
void correctlyClassified(String instrumentMethod, boolean isObserver, boolean isCounter) {
|
||||
Closure<?> instrument = (Closure<?>) Eval.me("otel", otel, "otel.&" + instrumentMethod);
|
||||
assertThat(InstrumentHelper.instrumentIsObserver(instrument)).isEqualTo(isObserver);
|
||||
assertThat(InstrumentHelper.instrumentIsCounter(instrument)).isEqualTo(isCounter);
|
||||
}
|
||||
|
||||
MBeanHelper registerThings(String thingName) throws Exception {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Thing thing = new Thing();
|
||||
String name = thingName + ",thing=" + i;
|
||||
registeredBeans.add(mbeanServer.registerMBean(thing, new ObjectName(name)));
|
||||
}
|
||||
|
||||
MBeanHelper mBeanHelper =
|
||||
new MBeanHelper(jmxClient, thingName + ",*", thingName.startsWith("single:"));
|
||||
mBeanHelper.fetch();
|
||||
return mBeanHelper;
|
||||
}
|
||||
|
||||
void updateWithHelper(
|
||||
MBeanHelper mBeanHelper,
|
||||
String instrumentMethod,
|
||||
String instrumentName,
|
||||
String description,
|
||||
String attribute) {
|
||||
Closure<?> instrument = (Closure<?>) Eval.me("otel", otel, "otel.&" + instrumentMethod);
|
||||
Map<String, Closure> labelFuncs = new HashMap<>();
|
||||
labelFuncs.put("labelOne", (Closure<?>) Eval.me("{ unused -> 'labelOneValue' }"));
|
||||
labelFuncs.put(
|
||||
"labelTwo", (Closure<?>) Eval.me("{ mbean -> mbean.name().getKeyProperty('thing') }"));
|
||||
InstrumentHelper instrumentHelper =
|
||||
new InstrumentHelper(
|
||||
mBeanHelper, instrumentName, description, "1", labelFuncs, attribute, instrument);
|
||||
instrumentHelper.update();
|
||||
}
|
||||
|
||||
public interface ThingMBean {
|
||||
double getDouble();
|
||||
|
||||
long getLong();
|
||||
}
|
||||
|
||||
static class Thing implements ThingMBean {
|
||||
@Override
|
||||
public double getDouble() {
|
||||
return 123.456;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong() {
|
||||
return 234;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue