Skip to content

Commit

Permalink
Stabilize synchronous gauge (#6419)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg committed May 9, 2024
1 parent ca79821 commit c7d472a
Show file tree
Hide file tree
Showing 21 changed files with 346 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,10 @@ public LongHistogram build() {
}

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) {
Expand All @@ -340,17 +342,34 @@ public LongGaugeBuilder ofLongs() {

@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) {
Expand All @@ -364,13 +383,29 @@ public LongGaugeBuilder setUnit(String unit) {

@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ void noopDoubleHistogram_doesNotThrow() {
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
Expand All @@ -107,6 +121,19 @@ void noopObservableLongGauage_doesNotThrow() {
});
}

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,10 @@
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,10 @@
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,48 +23,6 @@
/** 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
Expand Down
19 changes: 18 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-api.txt
Original file line number Diff line number Diff line change
@@ -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()
3 changes: 3 additions & 0 deletions docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ public enum InstrumentType {
OBSERVABLE_COUNTER,
OBSERVABLE_UP_DOWN_COUNTER,
OBSERVABLE_GAUGE,
GAUGE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

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;
Expand All @@ -31,8 +31,13 @@ private SdkDoubleGauge(InstrumentDescriptor descriptor, WriteableMetricStorage s
}

@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
Expand All @@ -48,11 +53,10 @@ static final class SdkDoubleGaugeBuilder implements ExtendedDoubleGaugeBuilder {
MeterSharedState meterSharedState,
String name) {

// TODO: use InstrumentType.GAUGE when available
builder =
new InstrumentBuilder(
name,
InstrumentType.OBSERVABLE_GAUGE,
InstrumentType.GAUGE,
InstrumentValueType.DOUBLE,
meterProviderSharedState,
meterSharedState);
Expand Down Expand Up @@ -88,13 +92,11 @@ public LongGaugeBuilder ofLongs() {

@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);
}

Expand Down

0 comments on commit c7d472a

Please sign in to comment.