Add experimental synchronous gauge (#5506)
This commit is contained in:
parent
43ee51ca4a
commit
a5889a685d
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.extension.incubator.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/** A gauge instrument that synchronously records {@code double} values. */
|
||||
@ThreadSafe
|
||||
public interface DoubleGauge {
|
||||
/**
|
||||
* Set the gauge value.
|
||||
*
|
||||
* @param value The current gauge value.
|
||||
*/
|
||||
void set(double value);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void set(double value, Attributes attributes);
|
||||
|
||||
// TODO(jack-berg): should we add overload with Context argument?
|
||||
}
|
||||
|
|
@ -11,6 +11,17 @@ 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 advice for gauge implementations. */
|
||||
default DoubleGaugeBuilder setAdvice(Consumer<DoubleGaugeAdviceConfigurer> adviceConsumer) {
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,17 @@ 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 advice for gauge implementations. */
|
||||
default LongGaugeBuilder setAdvice(Consumer<LongGaugeAdviceConfigurer> adviceConsumer) {
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.extension.incubator.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/** A gauge instrument that synchronously records {@code long} values. */
|
||||
@ThreadSafe
|
||||
public interface LongGauge {
|
||||
/**
|
||||
* Set the gauge value.
|
||||
*
|
||||
* @param value The current gauge value.
|
||||
*/
|
||||
void set(long value);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void set(long value, Attributes attributes);
|
||||
|
||||
// TODO(jack-berg): should we add overload with Context argument?
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.LongGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.extension.incubator.metrics.DoubleGauge;
|
||||
import io.opentelemetry.extension.incubator.metrics.DoubleGaugeAdviceConfigurer;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedDoubleGaugeBuilder;
|
||||
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
|
||||
|
||||
private final WriteableMetricStorage storage;
|
||||
|
||||
private SdkDoubleGauge(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
|
||||
super(descriptor);
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(double increment, Attributes attributes) {
|
||||
storage.recordDouble(increment, attributes, Context.root());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(double increment) {
|
||||
set(increment, Attributes.empty());
|
||||
}
|
||||
|
||||
static final class SdkDoubleGaugeBuilder extends AbstractInstrumentBuilder<SdkDoubleGaugeBuilder>
|
||||
implements ExtendedDoubleGaugeBuilder, DoubleGaugeAdviceConfigurer {
|
||||
|
||||
SdkDoubleGaugeBuilder(
|
||||
MeterProviderSharedState meterProviderSharedState,
|
||||
MeterSharedState meterSharedState,
|
||||
String name) {
|
||||
super(
|
||||
meterProviderSharedState,
|
||||
meterSharedState,
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
InstrumentType.OBSERVABLE_GAUGE,
|
||||
InstrumentValueType.DOUBLE,
|
||||
name,
|
||||
"",
|
||||
DEFAULT_UNIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SdkDoubleGaugeBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdkDoubleGauge build() {
|
||||
return buildSynchronousInstrument(SdkDoubleGauge::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGaugeBuilder setAdvice(Consumer<DoubleGaugeAdviceConfigurer> adviceConsumer) {
|
||||
adviceConsumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGaugeAdviceConfigurer setAttributes(List<AttributeKey<?>> attributes) {
|
||||
adviceBuilder.setAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeBuilder ofLongs() {
|
||||
return swapBuilder(SdkLongGauge.SdkLongGaugeBuilder::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableDoubleGauge buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
return registerDoubleAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableDoubleMeasurement buildObserver() {
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
return buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.LongGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
|
||||
import io.opentelemetry.extension.incubator.metrics.DoubleGaugeAdviceConfigurer;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedDoubleGaugeBuilder;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterSharedState;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class SdkDoubleGaugeBuilder extends AbstractInstrumentBuilder<SdkDoubleGaugeBuilder>
|
||||
implements ExtendedDoubleGaugeBuilder, DoubleGaugeAdviceConfigurer {
|
||||
|
||||
SdkDoubleGaugeBuilder(
|
||||
MeterProviderSharedState meterProviderSharedState,
|
||||
MeterSharedState meterSharedState,
|
||||
String name) {
|
||||
super(
|
||||
meterProviderSharedState,
|
||||
meterSharedState,
|
||||
InstrumentType.OBSERVABLE_GAUGE,
|
||||
InstrumentValueType.DOUBLE,
|
||||
name,
|
||||
"",
|
||||
DEFAULT_UNIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SdkDoubleGaugeBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGaugeBuilder setAdvice(Consumer<DoubleGaugeAdviceConfigurer> adviceConsumer) {
|
||||
adviceConsumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeBuilder ofLongs() {
|
||||
return swapBuilder(SdkLongGaugeBuilder::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableDoubleGauge buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
|
||||
return registerDoubleAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableDoubleMeasurement buildObserver() {
|
||||
return buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGaugeAdviceConfigurer setAttributes(List<AttributeKey<?>> attributes) {
|
||||
adviceBuilder.setAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.LongGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.ObservableLongGauge;
|
||||
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedLongGaugeBuilder;
|
||||
import io.opentelemetry.extension.incubator.metrics.LongGauge;
|
||||
import io.opentelemetry.extension.incubator.metrics.LongGaugeAdviceConfigurer;
|
||||
import io.opentelemetry.sdk.metrics.internal.descriptor.Advice;
|
||||
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class SdkLongGauge extends AbstractInstrument implements LongGauge {
|
||||
|
||||
private final WriteableMetricStorage storage;
|
||||
|
||||
private SdkLongGauge(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
|
||||
super(descriptor);
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long increment, Attributes attributes) {
|
||||
storage.recordLong(increment, attributes, Context.root());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long increment) {
|
||||
set(increment, Attributes.empty());
|
||||
}
|
||||
|
||||
static final class SdkLongGaugeBuilder extends AbstractInstrumentBuilder<SdkLongGaugeBuilder>
|
||||
implements ExtendedLongGaugeBuilder, LongGaugeAdviceConfigurer {
|
||||
|
||||
SdkLongGaugeBuilder(
|
||||
MeterProviderSharedState meterProviderSharedState,
|
||||
MeterSharedState sharedState,
|
||||
String name,
|
||||
String description,
|
||||
String unit,
|
||||
Advice.AdviceBuilder adviceBuilder) {
|
||||
super(
|
||||
meterProviderSharedState,
|
||||
sharedState,
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
InstrumentType.OBSERVABLE_GAUGE,
|
||||
InstrumentValueType.LONG,
|
||||
name,
|
||||
description,
|
||||
unit,
|
||||
adviceBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SdkLongGaugeBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdkLongGauge build() {
|
||||
return buildSynchronousInstrument(SdkLongGauge::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeBuilder setAdvice(Consumer<LongGaugeAdviceConfigurer> adviceConsumer) {
|
||||
adviceConsumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeAdviceConfigurer setAttributes(List<AttributeKey<?>> attributes) {
|
||||
adviceBuilder.setAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableLongGauge buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
return registerLongAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableLongMeasurement buildObserver() {
|
||||
// TODO: use InstrumentType.GAUGE when available
|
||||
return buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.metrics.LongGaugeBuilder;
|
||||
import io.opentelemetry.api.metrics.ObservableLongGauge;
|
||||
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedLongGaugeBuilder;
|
||||
import io.opentelemetry.extension.incubator.metrics.LongGaugeAdviceConfigurer;
|
||||
import io.opentelemetry.sdk.metrics.internal.descriptor.Advice;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterSharedState;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class SdkLongGaugeBuilder extends AbstractInstrumentBuilder<SdkLongGaugeBuilder>
|
||||
implements ExtendedLongGaugeBuilder, LongGaugeAdviceConfigurer {
|
||||
|
||||
SdkLongGaugeBuilder(
|
||||
MeterProviderSharedState meterProviderSharedState,
|
||||
MeterSharedState sharedState,
|
||||
String name,
|
||||
String description,
|
||||
String unit,
|
||||
Advice.AdviceBuilder adviceBuilder) {
|
||||
super(
|
||||
meterProviderSharedState,
|
||||
sharedState,
|
||||
InstrumentType.OBSERVABLE_GAUGE,
|
||||
InstrumentValueType.LONG,
|
||||
name,
|
||||
description,
|
||||
unit,
|
||||
adviceBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SdkLongGaugeBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeBuilder setAdvice(Consumer<LongGaugeAdviceConfigurer> adviceConsumer) {
|
||||
adviceConsumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableLongGauge buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
|
||||
return registerLongAsynchronousInstrument(InstrumentType.OBSERVABLE_GAUGE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableLongMeasurement buildObserver() {
|
||||
return buildObservableMeasurement(InstrumentType.OBSERVABLE_GAUGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongGaugeAdviceConfigurer setAttributes(List<AttributeKey<?>> attributes) {
|
||||
adviceBuilder.setAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +108,8 @@ final class SdkMeter implements Meter {
|
|||
public DoubleGaugeBuilder gaugeBuilder(String name) {
|
||||
return !checkValidInstrumentName(name)
|
||||
? NOOP_METER.gaugeBuilder(NOOP_INSTRUMENT_NAME)
|
||||
: new SdkDoubleGaugeBuilder(meterProviderSharedState, meterSharedState, name);
|
||||
: new SdkDoubleGauge.SdkDoubleGaugeBuilder(
|
||||
meterProviderSharedState, meterSharedState, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
|
||||
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 java.time.Duration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for SDK {@link ObservableDoubleGauge}. */
|
||||
class SdkDoubleGaugeBuilderTest {
|
||||
private static final Resource RESOURCE =
|
||||
Resource.create(Attributes.of(stringKey("resource_key"), "resource_value"));
|
||||
private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO =
|
||||
InstrumentationScopeInfo.create(SdkDoubleGaugeBuilderTest.class.getName());
|
||||
private final TestClock testClock = TestClock.create();
|
||||
private final InMemoryMetricReader sdkMeterReader = InMemoryMetricReader.create();
|
||||
private final SdkMeterProvider sdkMeterProvider =
|
||||
SdkMeterProvider.builder()
|
||||
.setClock(testClock)
|
||||
.setResource(RESOURCE)
|
||||
.registerMetricReader(sdkMeterReader)
|
||||
.build();
|
||||
private final Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
|
||||
|
||||
@Test
|
||||
void removeCallback() {
|
||||
ObservableDoubleGauge gauge =
|
||||
sdkMeter.gaugeBuilder("testGauge").buildWithCallback(measurement -> measurement.record(10));
|
||||
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName("testGauge")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
doubleGauge -> doubleGauge.hasPointsSatisfying(poit -> {})));
|
||||
|
||||
gauge.close();
|
||||
|
||||
assertThat(sdkMeterReader.collectAllMetrics()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_NoRecords() {
|
||||
sdkMeter
|
||||
.gaugeBuilder("testObserver")
|
||||
.setDescription("My own DoubleValueObserver")
|
||||
.setUnit("ms")
|
||||
.buildWithCallback(result -> {});
|
||||
assertThat(sdkMeterReader.collectAllMetrics()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithOneRecord() {
|
||||
sdkMeter
|
||||
.gaugeBuilder("testObserver")
|
||||
.setDescription("My own DoubleValueObserver")
|
||||
.setUnit("ms")
|
||||
.buildWithCallback(
|
||||
result -> result.record(12.1d, Attributes.builder().put("k", "v").build()));
|
||||
testClock.advance(Duration.ofSeconds(1));
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testObserver")
|
||||
.hasDescription("My own DoubleValueObserver")
|
||||
.hasUnit("ms")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - 1000000000L)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(attributeEntry("k", "v"))
|
||||
.hasValue(12.1d))));
|
||||
testClock.advance(Duration.ofSeconds(1));
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testObserver")
|
||||
.hasDescription("My own DoubleValueObserver")
|
||||
.hasUnit("ms")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - 2000000000L)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(attributeEntry("k", "v"))
|
||||
.hasValue(12.1d))));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
|
||||
import io.opentelemetry.extension.incubator.metrics.DoubleGauge;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedDoubleGaugeBuilder;
|
||||
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 java.time.Duration;
|
||||
import java.util.stream.IntStream;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link SdkDoubleGauge}. */
|
||||
class SdkDoubleGaugeTest {
|
||||
private static final long SECOND_NANOS = 1_000_000_000;
|
||||
private static final Resource RESOURCE =
|
||||
Resource.create(Attributes.of(stringKey("resource_key"), "resource_value"));
|
||||
private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO =
|
||||
InstrumentationScopeInfo.create(SdkDoubleGaugeTest.class.getName());
|
||||
private final TestClock testClock = TestClock.create();
|
||||
private final InMemoryMetricReader cumulativeReader = InMemoryMetricReader.create();
|
||||
private final InMemoryMetricReader deltaReader = InMemoryMetricReader.createDelta();
|
||||
private final SdkMeterProvider sdkMeterProvider =
|
||||
SdkMeterProvider.builder()
|
||||
.setClock(testClock)
|
||||
.registerMetricReader(cumulativeReader)
|
||||
.registerMetricReader(deltaReader)
|
||||
.setResource(RESOURCE)
|
||||
.build();
|
||||
private final Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
|
||||
|
||||
@Test
|
||||
void set_PreventNullAttributes() {
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge"))
|
||||
.build()
|
||||
.set(1.0, null))
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.hasMessage("attributes");
|
||||
}
|
||||
|
||||
@Test
|
||||
void observable_RemoveCallback() {
|
||||
ObservableDoubleGauge gauge =
|
||||
sdkMeter.gaugeBuilder("testGauge").buildWithCallback(measurement -> measurement.record(10));
|
||||
|
||||
Assertions.assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName("testGauge")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
doubleGauge -> doubleGauge.hasPointsSatisfying(point -> {})));
|
||||
|
||||
gauge.close();
|
||||
|
||||
Assertions.assertThat(cumulativeReader.collectAllMetrics()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_NoRecords() {
|
||||
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
|
||||
assertThat(cumulativeReader.collectAllMetrics()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithEmptyAttributes() {
|
||||
DoubleGauge doubleGauge =
|
||||
((ExtendedDoubleGaugeBuilder)
|
||||
sdkMeter.gaugeBuilder("testGauge").setDescription("description").setUnit("K"))
|
||||
.build();
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
doubleGauge.set(12d, Attributes.empty());
|
||||
doubleGauge.set(13d);
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("description")
|
||||
.hasUnit("K")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - SECOND_NANOS)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(13d))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithMultipleCollects() {
|
||||
long startTime = testClock.now();
|
||||
DoubleGauge doubleGauge =
|
||||
((ExtendedDoubleGaugeBuilder) 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());
|
||||
// Advancing time here should not matter.
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
doubleGauge.set(321.5d, Attributes.builder().put("K", "V").build());
|
||||
doubleGauge.set(111.1d, Attributes.builder().put("K", "V").build());
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("")
|
||||
.hasUnit("")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21.4d),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(111.1d)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
assertThat(deltaReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("")
|
||||
.hasUnit("")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21.4d),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(111.1d)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
|
||||
// Repeat to prove we keep previous values.
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
doubleGauge.set(222d, Attributes.builder().put("K", "V").build());
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21.4d),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(222d)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
// Delta reader should only have point for {K=V} series, since the {} did not have any
|
||||
// measurements
|
||||
assertThat(deltaReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime + SECOND_NANOS)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(222d)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void stressTest() {
|
||||
DoubleGauge doubleGauge =
|
||||
((ExtendedDoubleGaugeBuilder) sdkMeter.gaugeBuilder("testGauge")).build();
|
||||
|
||||
StressTestRunner.Builder stressTestBuilder =
|
||||
StressTestRunner.builder().setCollectionIntervalMs(100);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stressTestBuilder.addOperation(
|
||||
StressTestRunner.Operation.create(
|
||||
1_000,
|
||||
1,
|
||||
() -> {
|
||||
doubleGauge.set(10, Attributes.builder().put("K", "V").build());
|
||||
doubleGauge.set(11, Attributes.builder().put("K", "V").build());
|
||||
}));
|
||||
}
|
||||
|
||||
stressTestBuilder.build().run();
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
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();
|
||||
|
||||
StressTestRunner.Builder stressTestBuilder =
|
||||
StressTestRunner.builder().setCollectionIntervalMs(100);
|
||||
|
||||
IntStream.range(0, 4)
|
||||
.forEach(
|
||||
i ->
|
||||
stressTestBuilder.addOperation(
|
||||
StressTestRunner.Operation.create(
|
||||
2_000,
|
||||
1,
|
||||
() -> {
|
||||
doubleGauge.set(10, Attributes.builder().put(keys[i], values[i]).build());
|
||||
doubleGauge.set(11, Attributes.builder().put(keys[i], values[i]).build());
|
||||
})));
|
||||
|
||||
stressTestBuilder.build().run();
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasDoubleGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[0], values[0])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[1], values[1])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[2], values[2])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[3], values[3])))));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.api.metrics.ObservableLongGauge;
|
||||
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 java.time.Duration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for SDK {@link ObservableLongGauge}. */
|
||||
class SdkLongGaugeBuilderTest {
|
||||
private static final Resource RESOURCE =
|
||||
Resource.create(Attributes.of(stringKey("resource_key"), "resource_value"));
|
||||
private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO =
|
||||
InstrumentationScopeInfo.create(SdkLongGaugeBuilderTest.class.getName());
|
||||
private final TestClock testClock = TestClock.create();
|
||||
private final InMemoryMetricReader sdkMeterReader = InMemoryMetricReader.create();
|
||||
private final SdkMeterProvider sdkMeterProvider =
|
||||
SdkMeterProvider.builder()
|
||||
.setClock(testClock)
|
||||
.setResource(RESOURCE)
|
||||
.registerMetricReader(sdkMeterReader)
|
||||
.build();
|
||||
private final Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
|
||||
|
||||
@Test
|
||||
void removeCallback() {
|
||||
ObservableLongGauge gauge =
|
||||
sdkMeter
|
||||
.gaugeBuilder("testGauge")
|
||||
.ofLongs()
|
||||
.buildWithCallback(measurement -> measurement.record(10));
|
||||
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName("testGauge")
|
||||
.hasLongGaugeSatisfying(
|
||||
longGauge -> longGauge.hasPointsSatisfying(point -> {})));
|
||||
|
||||
gauge.close();
|
||||
|
||||
assertThat(sdkMeterReader.collectAllMetrics()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_NoRecords() {
|
||||
sdkMeter
|
||||
.gaugeBuilder("testObserver")
|
||||
.ofLongs()
|
||||
.setDescription("My own LongValueObserver")
|
||||
.setUnit("ms")
|
||||
.buildWithCallback(result -> {});
|
||||
assertThat(sdkMeterReader.collectAllMetrics()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithOneRecord() {
|
||||
sdkMeter
|
||||
.gaugeBuilder("testObserver")
|
||||
.ofLongs()
|
||||
.buildWithCallback(result -> result.record(12, Attributes.builder().put("k", "v").build()));
|
||||
testClock.advance(Duration.ofSeconds(1));
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testObserver")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - 1000000000L)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(attributeEntry("k", "v"))
|
||||
.hasValue(12))));
|
||||
testClock.advance(Duration.ofSeconds(1));
|
||||
assertThat(sdkMeterReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testObserver")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - 2000000000L)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(attributeEntry("k", "v"))
|
||||
.hasValue(12))));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.api.metrics.ObservableLongGauge;
|
||||
import io.opentelemetry.extension.incubator.metrics.ExtendedLongGaugeBuilder;
|
||||
import io.opentelemetry.extension.incubator.metrics.LongGauge;
|
||||
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 java.time.Duration;
|
||||
import java.util.stream.IntStream;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link SdkLongGauge}. */
|
||||
class SdkLongGaugeTest {
|
||||
private static final long SECOND_NANOS = 1_000_000_000;
|
||||
private static final Resource RESOURCE =
|
||||
Resource.create(Attributes.of(stringKey("resource_key"), "resource_value"));
|
||||
private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO =
|
||||
InstrumentationScopeInfo.create(SdkLongGaugeTest.class.getName());
|
||||
private final TestClock testClock = TestClock.create();
|
||||
private final InMemoryMetricReader cumulativeReader = InMemoryMetricReader.create();
|
||||
private final InMemoryMetricReader deltaReader = InMemoryMetricReader.createDelta();
|
||||
private final SdkMeterProvider sdkMeterProvider =
|
||||
SdkMeterProvider.builder()
|
||||
.setClock(testClock)
|
||||
.registerMetricReader(cumulativeReader)
|
||||
.registerMetricReader(deltaReader)
|
||||
.setResource(RESOURCE)
|
||||
.build();
|
||||
private final Meter sdkMeter = sdkMeterProvider.get(getClass().getName());
|
||||
|
||||
@Test
|
||||
void set_PreventNullAttributes() {
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs())
|
||||
.build()
|
||||
.set(1, null))
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.hasMessage("attributes");
|
||||
}
|
||||
|
||||
@Test
|
||||
void observable_RemoveCallback() {
|
||||
ObservableLongGauge gauge =
|
||||
sdkMeter
|
||||
.gaugeBuilder("testGauge")
|
||||
.ofLongs()
|
||||
.buildWithCallback(measurement -> measurement.record(10));
|
||||
|
||||
Assertions.assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasName("testGauge")
|
||||
.hasLongGaugeSatisfying(
|
||||
longGauge -> longGauge.hasPointsSatisfying(point -> {})));
|
||||
|
||||
gauge.close();
|
||||
|
||||
Assertions.assertThat(cumulativeReader.collectAllMetrics()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_NoRecords() {
|
||||
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
|
||||
assertThat(cumulativeReader.collectAllMetrics()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithEmptyAttributes() {
|
||||
LongGauge longGauge =
|
||||
((ExtendedLongGaugeBuilder)
|
||||
sdkMeter
|
||||
.gaugeBuilder("testGauge")
|
||||
.ofLongs()
|
||||
.setDescription("description")
|
||||
.setUnit("K"))
|
||||
.build();
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
longGauge.set(12, Attributes.empty());
|
||||
longGauge.set(13);
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("description")
|
||||
.hasUnit("K")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now() - SECOND_NANOS)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(13))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectMetrics_WithMultipleCollects() {
|
||||
long startTime = testClock.now();
|
||||
LongGauge longGauge =
|
||||
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
|
||||
longGauge.set(12, Attributes.empty());
|
||||
longGauge.set(12, Attributes.builder().put("K", "V").build());
|
||||
longGauge.set(21, Attributes.empty());
|
||||
// Advancing time here should not matter.
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
longGauge.set(321, Attributes.builder().put("K", "V").build());
|
||||
longGauge.set(111, Attributes.builder().put("K", "V").build());
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("")
|
||||
.hasUnit("")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(111)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
assertThat(deltaReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasDescription("")
|
||||
.hasUnit("")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(111)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
|
||||
// Repeat to prove we keep previous values.
|
||||
testClock.advance(Duration.ofNanos(SECOND_NANOS));
|
||||
longGauge.set(222, Attributes.builder().put("K", "V").build());
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasAttributes(Attributes.empty())
|
||||
.hasValue(21),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(222)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
// Delta reader should only have point for {K=V} series, since the {} did not have any
|
||||
// measurements
|
||||
assertThat(deltaReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(startTime + SECOND_NANOS)
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(222)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void stressTest() {
|
||||
LongGauge longGauge =
|
||||
((ExtendedLongGaugeBuilder) sdkMeter.gaugeBuilder("testGauge").ofLongs()).build();
|
||||
|
||||
StressTestRunner.Builder stressTestBuilder =
|
||||
StressTestRunner.builder().setCollectionIntervalMs(100);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stressTestBuilder.addOperation(
|
||||
StressTestRunner.Operation.create(
|
||||
1_000,
|
||||
1,
|
||||
() -> {
|
||||
longGauge.set(10, Attributes.builder().put("K", "V").build());
|
||||
longGauge.set(11, Attributes.builder().put("K", "V").build());
|
||||
}));
|
||||
}
|
||||
|
||||
stressTestBuilder.build().run();
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasName("testGauge")
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry("K", "V")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
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();
|
||||
|
||||
StressTestRunner.Builder stressTestBuilder =
|
||||
StressTestRunner.builder().setCollectionIntervalMs(100);
|
||||
|
||||
IntStream.range(0, 4)
|
||||
.forEach(
|
||||
i ->
|
||||
stressTestBuilder.addOperation(
|
||||
StressTestRunner.Operation.create(
|
||||
2_000,
|
||||
1,
|
||||
() -> {
|
||||
longGauge.set(10, Attributes.builder().put(keys[i], values[i]).build());
|
||||
longGauge.set(11, Attributes.builder().put(keys[i], values[i]).build());
|
||||
})));
|
||||
|
||||
stressTestBuilder.build().run();
|
||||
assertThat(cumulativeReader.collectAllMetrics())
|
||||
.satisfiesExactly(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasResource(RESOURCE)
|
||||
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
|
||||
.hasLongGaugeSatisfying(
|
||||
gauge ->
|
||||
gauge.hasPointsSatisfying(
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[0], values[0])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[1], values[1])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[2], values[2])),
|
||||
point ->
|
||||
point
|
||||
.hasStartEpochNanos(testClock.now())
|
||||
.hasEpochNanos(testClock.now())
|
||||
.hasValue(11)
|
||||
.hasAttributes(attributeEntry(keys[3], values[3])))));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue