Add asynchronous instruments to JMX Metric Gatherer (#18)

This commit is contained in:
Ryan Fitzpatrick 2020-11-06 15:50:07 -05:00 committed by GitHub
parent 9e0a5d6c2d
commit 657693ed1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1239 additions and 10 deletions

View File

@ -110,6 +110,30 @@ aren't desired upon invocation.
- `otel.<meterMethod>(String name)` - `description` is empty string, `unit` is "1" and `labels` are empty map.
### OpenTelemetry Asynchronous Instrument Helpers
- `otel.doubleSumObserver(String name, String description, String unit, Map<String, String> labels)`
- `otel.longSumObserver(String name, String description, String unit, Map<String, String> labels)`
- `otel.doubleUpDownSumObserver(String name, String description, String unit, Map<String, String> labels)`
- `otel.longUpDownSumObserver(String name, String description, String unit, Map<String, String> labels)`
- `otel.doubleValueObserver(String name, String description, String unit, Map<String, String> labels)`
- `otel.longValueObserver(String name, String description, String unit, Map<String, String> labels)`
These methods will return a new or previously registered instance of the applicable metric
instruments. Each one provides three additional signatures where labels, unit, and description
aren't desired upon invocation.
- `otel.<meterMethod>(String name, String description, String unit)` - `labels` are empty map.
- `otel.<meterMethod>(String name, String description)` - `unit` is "1" and `labels` are empty map.
- `otel.<meterMethod>(String name)` - `description` is empty string, `unit` is "1" and `labels` are empty map.
### Compatibility
This metric extension supports Java 7+, though SASL is only supported where

View File

@ -17,9 +17,16 @@
package io.opentelemetry.contrib.jmxmetrics
import io.opentelemetry.metrics.DoubleCounter
import io.opentelemetry.metrics.DoubleSumObserver
import io.opentelemetry.metrics.DoubleUpDownCounter
import io.opentelemetry.metrics.DoubleUpDownSumObserver
import io.opentelemetry.metrics.DoubleValueObserver
import io.opentelemetry.metrics.LongCounter
import io.opentelemetry.metrics.LongSumObserver
import io.opentelemetry.metrics.LongUpDownCounter
import io.opentelemetry.metrics.LongUpDownSumObserver
import io.opentelemetry.metrics.LongValueObserver
import java.util.logging.Logger
import javax.management.openmbean.CompositeData
@ -89,6 +96,11 @@ class InstrumentHelper {
return
}
// Observer instruments need to have a single callback set, so pool all update
// operations in a list of closures per instrument to be executed after all values
// are established, potentially as the callback.
def instToUpdates = [:]
[mbeans, values].transpose().each { mbean, value ->
if (value instanceof CompositeData) {
value.getCompositeType().keySet().each { key ->
@ -96,14 +108,36 @@ class InstrumentHelper {
def updatedInstrumentName = "${instrumentName}.${key}"
def labels = getLabels(mbean, labelFuncs)
def inst = instrument(updatedInstrumentName, description, unit)
println "InstrumentHelper.update (composite) - ${inst}"
logger.fine("Recording ${updatedInstrumentName} - ${inst} w/ ${val} - ${labels}")
updateInstrumentWithValue(inst, val, labels)
if (!instToUpdates.containsKey(inst)) {
instToUpdates[inst] = []
}
instToUpdates[inst].add(prepareUpdateClosure(inst, val, labels))
}
} else {
def labels = getLabels(mbean, labelFuncs)
def inst = instrument(instrumentName, description, unit)
println "InstrumentHelper.update - ${inst}"
logger.fine("Recording ${instrumentName} - ${inst} w/ ${value} - ${labels}")
updateInstrumentWithValue(inst, value, labels)
if (!instToUpdates.containsKey(inst)) {
instToUpdates[inst] = []
}
instToUpdates[inst].add(prepareUpdateClosure(inst, value, labels))
}
}
instToUpdates.each {inst, updateClosures ->
if (instrumentIsObserver(inst)) {
inst.setCallback({ result ->
updateClosures.each { update ->
update(result)
}
})
} else {
updateClosures.each {
it(inst)
}
}
}
}
@ -116,15 +150,32 @@ class InstrumentHelper {
return labels
}
private static void updateInstrumentWithValue(inst, value, labels) {
private static Closure prepareUpdateClosure(inst, value, labels) {
def labelMap = GroovyMetricEnvironment.mapToLabels(labels)
if (inst instanceof DoubleCounter
if (instrumentIsObserver(inst)) {
return { result ->
result.observe(value, labelMap)
}
} else if (instrumentIsCounter(inst)) {
return { i -> i.add(value, labelMap) }
} else {
return { i -> i.record(value, labelMap) }
}
}
private static boolean instrumentIsObserver(inst) {
return (inst instanceof DoubleSumObserver
|| inst instanceof DoubleUpDownSumObserver
|| inst instanceof LongSumObserver
|| inst instanceof LongUpDownSumObserver
|| inst instanceof DoubleValueObserver
|| inst instanceof LongValueObserver)
}
private static boolean instrumentIsCounter(inst) {
return (inst instanceof DoubleCounter
|| inst instanceof DoubleUpDownCounter
|| inst instanceof LongCounter
|| inst instanceof LongUpDownCounter) {
inst.add(value, labelMap)
} else {
inst.record(value, labelMap)
}
|| inst instanceof LongUpDownCounter)
}
}

View File

@ -17,10 +17,16 @@
package io.opentelemetry.contrib.jmxmetrics
import io.opentelemetry.metrics.DoubleCounter
import io.opentelemetry.metrics.DoubleSumObserver
import io.opentelemetry.metrics.DoubleUpDownCounter
import io.opentelemetry.metrics.DoubleUpDownSumObserver
import io.opentelemetry.metrics.DoubleValueObserver
import io.opentelemetry.metrics.DoubleValueRecorder
import io.opentelemetry.metrics.LongCounter
import io.opentelemetry.metrics.LongSumObserver
import io.opentelemetry.metrics.LongUpDownCounter
import io.opentelemetry.metrics.LongUpDownSumObserver
import io.opentelemetry.metrics.LongValueObserver
import io.opentelemetry.metrics.LongValueRecorder
import javax.management.ObjectName
@ -196,4 +202,100 @@ class OtelHelper {
LongValueRecorder longValueRecorder(String name) {
return longValueRecorder(name, '')
}
DoubleSumObserver doubleSumObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getDoubleSumObserver(name, description, unit, labels)
}
DoubleSumObserver doubleSumObserver(String name, String description, String unit) {
return doubleSumObserver(name, description, unit, null)
}
DoubleSumObserver doubleSumObserver(String name, String description) {
return doubleSumObserver(name, description, SCALAR)
}
DoubleSumObserver doubleSumObserver(String name) {
return doubleSumObserver(name, '')
}
LongSumObserver longSumObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getLongSumObserver(name, description, unit, labels)
}
LongSumObserver longSumObserver(String name, String description, String unit) {
return longSumObserver(name, description, unit, null)
}
LongSumObserver longSumObserver(String name, String description) {
return longSumObserver(name, description, SCALAR)
}
LongSumObserver longSumObserver(String name) {
return longSumObserver(name, '')
}
DoubleUpDownSumObserver doubleUpDownSumObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getDoubleUpDownSumObserver(name, description, unit, labels)
}
DoubleUpDownSumObserver doubleUpDownSumObserver(String name, String description, String unit) {
return doubleUpDownSumObserver(name, description, unit, null)
}
DoubleUpDownSumObserver doubleUpDownSumObserver(String name, String description) {
return doubleUpDownSumObserver(name, description, SCALAR)
}
DoubleUpDownSumObserver doubleUpDownSumObserver(String name) {
return doubleUpDownSumObserver(name, '')
}
LongUpDownSumObserver longUpDownSumObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getLongUpDownSumObserver(name, description, unit, labels)
}
LongUpDownSumObserver longUpDownSumObserver(String name, String description, String unit) {
return longUpDownSumObserver(name, description, unit, null)
}
LongUpDownSumObserver longUpDownSumObserver(String name, String description) {
return longUpDownSumObserver(name, description, SCALAR)
}
LongUpDownSumObserver longUpDownSumObserver(String name) {
return longUpDownSumObserver(name, '')
}
DoubleValueObserver doubleValueObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getDoubleValueObserver(name, description, unit, labels)
}
DoubleValueObserver doubleValueObserver(String name, String description, String unit) {
return doubleValueObserver(name, description, unit, null)
}
DoubleValueObserver doubleValueObserver(String name, String description) {
return doubleValueObserver(name, description, SCALAR)
}
DoubleValueObserver doubleValueObserver(String name) {
return doubleValueObserver(name, '')
}
LongValueObserver longValueObserver(String name, String description, String unit, Map<String, String> labels) {
return groovyMetricEnvironment.getLongValueObserver(name, description, unit, labels)
}
LongValueObserver longValueObserver(String name, String description, String unit) {
return longValueObserver(name, description, unit, null)
}
LongValueObserver longValueObserver(String name, String description) {
return longValueObserver(name, description, SCALAR)
}
LongValueObserver longValueObserver(String name) {
return longValueObserver(name, '')
}
}

View File

@ -23,10 +23,16 @@ import io.opentelemetry.exporters.logging.LoggingMetricExporter;
import io.opentelemetry.exporters.otlp.OtlpGrpcMetricExporter;
import io.opentelemetry.exporters.prometheus.PrometheusCollector;
import io.opentelemetry.metrics.DoubleCounter;
import io.opentelemetry.metrics.DoubleSumObserver;
import io.opentelemetry.metrics.DoubleUpDownCounter;
import io.opentelemetry.metrics.DoubleUpDownSumObserver;
import io.opentelemetry.metrics.DoubleValueObserver;
import io.opentelemetry.metrics.DoubleValueRecorder;
import io.opentelemetry.metrics.LongCounter;
import io.opentelemetry.metrics.LongSumObserver;
import io.opentelemetry.metrics.LongUpDownCounter;
import io.opentelemetry.metrics.LongUpDownSumObserver;
import io.opentelemetry.metrics.LongValueObserver;
import io.opentelemetry.metrics.LongValueRecorder;
import io.opentelemetry.metrics.Meter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
@ -258,4 +264,142 @@ public class GroovyMetricEnvironment {
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link DoubleSumObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link DoubleSumObserver}
*/
public DoubleSumObserver getDoubleSumObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.doubleSumObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link LongSumObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link LongSumObserver}
*/
public LongSumObserver getLongSumObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.longSumObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link DoubleUpDownSumObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link DoubleUpDownSumObserver}
*/
public DoubleUpDownSumObserver getDoubleUpDownSumObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.doubleUpDownSumObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link LongUpDownSumObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link LongUpDownSumObserver}
*/
public LongUpDownSumObserver getLongUpDownSumObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.longUpDownSumObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link DoubleValueObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link DoubleValueObserver}
*/
public DoubleValueObserver getDoubleValueObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.doubleValueObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
/**
* Build or retrieve previously registered {@link LongValueObserver}.
*
* @param name - metric name
* @param description metric description
* @param unit - metric unit
* @param constantLabels - metric descriptor's constant labels
* @return new or memoized {@link LongValueObserver}
*/
public LongValueObserver getLongValueObserver(
final String name,
final String description,
final String unit,
final Map<String, String> constantLabels) {
Labels labels = mapToLabels(constantLabels);
return meter
.longValueObserverBuilder(name)
.setDescription(description)
.setUnit(unit)
.setConstantLabels(labels)
.build();
}
}

View File

@ -175,5 +175,17 @@ class InstrumentHelperTest extends Specification {
false | "multiple" | "Double" | "doubleValueRecorder" | SUMMARY | 123.456
true | "single" | "Long" | "longValueRecorder" | SUMMARY | 234
false | "multiple" | "Long" | "longValueRecorder" | SUMMARY | 234
true | "single" | "Double" | "doubleSumObserver" | MONOTONIC_DOUBLE | 123.456
false | "multiple" | "Double" | "doubleSumObserver" | MONOTONIC_DOUBLE | 123.456
true | "single" | "Double" | "doubleUpDownSumObserver" | NON_MONOTONIC_DOUBLE | 123.456
false | "multiple" | "Double" | "doubleUpDownSumObserver" | NON_MONOTONIC_DOUBLE | 123.456
true | "single" | "Long" | "longSumObserver" | MONOTONIC_LONG | 234
false | "multiple" | "Long" | "longSumObserver" | MONOTONIC_LONG | 234
true | "single" | "Long" | "longUpDownSumObserver" | NON_MONOTONIC_LONG | 234
false | "multiple" | "Long" | "longUpDownSumObserver" | NON_MONOTONIC_LONG | 234
true | "single" | "Double" | "doubleValueObserver" | SUMMARY | 123.456
false | "multiple" | "Double" | "doubleValueObserver" | SUMMARY | 123.456
true | "single" | "Long" | "longValueObserver" | SUMMARY | 234
false | "multiple" | "Long" | "longValueObserver" | SUMMARY | 234
}
}

View File

@ -0,0 +1,896 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.contrib.jmxmetrics
import static io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type.MONOTONIC_DOUBLE
import static io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type.MONOTONIC_LONG
import static io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type.NON_MONOTONIC_DOUBLE
import static io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type.NON_MONOTONIC_LONG
import static io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type.SUMMARY
import io.opentelemetry.common.Labels
import io.opentelemetry.sdk.OpenTelemetrySdk
import org.junit.Rule
import org.junit.rules.TestName
import org.junit.rules.TestRule
import spock.lang.Shared
import spock.lang.Specification
class OtelHelperAsynchronousMetricTest extends Specification{
@Shared
GroovyMetricEnvironment gme
@Shared
OtelHelper otel
@Rule public final TestRule name = new TestName()
def setup() {
// Set up a MeterSdk per test to be able to collect its metrics alone
gme = new GroovyMetricEnvironment(
new JmxConfig(new Properties().tap {
it.setProperty(JmxConfig.EXPORTER_TYPE, 'inmemory')
it.setProperty(JmxConfig.INTERVAL_MILLISECONDS, '100')
}),
name.methodName, ''
)
otel = new OtelHelper(null, gme)
}
def exportMetrics() {
def provider = OpenTelemetrySdk.meterProvider.get(name.methodName, '')
return provider.collectAll().sort { md1, md2 ->
def p1 = md1.points[0]
def p2 = md2.points[0]
def s1 = p1.startEpochNanos
def s2 = p2.startEpochNanos
if (s1 == s2) {
if (md1.descriptor.type == SUMMARY) {
return p1.percentileValues[0].value <=> p2.percentileValues[0].value
}
return p1.value <=> p2.value
}
s1 <=> s2
}
}
def "double sum observer"() {
when:
def dso = otel.doubleSumObserver(
'double-sum', 'a double sum',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({doubleResult ->
doubleResult.observe(123.456, Labels.of('key', 'value'))
})
dso = otel.doubleSumObserver('my-double-sum', 'another double sum', 'µs')
dso.setCallback({ doubleResult ->
doubleResult.observe(234.567, Labels.of('myKey', 'myValue'))
} )
dso = otel.doubleSumObserver('another-double-sum', 'double sum')
dso.setCallback({ doubleResult ->
doubleResult.observe(345.678, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.doubleSumObserver('yet-another-double-sum')
dso.setCallback({ doubleResult ->
doubleResult.observe(456.789, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'double-sum'
assert first.descriptor.description == 'a double sum'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == MONOTONIC_DOUBLE
assert first.points.size() == 1
assert first.points[0].value == 123.456
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-double-sum'
assert second.descriptor.description == 'another double sum'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == MONOTONIC_DOUBLE
assert second.points.size() == 1
assert second.points[0].value == 234.567
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-double-sum'
assert third.descriptor.description == 'double sum'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == MONOTONIC_DOUBLE
assert third.points.size() == 1
assert third.points[0].value == 345.678
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-double-sum'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == MONOTONIC_DOUBLE
assert fourth.points.size() == 1
assert fourth.points[0].value == 456.789
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "double sum observer memoization"() {
when:
def dcOne = otel.doubleSumObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(10.1, Labels.of('key1', 'value1'))
})
def dcTwo = otel.doubleSumObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(20.2, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.doubleSumObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(30.3, Labels.of('key3', 'value3'))
})
def dcFour = otel.doubleSumObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(40.4, Labels.of('key4', 'value4'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'double'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == MONOTONIC_DOUBLE
assert firstMetric.points.size() == 1
assert firstMetric.points[0].value == 20.2
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'double'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == MONOTONIC_DOUBLE
assert secondMetric.points.size() == 2
assert secondMetric.points[0].value == 20.2
assert secondMetric.points[0].labels == Labels.of('key2', 'value2')
assert secondMetric.points[1].value == 40.4
assert secondMetric.points[1].labels == Labels.of('key4', 'value4')
}
def "long sum observer"() {
when:
def dso = otel.longSumObserver(
'long-sum', 'a long sum',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({longResult ->
longResult.observe(123, Labels.of('key', 'value'))
})
dso = otel.longSumObserver('my-long-sum', 'another long sum', 'µs')
dso.setCallback({ longResult ->
longResult.observe(234, Labels.of('myKey', 'myValue'))
} )
dso = otel.longSumObserver('another-long-sum', 'long sum')
dso.setCallback({ longResult ->
longResult.observe(345, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.longSumObserver('yet-another-long-sum')
dso.setCallback({ longResult ->
longResult.observe(456, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'long-sum'
assert first.descriptor.description == 'a long sum'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == MONOTONIC_LONG
assert first.points.size() == 1
assert first.points[0].value == 123
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-long-sum'
assert second.descriptor.description == 'another long sum'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == MONOTONIC_LONG
assert second.points.size() == 1
assert second.points[0].value == 234
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-long-sum'
assert third.descriptor.description == 'long sum'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == MONOTONIC_LONG
assert third.points.size() == 1
assert third.points[0].value == 345
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-long-sum'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == MONOTONIC_LONG
assert fourth.points.size() == 1
assert fourth.points[0].value == 456
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "long sum observer memoization"() {
when:
def dcOne = otel.longSumObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(10, Labels.of('key1', 'value1'))
})
def dcTwo = otel.longSumObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(20, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.longSumObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(30, Labels.of('key3', 'value3'))
})
def dcFour = otel.longSumObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(40, Labels.of('key4', 'value4'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'long'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == MONOTONIC_LONG
assert firstMetric.points.size() == 1
assert firstMetric.points[0].value == 20
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'long'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == MONOTONIC_LONG
assert secondMetric.points.size() == 2
assert secondMetric.points[0].value == 20
assert secondMetric.points[0].labels == Labels.of('key2', 'value2')
assert secondMetric.points[1].value == 40
assert secondMetric.points[1].labels == Labels.of('key4', 'value4')
}
def "double up down sum observer"() {
when:
def dso = otel.doubleUpDownSumObserver(
'double-up-down-sum', 'a double up down sum',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({doubleResult ->
doubleResult.observe(123.456, Labels.of('key', 'value'))
})
dso = otel.doubleUpDownSumObserver('my-double-up-down-sum', 'another double up down sum', 'µs')
dso.setCallback({ doubleResult ->
doubleResult.observe(234.567, Labels.of('myKey', 'myValue'))
} )
dso = otel.doubleUpDownSumObserver('another-double-up-down-sum', 'double up down sum')
dso.setCallback({ doubleResult ->
doubleResult.observe(345.678, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.doubleUpDownSumObserver('yet-another-double-up-down-sum')
dso.setCallback({ doubleResult ->
doubleResult.observe(456.789, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'double-up-down-sum'
assert first.descriptor.description == 'a double up down sum'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == NON_MONOTONIC_DOUBLE
assert first.points.size() == 1
assert first.points[0].value == 123.456
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-double-up-down-sum'
assert second.descriptor.description == 'another double up down sum'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == NON_MONOTONIC_DOUBLE
assert second.points.size() == 1
assert second.points[0].value == 234.567
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-double-up-down-sum'
assert third.descriptor.description == 'double up down sum'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == NON_MONOTONIC_DOUBLE
assert third.points.size() == 1
assert third.points[0].value == 345.678
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-double-up-down-sum'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == NON_MONOTONIC_DOUBLE
assert fourth.points.size() == 1
assert fourth.points[0].value == 456.789
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "double up down sum observer memoization"() {
when:
def dcOne = otel.doubleUpDownSumObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(10.1, Labels.of('key1', 'value1'))
})
def dcTwo = otel.doubleUpDownSumObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(20.2, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.doubleUpDownSumObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(30.3, Labels.of('key3', 'value3'))
})
def dcFour = otel.doubleUpDownSumObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(40.4, Labels.of('key4', 'value4'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'double'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == NON_MONOTONIC_DOUBLE
assert firstMetric.points.size() == 1
assert firstMetric.points[0].value == 20.2
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'double'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == NON_MONOTONIC_DOUBLE
assert secondMetric.points.size() == 2
assert secondMetric.points[0].value == 20.2
assert secondMetric.points[0].labels == Labels.of('key2', 'value2')
assert secondMetric.points[1].value == 40.4
assert secondMetric.points[1].labels == Labels.of('key4', 'value4')
}
def "long up down sum observer"() {
when:
def dso = otel.longUpDownSumObserver(
'long-up-down-sum', 'a long up down sum',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({longResult ->
longResult.observe(123, Labels.of('key', 'value'))
})
dso = otel.longUpDownSumObserver('my-long-up-down-sum', 'another long up down sum', 'µs')
dso.setCallback({ longResult ->
longResult.observe(234, Labels.of('myKey', 'myValue'))
} )
dso = otel.longUpDownSumObserver('another-long-up-down-sum', 'long up down sum')
dso.setCallback({ longResult ->
longResult.observe(345, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.longUpDownSumObserver('yet-another-long-up-down-sum')
dso.setCallback({ longResult ->
longResult.observe(456, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'long-up-down-sum'
assert first.descriptor.description == 'a long up down sum'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == NON_MONOTONIC_LONG
assert first.points.size() == 1
assert first.points[0].value == 123
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-long-up-down-sum'
assert second.descriptor.description == 'another long up down sum'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == NON_MONOTONIC_LONG
assert second.points.size() == 1
assert second.points[0].value == 234
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-long-up-down-sum'
assert third.descriptor.description == 'long up down sum'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == NON_MONOTONIC_LONG
assert third.points.size() == 1
assert third.points[0].value == 345
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-long-up-down-sum'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == NON_MONOTONIC_LONG
assert fourth.points.size() == 1
assert fourth.points[0].value == 456
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "long up down sum observer memoization"() {
when:
def dcOne = otel.longUpDownSumObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(10, Labels.of('key1', 'value1'))
})
def dcTwo = otel.longUpDownSumObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(20, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.longUpDownSumObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(30, Labels.of('key3', 'value3'))
})
def dcFour = otel.longUpDownSumObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(40, Labels.of('key4', 'value4'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'long'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == NON_MONOTONIC_LONG
assert firstMetric.points.size() == 1
assert firstMetric.points[0].value == 20
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'long'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == NON_MONOTONIC_LONG
assert secondMetric.points.size() == 2
assert secondMetric.points[0].value == 20
assert secondMetric.points[0].labels == Labels.of('key2', 'value2')
assert secondMetric.points[1].value == 40
assert secondMetric.points[1].labels == Labels.of('key4', 'value4')
}
def "double value observer"() {
when:
def dso = otel.doubleValueObserver(
'double-value', 'a double value',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({doubleResult ->
doubleResult.observe(123.456, Labels.of('key', 'value'))
})
dso = otel.doubleValueObserver('my-double-value', 'another double value', 'µs')
dso.setCallback({ doubleResult ->
doubleResult.observe(234.567, Labels.of('myKey', 'myValue'))
} )
dso = otel.doubleValueObserver('another-double-value', 'double value')
dso.setCallback({ doubleResult ->
doubleResult.observe(345.678, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.doubleValueObserver('yet-another-double-value')
dso.setCallback({ doubleResult ->
doubleResult.observe(456.789, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'double-value'
assert first.descriptor.description == 'a double value'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == SUMMARY
assert first.points.size() == 1
assert first.points[0].count == 1
assert first.points[0].sum == 123.456
assert first.points[0].percentileValues[0].percentile == 0
assert first.points[0].percentileValues[0].value == 123.456
assert first.points[0].percentileValues[1].percentile == 100
assert first.points[0].percentileValues[1].value == 123.456
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-double-value'
assert second.descriptor.description == 'another double value'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == SUMMARY
assert second.points[0].count == 1
assert second.points[0].sum == 234.567
assert second.points[0].percentileValues[0].percentile == 0
assert second.points[0].percentileValues[0].value == 234.567
assert second.points[0].percentileValues[1].percentile == 100
assert second.points[0].percentileValues[1].value == 234.567
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-double-value'
assert third.descriptor.description == 'double value'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == SUMMARY
assert third.points.size() == 1
assert third.points[0].count == 1
assert third.points[0].sum == 345.678
assert third.points[0].percentileValues[0].percentile == 0
assert third.points[0].percentileValues[0].value == 345.678
assert third.points[0].percentileValues[1].percentile == 100
assert third.points[0].percentileValues[1].value == 345.678
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-double-value'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == SUMMARY
assert fourth.points.size() == 1
assert fourth.points[0].count == 1
assert fourth.points[0].sum == 456.789
assert fourth.points[0].percentileValues[0].percentile == 0
assert fourth.points[0].percentileValues[0].value == 456.789
assert fourth.points[0].percentileValues[1].percentile == 100
assert fourth.points[0].percentileValues[1].value == 456.789
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "double value observer memoization"() {
when:
def dcOne = otel.doubleValueObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(10.1, Labels.of('key1', 'value1'))
})
def dcTwo = otel.doubleValueObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(20.2, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.doubleValueObserver('dc', 'double')
dcOne.setCallback({ doubleResult ->
doubleResult.observe(30.3, Labels.of('key3', 'value3'))
})
def dcFour = otel.doubleValueObserver('dc', 'double')
dcTwo.setCallback({ doubleResult ->
doubleResult.observe(40.4, Labels.of('key4', 'value4'))
doubleResult.observe(50.5, Labels.of('key2', 'value2'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'double'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == SUMMARY
assert firstMetric.points.size() == 1
assert firstMetric.points[0].count == 1
assert firstMetric.points[0].sum == 20.2
assert firstMetric.points[0].percentileValues[0].percentile == 0
assert firstMetric.points[0].percentileValues[0].value == 20.2
assert firstMetric.points[0].percentileValues[1].percentile == 100
assert firstMetric.points[0].percentileValues[1].value == 20.2
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'double'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == SUMMARY
assert secondMetric.points.size() == 2
assert secondMetric.points[0].count == 1
assert secondMetric.points[0].sum == 40.4
assert secondMetric.points[0].percentileValues[0].percentile == 0
assert secondMetric.points[0].percentileValues[0].value == 40.4
assert secondMetric.points[0].percentileValues[1].percentile == 100
assert secondMetric.points[0].percentileValues[1].value == 40.4
assert secondMetric.points[0].labels == Labels.of('key4', 'value4')
assert secondMetric.points[1].count == 1
assert secondMetric.points[1].sum == 50.5
assert secondMetric.points[1].percentileValues[0].percentile == 0
assert secondMetric.points[1].percentileValues[0].value == 50.5
assert secondMetric.points[1].percentileValues[1].percentile == 100
assert secondMetric.points[1].percentileValues[1].value == 50.5
assert secondMetric.points[1].labels == Labels.of('key2', 'value2')
}
def "long value observer"() {
when:
def dso = otel.longValueObserver(
'long-value', 'a long value',
'ms', [key1:'value1', key2:'value2']
)
dso.setCallback({longResult ->
longResult.observe(123, Labels.of('key', 'value'))
})
dso = otel.longValueObserver('my-long-value', 'another long value', 'µs')
dso.setCallback({ longResult ->
longResult.observe(234, Labels.of('myKey', 'myValue'))
} )
dso = otel.longValueObserver('another-long-value', 'long value')
dso.setCallback({ longResult ->
longResult.observe(345, Labels.of('anotherKey', 'anotherValue'))
})
dso = otel.longValueObserver('yet-another-long-value')
dso.setCallback({ longResult ->
longResult.observe(456, Labels.of('yetAnotherKey', 'yetAnotherValue'))
})
def metrics = exportMetrics()
then:
assert metrics.size() == 4
def first = metrics[0]
def second = metrics[1]
def third = metrics[2]
def fourth = metrics[3]
assert first.descriptor.name == 'long-value'
assert first.descriptor.description == 'a long value'
assert first.descriptor.unit == 'ms'
assert first.descriptor.constantLabels == Labels.of(
'key1', 'value1', 'key2', 'value2'
)
assert first.descriptor.type == SUMMARY
assert first.points.size() == 1
assert first.points[0].count == 1
assert first.points[0].sum == 123
assert first.points[0].percentileValues[0].percentile == 0
assert first.points[0].percentileValues[0].value == 123
assert first.points[0].percentileValues[1].percentile == 100
assert first.points[0].percentileValues[1].value == 123
assert first.points[0].labels == Labels.of('key', 'value')
assert second.descriptor.name == 'my-long-value'
assert second.descriptor.description == 'another long value'
assert second.descriptor.unit == 'µs'
assert second.descriptor.constantLabels == Labels.empty()
assert second.descriptor.type == SUMMARY
assert second.points.size() == 1
assert second.points[0].count == 1
assert second.points[0].sum == 234
assert second.points[0].percentileValues[0].percentile == 0
assert second.points[0].percentileValues[0].value == 234
assert second.points[0].percentileValues[1].percentile == 100
assert second.points[0].percentileValues[1].value == 234
assert second.points[0].labels == Labels.of('myKey', 'myValue')
assert third.descriptor.name == 'another-long-value'
assert third.descriptor.description == 'long value'
assert third.descriptor.unit == '1'
assert third.descriptor.constantLabels == Labels.empty()
assert third.descriptor.type == SUMMARY
assert third.points.size() == 1
assert third.points[0].count == 1
assert third.points[0].sum == 345
assert third.points[0].percentileValues[0].percentile == 0
assert third.points[0].percentileValues[0].value == 345
assert third.points[0].percentileValues[1].percentile == 100
assert third.points[0].percentileValues[1].value == 345
assert third.points[0].labels == Labels.of('anotherKey', 'anotherValue')
assert fourth.descriptor.name == 'yet-another-long-value'
assert fourth.descriptor.description == ''
assert fourth.descriptor.unit == '1'
assert fourth.descriptor.constantLabels == Labels.empty()
assert fourth.descriptor.type == SUMMARY
assert fourth.points.size() == 1
assert fourth.points[0].count == 1
assert fourth.points[0].sum == 456
assert fourth.points[0].percentileValues[0].percentile == 0
assert fourth.points[0].percentileValues[0].value == 456
assert fourth.points[0].percentileValues[1].percentile == 100
assert fourth.points[0].percentileValues[1].value == 456
assert fourth.points[0].labels == Labels.of('yetAnotherKey', 'yetAnotherValue')
}
def "long value observer memoization"() {
when:
def dcOne = otel.longValueObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(10, Labels.of('key1', 'value1'))
})
def dcTwo = otel.longValueObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(20, Labels.of('key2', 'value2'))
})
def firstMetrics = exportMetrics()
def dcThree = otel.longValueObserver('dc', 'long')
dcOne.setCallback({ longResult ->
longResult.observe(30, Labels.of('key3', 'value3'))
})
def dcFour = otel.longValueObserver('dc', 'long')
dcTwo.setCallback({ longResult ->
longResult.observe(40, Labels.of('key4', 'value4'))
longResult.observe(50, Labels.of('key2', 'value2'))
})
def secondMetrics = exportMetrics()
then:
assert dcOne.is(dcTwo)
assert dcTwo.is(dcThree)
assert dcTwo.is(dcFour)
assert firstMetrics.size() == 1
assert secondMetrics.size() == 1
def firstMetric = firstMetrics[0]
assert firstMetric.descriptor.name == 'dc'
assert firstMetric.descriptor.description == 'long'
assert firstMetric.descriptor.unit == '1'
assert firstMetric.descriptor.constantLabels == Labels.empty()
assert firstMetric.descriptor.type == SUMMARY
assert firstMetric.points.size() == 1
assert firstMetric.points[0].sum == 20
assert firstMetric.points[0].percentileValues[0].percentile == 0
assert firstMetric.points[0].percentileValues[0].value == 20
assert firstMetric.points[0].percentileValues[1].percentile == 100
assert firstMetric.points[0].percentileValues[1].value == 20
assert firstMetric.points[0].labels == Labels.of('key2', 'value2')
def secondMetric = secondMetrics[0]
assert secondMetric.descriptor.name == 'dc'
assert secondMetric.descriptor.description == 'long'
assert secondMetric.descriptor.unit == '1'
assert secondMetric.descriptor.constantLabels == Labels.empty()
assert secondMetric.descriptor.type == SUMMARY
assert secondMetric.points.size() == 2
assert secondMetric.points[0].sum == 40
assert secondMetric.points[0].percentileValues[0].percentile == 0
assert secondMetric.points[0].percentileValues[0].value == 40
assert secondMetric.points[0].percentileValues[1].percentile == 100
assert secondMetric.points[0].percentileValues[1].value == 40
assert secondMetric.points[0].labels == Labels.of('key4', 'value4')
assert secondMetric.points[1].sum == 50
assert secondMetric.points[1].percentileValues[0].percentile == 0
assert secondMetric.points[1].percentileValues[0].value == 50
assert secondMetric.points[1].percentileValues[1].percentile == 100
assert secondMetric.points[1].percentileValues[1].value == 50
assert secondMetric.points[1].labels == Labels.of('key2', 'value2')
}
}