Stabilize synchronous gauge (#6419)

This commit is contained in:
jack-berg 2024-05-09 13:10:53 -05:00 committed by GitHub
parent ca798212a2
commit c7d472ad36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 346 additions and 136 deletions

View File

@ -320,8 +320,10 @@ class DefaultMeter implements Meter {
}
private static class NoopDoubleGaugeBuilder implements DoubleGaugeBuilder {
private static final ObservableDoubleGauge NOOP = new ObservableDoubleGauge() {};
private static final ObservableDoubleGauge NOOP_OBSERVABLE_GAUGE =
new ObservableDoubleGauge() {};
private static final LongGaugeBuilder NOOP_LONG_GAUGE_BUILDER = new NoopLongGaugeBuilder();
private static final NoopDoubleGauge NOOP_GAUGE = new NoopDoubleGauge();
@Override
public DoubleGaugeBuilder setDescription(String description) {
@ -340,17 +342,34 @@ class DefaultMeter implements Meter {
@Override
public ObservableDoubleGauge buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
return NOOP;
return NOOP_OBSERVABLE_GAUGE;
}
@Override
public ObservableDoubleMeasurement buildObserver() {
return NOOP_OBSERVABLE_DOUBLE_MEASUREMENT;
}
@Override
public DoubleGauge build() {
return NOOP_GAUGE;
}
}
private static class NoopDoubleGauge implements DoubleGauge {
@Override
public void set(double value) {}
@Override
public void set(double value, Attributes attributes) {}
@Override
public void set(double value, Attributes attributes, Context context) {}
}
private static class NoopLongGaugeBuilder implements LongGaugeBuilder {
private static final ObservableLongGauge NOOP = new ObservableLongGauge() {};
private static final ObservableLongGauge NOOP_OBSERVABLE_GAUGE = new ObservableLongGauge() {};
private static final NoopLongGauge NOOP_GAUGE = new NoopLongGauge();
@Override
public LongGaugeBuilder setDescription(String description) {
@ -364,13 +383,29 @@ class DefaultMeter implements Meter {
@Override
public ObservableLongGauge buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
return NOOP;
return NOOP_OBSERVABLE_GAUGE;
}
@Override
public ObservableLongMeasurement buildObserver() {
return NOOP_OBSERVABLE_LONG_MEASUREMENT;
}
@Override
public LongGauge build() {
return NOOP_GAUGE;
}
}
private static class NoopLongGauge implements LongGauge {
@Override
public void set(long value) {}
@Override
public void set(long value, Attributes attributes) {}
@Override
public void set(long value, Attributes attributes, Context context) {}
}
private static class NoopObservableDoubleMeasurement implements ObservableDoubleMeasurement {

View File

@ -3,9 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
package io.opentelemetry.api.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import javax.annotation.concurrent.ThreadSafe;
/** A gauge instrument that synchronously records {@code double} values. */
@ -26,5 +27,12 @@ public interface DoubleGauge {
*/
void set(double value, Attributes attributes);
// TODO(jack-berg): should we add overload with Context argument?
/**
* Records a value with a set of attributes.
*
* @param value The current gauge value.
* @param attributes A set of attributes to associate with the value.
* @param context The explicit context to associate with this measurement.
*/
void set(double value, Attributes attributes, Context context);
}

View File

@ -65,4 +65,20 @@ public interface DoubleGaugeBuilder {
default ObservableDoubleMeasurement buildObserver() {
return DefaultMeter.getInstance().gaugeBuilder("noop").buildObserver();
}
/**
* Builds and returns a DoubleGauge instrument with the configuration.
*
* <p>NOTE: This produces a synchronous gauge which records gauge values as they occur. Most users
* will want to instead register an {@link #buildWithCallback(Consumer)} to asynchronously observe
* the value of the gauge when metrics are collected.
*
* <p>If using the OpenTelemetry SDK, by default gauges use last value aggregation, such that only
* the value of the last recorded measurement is exported.
*
* @return The DoubleGauge instrument.
*/
default DoubleGauge build() {
return DefaultMeter.getInstance().gaugeBuilder("noop").build();
}
}

View File

@ -3,9 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
package io.opentelemetry.api.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import javax.annotation.concurrent.ThreadSafe;
/** A gauge instrument that synchronously records {@code long} values. */
@ -26,5 +27,12 @@ public interface LongGauge {
*/
void set(long value, Attributes attributes);
// TODO(jack-berg): should we add overload with Context argument?
/**
* Records a value with a set of attributes.
*
* @param value The current gauge value.
* @param attributes A set of attributes to associate with the value.
* @param context The explicit context to associate with this measurement.
*/
void set(long value, Attributes attributes, Context context);
}

View File

@ -62,4 +62,20 @@ public interface LongGaugeBuilder {
default ObservableLongMeasurement buildObserver() {
return DefaultMeter.getInstance().gaugeBuilder("noop").ofLongs().buildObserver();
}
/**
* Builds and returns a LongGauge instrument with the configuration.
*
* <p>NOTE: This produces a synchronous gauge which records gauge values as they occur. Most users
* will want to instead register an {@link #buildWithCallback(Consumer)} to asynchronously observe
* the value of the gauge when metrics are collected.
*
* <p>If using the OpenTelemetry SDK, by default gauges use last value aggregation, such that only
* the value of the last recorded measurement is exported.
*
* @return The LongGauge instrument.
*/
default LongGauge build() {
return DefaultMeter.getInstance().gaugeBuilder("noop").ofLongs().build();
}
}

View File

@ -93,6 +93,20 @@ public class DefaultMeterTest {
histogram.record(1.0e-1, Attributes.of(stringKey("thing"), "car"), Context.current());
}
@Test
void noopLongGauage_doesNotThrow() {
LongGauge gauge =
METER
.gaugeBuilder("temperature")
.ofLongs()
.setDescription("The current temperature")
.setUnit("C")
.build();
gauge.set(1);
gauge.set(2, Attributes.of(stringKey("thing"), "engine"));
gauge.set(2, Attributes.of(stringKey("thing"), "engine"), Context.current());
}
@Test
void noopObservableLongGauage_doesNotThrow() {
METER
@ -107,6 +121,19 @@ public class DefaultMeterTest {
});
}
@Test
void noopDoubleGauage_doesNotThrow() {
DoubleGauge gauge =
METER
.gaugeBuilder("temperature")
.setDescription("The current temperature")
.setUnit("C")
.build();
gauge.set(1);
gauge.set(2, Attributes.of(stringKey("thing"), "engine"));
gauge.set(2, Attributes.of(stringKey("thing"), "engine"), Context.current());
}
@Test
void noopObservableDoubleGauage_doesNotThrow() {
METER

View File

@ -8,22 +8,10 @@ package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
import java.util.List;
import java.util.function.Consumer;
/** Extended {@link DoubleGaugeBuilder} with experimental APIs. */
public interface ExtendedDoubleGaugeBuilder extends DoubleGaugeBuilder {
/**
* Builds and returns a DoubleGauge instrument with the configuration.
*
* <p>NOTE: This produces a synchronous gauge which records gauge values as they occur. Most users
* will want to instead register an {@link #buildWithCallback(Consumer)} to asynchronously observe
* the value of the gauge when metrics are collected.
*
* @return The DoubleGauge instrument.
*/
DoubleGauge build();
/**
* Specify the attribute advice, which suggests the recommended set of attribute keys to be used
* for this gauge.

View File

@ -8,22 +8,10 @@ package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.metrics.LongGaugeBuilder;
import java.util.List;
import java.util.function.Consumer;
/** Extended {@link LongGaugeBuilder} with experimental APIs. */
public interface ExtendedLongGaugeBuilder extends LongGaugeBuilder {
/**
* Builds and returns a LongGauge instrument with the configuration.
*
* <p>NOTE: This produces a synchronous gauge which records gauge values as they occur. Most users
* will want to instead register an {@link #buildWithCallback(Consumer)} to asynchronously observe
* the value of the gauge when metrics are collected.
*
* @return The LongGauge instrument.
*/
LongGauge build();
/**
* Specify the attribute advice, which suggests the recommended set of attribute keys to be used
* for this gauge.

View File

@ -23,48 +23,6 @@ import org.junit.jupiter.api.Test;
/** Demonstrating usage of extended Metrics API. */
class ExtendedMetricsApiUsageTest {
@Test
void synchronousGaugeUsage() {
// Setup SdkMeterProvider
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider meterProvider =
SdkMeterProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory reader used for demonstration purposes
.registerMetricReader(reader)
.build();
// Get a Meter for a scope
Meter meter = meterProvider.get("org.foo.my-scope");
// Cast GaugeBuilder to ExtendedDoubleGaugeBuilder
DoubleGauge gauge = ((ExtendedDoubleGaugeBuilder) meter.gaugeBuilder("my-gauge")).build();
// Call set synchronously to set the value
gauge.set(1.0, Attributes.builder().put("key", "value1").build());
gauge.set(2.0, Attributes.builder().put("key", "value2").build());
assertThat(reader.collectAllMetrics())
.satisfiesExactly(
metricData ->
assertThat(metricData)
.hasName("my-gauge")
.hasDoubleGaugeSatisfying(
gaugeAssert ->
gaugeAssert.hasPointsSatisfying(
point ->
point
.hasValue(1.0)
.hasAttributes(
Attributes.builder().put("key", "value1").build()),
point ->
point
.hasValue(2.0)
.hasAttributes(
Attributes.builder().put("key", "value2").build()))));
}
@Test
void attributesAdvice() {
// Setup SdkMeterProvider

View File

@ -1,2 +1,19 @@
Comparing source compatibility of against
No changes.
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.metrics.DoubleGauge (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(double)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(double, io.opentelemetry.api.common.Attributes)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(double, io.opentelemetry.api.common.Attributes, io.opentelemetry.context.Context)
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.metrics.DoubleGaugeBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.metrics.DoubleGauge build()
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.api.metrics.LongGauge (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(long)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(long, io.opentelemetry.api.common.Attributes)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) void set(long, io.opentelemetry.api.common.Attributes, io.opentelemetry.context.Context)
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.metrics.LongGaugeBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.metrics.LongGauge build()

View File

@ -5,3 +5,6 @@ Comparing source compatibility of against
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) java.lang.String asString(io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector)
*** MODIFIED ENUM: PUBLIC FINAL io.opentelemetry.sdk.metrics.InstrumentType (compatible)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.metrics.InstrumentType GAUGE

View File

@ -17,4 +17,5 @@ public enum InstrumentType {
OBSERVABLE_COUNTER,
OBSERVABLE_UP_DOWN_COUNTER,
OBSERVABLE_GAUGE,
GAUGE,
}

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.DoubleGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGaugeBuilder;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
import io.opentelemetry.api.metrics.LongGaugeBuilder;
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
@ -31,8 +31,13 @@ final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
}
@Override
public void set(double increment, Attributes attributes) {
storage.recordDouble(increment, attributes, Context.root());
public void set(double value, Attributes attributes) {
storage.recordDouble(value, attributes, Context.current());
}
@Override
public void set(double value, Attributes attributes, Context context) {
storage.recordDouble(value, attributes, context);
}
@Override
@ -48,11 +53,10 @@ final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
MeterSharedState meterSharedState,
String name) {
// TODO: use InstrumentType.GAUGE when available
builder =
new InstrumentBuilder(
name,
InstrumentType.OBSERVABLE_GAUGE,
InstrumentType.GAUGE,
InstrumentValueType.DOUBLE,
meterProviderSharedState,
meterSharedState);
@ -88,13 +92,11 @@ final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
@Override
public ObservableDoubleGauge buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
// TODO: use InstrumentType.GAUGE when available
return builder.buildDoubleAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
}
@Override
public ObservableDoubleMeasurement buildObserver() {
// TODO: use InstrumentType.GAUGE when available
return builder.buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
}

View File

@ -8,7 +8,7 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongGaugeBuilder;
import io.opentelemetry.api.incubator.metrics.LongGauge;
import io.opentelemetry.api.metrics.LongGauge;
import io.opentelemetry.api.metrics.LongGaugeBuilder;
import io.opentelemetry.api.metrics.ObservableLongGauge;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
@ -31,8 +31,13 @@ final class SdkLongGauge extends AbstractInstrument implements LongGauge {
}
@Override
public void set(long increment, Attributes attributes) {
storage.recordLong(increment, attributes, Context.root());
public void set(long value, Attributes attributes) {
storage.recordLong(value, attributes, Context.current());
}
@Override
public void set(long value, Attributes attributes, Context context) {
storage.recordLong(value, attributes, context);
}
@Override
@ -52,11 +57,10 @@ final class SdkLongGauge extends AbstractInstrument implements LongGauge {
String unit,
Advice.AdviceBuilder adviceBuilder) {
// TODO: use InstrumentType.GAUGE when available
builder =
new InstrumentBuilder(
name,
InstrumentType.OBSERVABLE_GAUGE,
InstrumentType.GAUGE,
InstrumentValueType.LONG,
meterProviderSharedState,
sharedState)
@ -90,13 +94,11 @@ final class SdkLongGauge extends AbstractInstrument implements LongGauge {
@Override
public ObservableLongGauge buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
// TODO: use InstrumentType.GAUGE when available
return builder.buildLongAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
}
@Override
public ObservableLongMeasurement buildObserver() {
// TODO: use InstrumentType.GAUGE when available
return builder.buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
}

View File

@ -50,6 +50,7 @@ public final class DefaultAggregation implements Aggregation, AggregatorFactory
}
return ExplicitBucketHistogramAggregation.getDefault();
case OBSERVABLE_GAUGE:
case GAUGE:
return LastValueAggregation.getInstance();
}
logger.log(Level.WARNING, "Unable to find default aggregation for instrument: " + instrument);

View File

@ -5,10 +5,14 @@
package io.opentelemetry.sdk.metrics.internal.view;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.internal.RandomSupplier;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.DoubleExemplarData;
import io.opentelemetry.sdk.metrics.data.ExemplarData;
import io.opentelemetry.sdk.metrics.data.LongExemplarData;
import io.opentelemetry.sdk.metrics.data.PointData;
import io.opentelemetry.sdk.metrics.internal.aggregator.Aggregator;
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorFactory;
@ -17,6 +21,7 @@ import io.opentelemetry.sdk.metrics.internal.aggregator.LongLastValueAggregator;
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir;
import java.util.function.Supplier;
/**
* Last-value aggregation configuration.
@ -44,18 +49,38 @@ public final class LastValueAggregation implements Aggregation, AggregatorFactor
// For the initial version we do not sample exemplars on gauges.
switch (instrumentDescriptor.getValueType()) {
case LONG:
return (Aggregator<T, U>)
new LongLastValueAggregator(ExemplarReservoir::longNoSamples, memoryMode);
{
Supplier<ExemplarReservoir<LongExemplarData>> reservoirFactory =
() ->
ExemplarReservoir.filtered(
exemplarFilter,
ExemplarReservoir.longFixedSizeReservoir(
Clock.getDefault(),
Runtime.getRuntime().availableProcessors(),
RandomSupplier.platformDefault()));
return (Aggregator<T, U>) new LongLastValueAggregator(reservoirFactory, memoryMode);
}
case DOUBLE:
return (Aggregator<T, U>)
new DoubleLastValueAggregator(ExemplarReservoir::doubleNoSamples, memoryMode);
{
Supplier<ExemplarReservoir<DoubleExemplarData>> reservoirFactory =
() ->
ExemplarReservoir.filtered(
exemplarFilter,
ExemplarReservoir.doubleFixedSizeReservoir(
Clock.getDefault(),
Runtime.getRuntime().availableProcessors(),
RandomSupplier.platformDefault()));
return (Aggregator<T, U>) new DoubleLastValueAggregator(reservoirFactory, memoryMode);
}
}
throw new IllegalArgumentException("Invalid instrument value type");
}
@Override
public boolean isCompatibleWithInstrument(InstrumentDescriptor instrumentDescriptor) {
return instrumentDescriptor.getType() == InstrumentType.OBSERVABLE_GAUGE;
InstrumentType instrumentType = instrumentDescriptor.getType();
return instrumentType == InstrumentType.OBSERVABLE_GAUGE
|| instrumentType == InstrumentType.GAUGE;
}
@Override

View File

@ -51,11 +51,13 @@ class AggregationTest {
InstrumentDescriptor observableUpDownCounter =
descriptorForType(InstrumentType.OBSERVABLE_UP_DOWN_COUNTER);
InstrumentDescriptor observableGauge = descriptorForType(InstrumentType.OBSERVABLE_GAUGE);
InstrumentDescriptor gauge = descriptorForType(InstrumentType.GAUGE);
InstrumentDescriptor histogram = descriptorForType(InstrumentType.HISTOGRAM);
AggregatorFactory defaultAggregation = ((AggregatorFactory) Aggregation.defaultAggregation());
assertThat(defaultAggregation.isCompatibleWithInstrument(counter)).isTrue();
assertThat(defaultAggregation.isCompatibleWithInstrument(observableCounter)).isTrue();
assertThat(defaultAggregation.isCompatibleWithInstrument(gauge)).isTrue();
assertThat(defaultAggregation.isCompatibleWithInstrument(upDownCounter)).isTrue();
assertThat(defaultAggregation.isCompatibleWithInstrument(observableUpDownCounter)).isTrue();
assertThat(defaultAggregation.isCompatibleWithInstrument(observableGauge)).isTrue();
@ -64,6 +66,7 @@ class AggregationTest {
AggregatorFactory drop = ((AggregatorFactory) Aggregation.drop());
assertThat(drop.isCompatibleWithInstrument(counter)).isTrue();
assertThat(drop.isCompatibleWithInstrument(observableCounter)).isTrue();
assertThat(drop.isCompatibleWithInstrument(gauge)).isTrue();
assertThat(drop.isCompatibleWithInstrument(upDownCounter)).isTrue();
assertThat(drop.isCompatibleWithInstrument(observableUpDownCounter)).isTrue();
assertThat(drop.isCompatibleWithInstrument(observableGauge)).isTrue();
@ -75,6 +78,7 @@ class AggregationTest {
assertThat(sum.isCompatibleWithInstrument(upDownCounter)).isTrue();
assertThat(sum.isCompatibleWithInstrument(observableUpDownCounter)).isTrue();
assertThat(sum.isCompatibleWithInstrument(observableGauge)).isFalse();
assertThat(sum.isCompatibleWithInstrument(gauge)).isFalse();
assertThat(sum.isCompatibleWithInstrument(histogram)).isTrue();
AggregatorFactory explicitHistogram =
@ -84,6 +88,7 @@ class AggregationTest {
assertThat(explicitHistogram.isCompatibleWithInstrument(upDownCounter)).isFalse();
assertThat(explicitHistogram.isCompatibleWithInstrument(observableUpDownCounter)).isFalse();
assertThat(explicitHistogram.isCompatibleWithInstrument(observableGauge)).isFalse();
assertThat(explicitHistogram.isCompatibleWithInstrument(gauge)).isFalse();
assertThat(explicitHistogram.isCompatibleWithInstrument(histogram)).isTrue();
AggregatorFactory exponentialHistogram =
@ -93,6 +98,7 @@ class AggregationTest {
assertThat(exponentialHistogram.isCompatibleWithInstrument(upDownCounter)).isFalse();
assertThat(exponentialHistogram.isCompatibleWithInstrument(observableUpDownCounter)).isFalse();
assertThat(exponentialHistogram.isCompatibleWithInstrument(observableGauge)).isFalse();
assertThat(exponentialHistogram.isCompatibleWithInstrument(gauge)).isFalse();
assertThat(exponentialHistogram.isCompatibleWithInstrument(histogram)).isTrue();
AggregatorFactory lastValue = ((AggregatorFactory) Aggregation.lastValue());
@ -101,6 +107,7 @@ class AggregationTest {
assertThat(lastValue.isCompatibleWithInstrument(upDownCounter)).isFalse();
assertThat(lastValue.isCompatibleWithInstrument(observableUpDownCounter)).isFalse();
assertThat(lastValue.isCompatibleWithInstrument(observableGauge)).isTrue();
assertThat(lastValue.isCompatibleWithInstrument(gauge)).isTrue();
assertThat(lastValue.isCompatibleWithInstrument(histogram)).isFalse();
}

View File

@ -11,10 +11,12 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attri
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.DoubleGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGaugeBuilder;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.internal.state.DefaultSynchronousMetricStorage;
@ -22,7 +24,9 @@ import io.opentelemetry.sdk.metrics.internal.state.SdkObservableMeasurement;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import io.opentelemetry.sdk.testing.time.TestClock;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import java.time.Duration;
import java.util.Collections;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
@ -47,11 +51,7 @@ class SdkDoubleGaugeTest {
@Test
void set_PreventNullAttributes() {
assertThatThrownBy(
() ->
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge"))
.build()
.set(1.0, null))
assertThatThrownBy(() -> sdkMeter.gaugeBuilder("testGauge").build().set(1.0, null))
.isInstanceOf(NullPointerException.class)
.hasMessage("attributes");
}
@ -59,7 +59,7 @@ class SdkDoubleGaugeTest {
@Test
@SuppressLogger(DefaultSynchronousMetricStorage.class)
void set_NaN() {
DoubleGauge gauge = ((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
DoubleGauge gauge = sdkMeter.gaugeBuilder("testGauge").build();
gauge.set(Double.NaN);
assertThat(cumulativeReader.collectAllMetrics()).hasSize(0);
}
@ -93,16 +93,14 @@ class SdkDoubleGaugeTest {
@Test
void collectMetrics_NoRecords() {
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
sdkMeter.gaugeBuilder("testGauge").build();
assertThat(cumulativeReader.collectAllMetrics()).isEmpty();
}
@Test
void collectMetrics_WithEmptyAttributes() {
DoubleGauge doubleGauge =
((ExtendedDoubleGaugeBuilder)
sdkMeter.gaugeBuilder("testGauge").setDescription("description").setUnit("K"))
.build();
sdkMeter.gaugeBuilder("testGauge").setDescription("description").setUnit("K").build();
testClock.advance(Duration.ofNanos(SECOND_NANOS));
doubleGauge.set(12d, Attributes.empty());
doubleGauge.set(13d);
@ -126,11 +124,59 @@ class SdkDoubleGaugeTest {
.hasValue(13d))));
}
@Test
void collectMetrics_WithExemplars() {
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider sdkMeterProvider =
SdkMeterProvider.builder()
.setClock(testClock)
.setResource(RESOURCE)
.registerView(
InstrumentSelector.builder().setName("*").build(),
View.builder().setAttributeFilter(Collections.emptySet()).build())
.registerMetricReader(reader)
.build();
Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
DoubleGauge doubleGauge =
sdkMeter.gaugeBuilder("testGauge").setDescription("description").setUnit("K").build();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder().build();
Tracer tracer = tracerProvider.get("foo");
Span span = tracer.spanBuilder("span").startSpan();
try (Scope unused = span.makeCurrent()) {
doubleGauge.set(12d, Attributes.builder().put("key", "value").build());
}
assertThat(reader.collectAllMetrics())
.satisfiesExactly(
metric ->
assertThat(metric)
.hasResource(RESOURCE)
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
.hasName("testGauge")
.hasDescription("description")
.hasUnit("K")
.hasDoubleGaugeSatisfying(
gauge ->
gauge.hasPointsSatisfying(
point ->
point
.hasValue(12d)
.hasExemplarsSatisfying(
exemplar ->
exemplar
.hasValue(12d)
.hasFilteredAttributes(
Attributes.builder()
.put("key", "value")
.build())))));
}
@Test
void collectMetrics_WithMultipleCollects() {
long startTime = testClock.now();
DoubleGauge doubleGauge =
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
DoubleGauge doubleGauge = sdkMeter.gaugeBuilder("testGauge").build();
doubleGauge.set(12.1d, Attributes.empty());
doubleGauge.set(123.3d, Attributes.builder().put("K", "V").build());
doubleGauge.set(21.4d, Attributes.empty());
@ -228,8 +274,7 @@ class SdkDoubleGaugeTest {
@Test
void stressTest() {
DoubleGauge doubleGauge =
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
DoubleGauge doubleGauge = sdkMeter.gaugeBuilder("testGauge").build();
StressTestRunner.Builder stressTestBuilder =
StressTestRunner.builder().setCollectionIntervalMs(100);
@ -268,8 +313,7 @@ class SdkDoubleGaugeTest {
void stressTest_WithDifferentLabelSet() {
String[] keys = {"Key_1", "Key_2", "Key_3", "Key_4"};
String[] values = {"Value_1", "Value_2", "Value_3", "Value_4"};
DoubleGauge doubleGauge =
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
DoubleGauge doubleGauge = sdkMeter.gaugeBuilder("testGauge").build();
StressTestRunner.Builder stressTestBuilder =
StressTestRunner.builder().setCollectionIntervalMs(100);

View File

@ -11,15 +11,19 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attri
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongGaugeBuilder;
import io.opentelemetry.api.incubator.metrics.LongGauge;
import io.opentelemetry.api.metrics.LongGauge;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongGauge;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import io.opentelemetry.sdk.testing.time.TestClock;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import java.time.Duration;
import java.util.Collections;
import java.util.stream.IntStream;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@ -45,11 +49,7 @@ class SdkLongGaugeTest {
@Test
void set_PreventNullAttributes() {
assertThatThrownBy(
() ->
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs())
.build()
.set(1, null))
assertThatThrownBy(() -> sdkMeter.gaugeBuilder("testGauge").ofLongs().build().set(1, null))
.isInstanceOf(NullPointerException.class)
.hasMessage("attributes");
}
@ -77,19 +77,18 @@ class SdkLongGaugeTest {
@Test
void collectMetrics_NoRecords() {
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
sdkMeter.gaugeBuilder("testGauge").ofLongs().build();
assertThat(cumulativeReader.collectAllMetrics()).isEmpty();
}
@Test
void collectMetrics_WithEmptyAttributes() {
LongGauge longGauge =
((ExtendedLongGaugeBuilder)
sdkMeter
.gaugeBuilder("testGauge")
.ofLongs()
.setDescription("description")
.setUnit("K"))
sdkMeter
.gaugeBuilder("testGauge")
.ofLongs()
.setDescription("description")
.setUnit("K")
.build();
testClock.advance(Duration.ofNanos(SECOND_NANOS));
longGauge.set(12, Attributes.empty());
@ -114,11 +113,64 @@ class SdkLongGaugeTest {
.hasValue(13))));
}
@Test
void collectMetrics_WithExemplars() {
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider sdkMeterProvider =
SdkMeterProvider.builder()
.setClock(testClock)
.setResource(RESOURCE)
.registerView(
InstrumentSelector.builder().setName("*").build(),
View.builder().setAttributeFilter(Collections.emptySet()).build())
.registerMetricReader(reader)
.build();
Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
LongGauge longGauge =
sdkMeter
.gaugeBuilder("testGauge")
.setDescription("description")
.setUnit("K")
.ofLongs()
.build();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder().build();
Tracer tracer = tracerProvider.get("foo");
Span span = tracer.spanBuilder("span").startSpan();
try (Scope unused = span.makeCurrent()) {
longGauge.set(12L, Attributes.builder().put("key", "value").build());
}
assertThat(reader.collectAllMetrics())
.satisfiesExactly(
metric ->
assertThat(metric)
.hasResource(RESOURCE)
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
.hasName("testGauge")
.hasDescription("description")
.hasUnit("K")
.hasLongGaugeSatisfying(
gauge ->
gauge.hasPointsSatisfying(
point ->
point
.hasValue(12L)
.hasExemplarsSatisfying(
exemplar ->
exemplar
.hasValue(12L)
.hasFilteredAttributes(
Attributes.builder()
.put("key", "value")
.build())))));
}
@Test
void collectMetrics_WithMultipleCollects() {
long startTime = testClock.now();
LongGauge longGauge =
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
LongGauge longGauge = sdkMeter.gaugeBuilder("testGauge").ofLongs().build();
longGauge.set(12, Attributes.empty());
longGauge.set(12, Attributes.builder().put("K", "V").build());
longGauge.set(21, Attributes.empty());
@ -216,8 +268,7 @@ class SdkLongGaugeTest {
@Test
void stressTest() {
LongGauge longGauge =
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
LongGauge longGauge = sdkMeter.gaugeBuilder("testGauge").ofLongs().build();
StressTestRunner.Builder stressTestBuilder =
StressTestRunner.builder().setCollectionIntervalMs(100);
@ -256,8 +307,7 @@ class SdkLongGaugeTest {
void stressTest_WithDifferentLabelSet() {
String[] keys = {"Key_1", "Key_2", "Key_3", "Key_4"};
String[] values = {"Value_1", "Value_2", "Value_3", "Value_4"};
LongGauge longGauge =
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
LongGauge longGauge = sdkMeter.gaugeBuilder("testGauge").ofLongs().build();
StressTestRunner.Builder stressTestBuilder =
StressTestRunner.builder().setCollectionIntervalMs(100);

View File

@ -28,6 +28,8 @@ class AggregationTemporalitySelectorTest {
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.OBSERVABLE_UP_DOWN_COUNTER))
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.GAUGE))
.isEqualTo(AggregationTemporality.CUMULATIVE);
}
@Test
@ -45,6 +47,8 @@ class AggregationTemporalitySelectorTest {
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.OBSERVABLE_UP_DOWN_COUNTER))
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.GAUGE))
.isEqualTo(AggregationTemporality.DELTA);
}
@Test
@ -62,6 +66,8 @@ class AggregationTemporalitySelectorTest {
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.OBSERVABLE_UP_DOWN_COUNTER))
.isEqualTo(AggregationTemporality.CUMULATIVE);
assertThat(selector.getAggregationTemporality(InstrumentType.GAUGE))
.isEqualTo(AggregationTemporality.DELTA);
}
@Test
@ -76,7 +82,8 @@ class AggregationTemporalitySelectorTest {
+ "HISTOGRAM=CUMULATIVE, "
+ "OBSERVABLE_COUNTER=CUMULATIVE, "
+ "OBSERVABLE_UP_DOWN_COUNTER=CUMULATIVE, "
+ "OBSERVABLE_GAUGE=CUMULATIVE"
+ "OBSERVABLE_GAUGE=CUMULATIVE, "
+ "GAUGE=CUMULATIVE"
+ "}");
assertThat(
AggregationTemporalitySelector.asString(
@ -88,7 +95,8 @@ class AggregationTemporalitySelectorTest {
+ "HISTOGRAM=DELTA, "
+ "OBSERVABLE_COUNTER=DELTA, "
+ "OBSERVABLE_UP_DOWN_COUNTER=CUMULATIVE, "
+ "OBSERVABLE_GAUGE=DELTA"
+ "OBSERVABLE_GAUGE=DELTA, "
+ "GAUGE=DELTA"
+ "}");
}
}

View File

@ -45,6 +45,8 @@ class DefaultAggregationSelectorTest {
.isEqualTo(Aggregation.defaultAggregation());
assertThat(selector1.getDefaultAggregation(InstrumentType.OBSERVABLE_GAUGE))
.isEqualTo(Aggregation.defaultAggregation());
assertThat(selector1.getDefaultAggregation(InstrumentType.GAUGE))
.isEqualTo(Aggregation.defaultAggregation());
DefaultAggregationSelector selector2 =
selector1.with(InstrumentType.COUNTER, Aggregation.drop());
@ -60,6 +62,8 @@ class DefaultAggregationSelectorTest {
.isEqualTo(Aggregation.defaultAggregation());
assertThat(selector2.getDefaultAggregation(InstrumentType.OBSERVABLE_GAUGE))
.isEqualTo(Aggregation.defaultAggregation());
assertThat(selector2.getDefaultAggregation(InstrumentType.GAUGE))
.isEqualTo(Aggregation.defaultAggregation());
}
@Test
@ -72,7 +76,8 @@ class DefaultAggregationSelectorTest {
+ "HISTOGRAM=default, "
+ "OBSERVABLE_COUNTER=default, "
+ "OBSERVABLE_UP_DOWN_COUNTER=default, "
+ "OBSERVABLE_GAUGE=default"
+ "OBSERVABLE_GAUGE=default, "
+ "GAUGE=default"
+ "}");
assertThat(
DefaultAggregationSelector.asString(
@ -85,7 +90,8 @@ class DefaultAggregationSelectorTest {
+ "HISTOGRAM=base2_exponential_bucket_histogram, "
+ "OBSERVABLE_COUNTER=default, "
+ "OBSERVABLE_UP_DOWN_COUNTER=default, "
+ "OBSERVABLE_GAUGE=default"
+ "OBSERVABLE_GAUGE=default, "
+ "GAUGE=default"
+ "}");
}
}