diff --git a/sdk/src/main/java/io/opentelemetry/sdk/metrics/AbstractObserver.java b/sdk/src/main/java/io/opentelemetry/sdk/metrics/AbstractObserver.java index 8c8ca5cdd7..617d06cb5f 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/metrics/AbstractObserver.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/metrics/AbstractObserver.java @@ -17,7 +17,9 @@ package io.opentelemetry.sdk.metrics; import io.opentelemetry.metrics.Observer; +import io.opentelemetry.sdk.metrics.common.InstrumentType; import io.opentelemetry.sdk.metrics.common.InstrumentValueType; +import io.opentelemetry.sdk.metrics.view.Aggregations; abstract class AbstractObserver extends AbstractInstrument { private final boolean monotonic; @@ -33,7 +35,15 @@ abstract class AbstractObserver extends AbstractInstrument { descriptor, meterProviderSharedState, meterSharedState, - new ActiveBatcher(Batchers.getNoop())); + new ActiveBatcher( + new ActiveBatcher( + getDefaultBatcher( + descriptor, + getInstrumentType(monotonic), + instrumentValueType, + meterProviderSharedState, + meterSharedState, + Aggregations.lastValue())))); this.monotonic = monotonic; this.instrumentValueType = instrumentValueType; } @@ -88,4 +98,8 @@ abstract class AbstractObserver extends AbstractInstrument { return this.monotonic; } } + + private static InstrumentType getInstrumentType(boolean monotonic) { + return monotonic ? InstrumentType.OBSERVER_MONOTONIC : InstrumentType.OBSERVER_NON_MONOTONIC; + } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/metrics/DoubleObserverSdk.java b/sdk/src/main/java/io/opentelemetry/sdk/metrics/DoubleObserverSdk.java index ece6bd7df2..2e7501760a 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/metrics/DoubleObserverSdk.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/metrics/DoubleObserverSdk.java @@ -23,10 +23,12 @@ import io.opentelemetry.sdk.metrics.common.InstrumentValueType; import io.opentelemetry.sdk.metrics.data.MetricData; import java.util.Collections; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; final class DoubleObserverSdk extends AbstractObserver implements DoubleObserver { @Nullable private volatile Callback metricUpdater = null; + private final ReentrantLock collectLock = new ReentrantLock(); DoubleObserverSdk( InstrumentDescriptor descriptor, @@ -35,7 +37,7 @@ final class DoubleObserverSdk extends AbstractObserver implements DoubleObserver boolean monotonic) { super( descriptor, - InstrumentValueType.LONG, + InstrumentValueType.DOUBLE, meterProviderSharedState, meterSharedState, monotonic); @@ -47,9 +49,14 @@ final class DoubleObserverSdk extends AbstractObserver implements DoubleObserver if (currentMetricUpdater == null) { return Collections.emptyList(); } - final ActiveBatcher activeBatcher = getActiveBatcher(); - currentMetricUpdater.update(new ResultDoubleObserverSdk(activeBatcher, isMonotonic())); - return activeBatcher.completeCollectionCycle(); + collectLock.lock(); + try { + final ActiveBatcher activeBatcher = getActiveBatcher(); + currentMetricUpdater.update(new ResultDoubleObserverSdk(activeBatcher, isMonotonic())); + return activeBatcher.completeCollectionCycle(); + } finally { + collectLock.unlock(); + } } @Override diff --git a/sdk/src/main/java/io/opentelemetry/sdk/metrics/LongObserverSdk.java b/sdk/src/main/java/io/opentelemetry/sdk/metrics/LongObserverSdk.java index cb397106b0..efdf27b0f4 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/metrics/LongObserverSdk.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/metrics/LongObserverSdk.java @@ -23,10 +23,12 @@ import io.opentelemetry.sdk.metrics.common.InstrumentValueType; import io.opentelemetry.sdk.metrics.data.MetricData; import java.util.Collections; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; final class LongObserverSdk extends AbstractObserver implements LongObserver { @Nullable private volatile Callback metricUpdater = null; + private final ReentrantLock collectLock = new ReentrantLock(); LongObserverSdk( InstrumentDescriptor descriptor, @@ -47,9 +49,14 @@ final class LongObserverSdk extends AbstractObserver implements LongObserver { if (currentMetricUpdater == null) { return Collections.emptyList(); } - final ActiveBatcher activeBatcher = getActiveBatcher(); - currentMetricUpdater.update(new ResultLongObserverSdk(activeBatcher, isMonotonic())); - return activeBatcher.completeCollectionCycle(); + collectLock.lock(); + try { + final ActiveBatcher activeBatcher = getActiveBatcher(); + currentMetricUpdater.update(new ResultLongObserverSdk(activeBatcher, isMonotonic())); + return activeBatcher.completeCollectionCycle(); + } finally { + collectLock.unlock(); + } } @Override diff --git a/sdk/src/test/java/io/opentelemetry/sdk/metrics/DoubleObserverSdkTest.java b/sdk/src/test/java/io/opentelemetry/sdk/metrics/DoubleObserverSdkTest.java index ab1cc86fe6..0246343f9a 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/metrics/DoubleObserverSdkTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/metrics/DoubleObserverSdkTest.java @@ -16,11 +16,18 @@ package io.opentelemetry.sdk.metrics; +import static com.google.common.truth.Truth.assertThat; + import io.opentelemetry.common.AttributeValue; import io.opentelemetry.metrics.DoubleObserver.ResultDoubleObserver; import io.opentelemetry.metrics.Observer.Callback; import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; import io.opentelemetry.sdk.internal.TestClock; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricData.Descriptor; +import io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type; +import io.opentelemetry.sdk.metrics.data.MetricData.DoublePoint; +import io.opentelemetry.sdk.metrics.data.MetricData.Point; import io.opentelemetry.sdk.resources.Resource; import java.util.Collections; import org.junit.Rule; @@ -34,6 +41,7 @@ import org.junit.runners.JUnit4; public class DoubleObserverSdkTest { @Rule public ExpectedException thrown = ExpectedException.none(); + private static final long SECOND_NANOS = 1_000_000_000; private static final Resource RESOURCE = Resource.create( Collections.singletonMap( @@ -46,6 +54,99 @@ public class DoubleObserverSdkTest { private final MeterSdk testSdk = new MeterSdk(meterProviderSharedState, INSTRUMENTATION_LIBRARY_INFO); + @Test + public void collectMetrics_NoCallback() { + DoubleObserverSdk doubleObserver = + testSdk + .doubleObserverBuilder("testObserver") + .setConstantLabels(Collections.singletonMap("sk1", "sv1")) + .setLabelKeys(Collections.singletonList("sk1")) + .setDescription("My very own measure") + .setUnit("ms") + .build(); + assertThat(doubleObserver.collectAll()).isEmpty(); + } + + @Test + public void collectMetrics_NoRecords() { + DoubleObserverSdk doubleObserver = + testSdk + .doubleObserverBuilder("testObserver") + .setConstantLabels(Collections.singletonMap("sk1", "sv1")) + .setLabelKeys(Collections.singletonList("sk1")) + .setDescription("My very own measure") + .setUnit("ms") + .build(); + doubleObserver.setCallback( + new Callback() { + @Override + public void update(ResultDoubleObserver result) { + // Do nothing. + } + }); + assertThat(doubleObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "My very own measure", + "ms", + Type.NON_MONOTONIC_DOUBLE, + Collections.singletonMap("sk1", "sv1")), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.emptyList())); + } + + @Test + public void collectMetrics_WithOneRecord() { + DoubleObserverSdk doubleObserver = + testSdk.doubleObserverBuilder("testObserver").setMonotonic(true).build(); + doubleObserver.setCallback( + new Callback() { + @Override + public void update(ResultDoubleObserver result) { + result.observe(12.1d, "k", "v"); + } + }); + testClock.advanceNanos(SECOND_NANOS); + assertThat(doubleObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "", + "1", + Type.MONOTONIC_DOUBLE, + Collections.emptyMap()), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.singletonList( + DoublePoint.create( + testClock.now() - SECOND_NANOS, + testClock.now(), + Collections.singletonMap("k", "v"), + 12.1d)))); + testClock.advanceNanos(SECOND_NANOS); + assertThat(doubleObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "", + "1", + Type.MONOTONIC_DOUBLE, + Collections.emptyMap()), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.singletonList( + DoublePoint.create( + testClock.now() - 2 * SECOND_NANOS, + testClock.now(), + Collections.singletonMap("k", "v"), + 12.1d)))); + } + @Test public void observeMonotonic_NegativeValue() { DoubleObserverSdk doubleObserver = diff --git a/sdk/src/test/java/io/opentelemetry/sdk/metrics/LongObserverSdkTest.java b/sdk/src/test/java/io/opentelemetry/sdk/metrics/LongObserverSdkTest.java index 5d97285b05..af53081b34 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/metrics/LongObserverSdkTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/metrics/LongObserverSdkTest.java @@ -16,11 +16,18 @@ package io.opentelemetry.sdk.metrics; +import static com.google.common.truth.Truth.assertThat; + import io.opentelemetry.common.AttributeValue; import io.opentelemetry.metrics.LongObserver.ResultLongObserver; import io.opentelemetry.metrics.Observer.Callback; import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; import io.opentelemetry.sdk.internal.TestClock; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricData.Descriptor; +import io.opentelemetry.sdk.metrics.data.MetricData.Descriptor.Type; +import io.opentelemetry.sdk.metrics.data.MetricData.LongPoint; +import io.opentelemetry.sdk.metrics.data.MetricData.Point; import io.opentelemetry.sdk.resources.Resource; import java.util.Collections; import org.junit.Rule; @@ -34,6 +41,7 @@ import org.junit.runners.JUnit4; public class LongObserverSdkTest { @Rule public ExpectedException thrown = ExpectedException.none(); + private static final long SECOND_NANOS = 1_000_000_000; private static final Resource RESOURCE = Resource.create( Collections.singletonMap( @@ -46,6 +54,99 @@ public class LongObserverSdkTest { private final MeterSdk testSdk = new MeterSdk(meterProviderSharedState, INSTRUMENTATION_LIBRARY_INFO); + @Test + public void collectMetrics_NoCallback() { + LongObserverSdk longObserver = + testSdk + .longObserverBuilder("testObserver") + .setConstantLabels(Collections.singletonMap("sk1", "sv1")) + .setLabelKeys(Collections.singletonList("sk1")) + .setDescription("My very own measure") + .setUnit("ms") + .build(); + assertThat(longObserver.collectAll()).isEmpty(); + } + + @Test + public void collectMetrics_NoRecords() { + LongObserverSdk longObserver = + testSdk + .longObserverBuilder("testObserver") + .setConstantLabels(Collections.singletonMap("sk1", "sv1")) + .setLabelKeys(Collections.singletonList("sk1")) + .setDescription("My very own measure") + .setUnit("ms") + .build(); + longObserver.setCallback( + new Callback() { + @Override + public void update(ResultLongObserver result) { + // Do nothing. + } + }); + assertThat(longObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "My very own measure", + "ms", + Type.NON_MONOTONIC_LONG, + Collections.singletonMap("sk1", "sv1")), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.emptyList())); + } + + @Test + public void collectMetrics_WithOneRecord() { + LongObserverSdk longObserver = + testSdk.longObserverBuilder("testObserver").setMonotonic(true).build(); + longObserver.setCallback( + new Callback() { + @Override + public void update(ResultLongObserver result) { + result.observe(12, "k", "v"); + } + }); + testClock.advanceNanos(SECOND_NANOS); + assertThat(longObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "", + "1", + Type.MONOTONIC_LONG, + Collections.emptyMap()), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.singletonList( + LongPoint.create( + testClock.now() - SECOND_NANOS, + testClock.now(), + Collections.singletonMap("k", "v"), + 12)))); + testClock.advanceNanos(SECOND_NANOS); + assertThat(longObserver.collectAll()) + .containsExactly( + MetricData.create( + Descriptor.create( + "testObserver", + "", + "1", + Type.MONOTONIC_LONG, + Collections.emptyMap()), + RESOURCE, + INSTRUMENTATION_LIBRARY_INFO, + Collections.singletonList( + LongPoint.create( + testClock.now() - 2 * SECOND_NANOS, + testClock.now(), + Collections.singletonMap("k", "v"), + 12)))); + } + @Test public void observeMonotonic_NegativeValue() { LongObserverSdk longObserver =