Implement metric identity specification (#4222)
* Implement metric identity specification * PR feedback * Fix build
This commit is contained in:
parent
ef99593d4f
commit
ccfa2dcbe4
|
|
@ -11,6 +11,7 @@ import static java.util.stream.Collectors.toList;
|
|||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil;
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
|
||||
import io.opentelemetry.sdk.metrics.view.InstrumentSelectorBuilder;
|
||||
|
|
@ -182,17 +183,10 @@ public final class ViewConfig {
|
|||
|
||||
// Visible for testing
|
||||
static Aggregation toAggregation(String aggregation) {
|
||||
switch (aggregation) {
|
||||
case "sum":
|
||||
return Aggregation.sum();
|
||||
case "last_value":
|
||||
return Aggregation.lastValue();
|
||||
case "drop":
|
||||
return Aggregation.drop();
|
||||
case "histogram":
|
||||
return Aggregation.explicitBucketHistogram();
|
||||
default:
|
||||
throw new ConfigurationException("Unrecognized aggregation " + aggregation);
|
||||
try {
|
||||
return AggregationUtil.forName(aggregation);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ConfigurationException("Error creating aggregation", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,14 +132,9 @@ class ViewConfigTest {
|
|||
|
||||
@Test
|
||||
void toAggregation() {
|
||||
assertThat(ViewConfig.toAggregation("sum")).isEqualTo(Aggregation.sum());
|
||||
assertThat(ViewConfig.toAggregation("last_value")).isEqualTo(Aggregation.lastValue());
|
||||
assertThat(ViewConfig.toAggregation("histogram"))
|
||||
.isEqualTo(Aggregation.explicitBucketHistogram());
|
||||
assertThat(ViewConfig.toAggregation("drop")).isEqualTo(Aggregation.drop());
|
||||
assertThatThrownBy(() -> ViewConfig.toAggregation("foo"))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Unrecognized aggregation foo");
|
||||
.hasMessageContaining("Error creating aggregation");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics.internal.descriptor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentValueType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* {@link InstrumentDescriptor#equals(Object)} must ignore {@link
|
||||
* InstrumentDescriptor#getSourceInfo()}, which only returns a meaningful value when {@code
|
||||
* otel.experimental.sdk.metrics.debug=true}.
|
||||
*/
|
||||
class InstrumentDescriptorTest {
|
||||
|
||||
@Test
|
||||
void equals() {
|
||||
InstrumentDescriptor descriptor =
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.LONG);
|
||||
|
||||
assertThat(descriptor)
|
||||
.isEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.LONG));
|
||||
|
||||
// Validate getSourceInfo() is not equal for otherwise equal descriptors
|
||||
assertThat(descriptor.getSourceInfo())
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.LONG)
|
||||
.getSourceInfo());
|
||||
|
||||
// Validate that name, description, unit, type, and value type are considered in equals
|
||||
assertThat(descriptor)
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"foo", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.LONG));
|
||||
assertThat(descriptor)
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name", "foo", "unit", InstrumentType.COUNTER, InstrumentValueType.LONG));
|
||||
assertThat(descriptor)
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "foo", InstrumentType.COUNTER, InstrumentValueType.LONG));
|
||||
assertThat(descriptor)
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name",
|
||||
"description",
|
||||
"unit",
|
||||
InstrumentType.UP_DOWN_COUNTER,
|
||||
InstrumentValueType.LONG));
|
||||
assertThat(descriptor)
|
||||
.isNotEqualTo(
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.DOUBLE));
|
||||
}
|
||||
}
|
||||
|
|
@ -45,11 +45,16 @@ class SourceInfoTest {
|
|||
MetricDescriptor.create(
|
||||
View.builder().build(),
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description2", "unit2", InstrumentType.COUNTER, InstrumentValueType.LONG));
|
||||
"name2",
|
||||
"description2",
|
||||
"unit2",
|
||||
InstrumentType.COUNTER,
|
||||
InstrumentValueType.LONG));
|
||||
assertThat(DebugUtils.duplicateMetricErrorMessage(simple, simpleWithNewDescription))
|
||||
.contains("Found duplicate metric definition: name")
|
||||
.contains("- Unit [unit2] does not match [unit]")
|
||||
.contains("- Description [description2] does not match [description]")
|
||||
.contains("- InstrumentDescription [description2] does not match [description]")
|
||||
.contains("- InstrumentName [name2] does not match [name]")
|
||||
.contains("- InstrumentUnit [unit2] does not match [unit]")
|
||||
.contains("- InstrumentType [COUNTER] does not match [OBSERVABLE_COUNTER]")
|
||||
.contains("- InstrumentValueType [LONG] does not match [DOUBLE]")
|
||||
.contains(simple.getSourceInstrument().getSourceInfo().multiLineDebugString())
|
||||
|
|
@ -60,10 +65,9 @@ class SourceInfoTest {
|
|||
|
||||
@Test
|
||||
void testDuplicateExceptionMessage_viewBasedConflict() {
|
||||
View problemView = View.builder().setName("name2").build();
|
||||
MetricDescriptor simple =
|
||||
MetricDescriptor.create(
|
||||
problemView,
|
||||
View.builder().setName("name2").build(),
|
||||
InstrumentDescriptor.create(
|
||||
"name",
|
||||
"description",
|
||||
|
|
@ -82,9 +86,9 @@ class SourceInfoTest {
|
|||
assertThat(DebugUtils.duplicateMetricErrorMessage(simple, simpleWithNewDescription))
|
||||
.contains("Found duplicate metric definition: name2")
|
||||
.contains(simple.getSourceInstrument().getSourceInfo().multiLineDebugString())
|
||||
.contains("- Description [description2] does not match [description]")
|
||||
.contains("- InstrumentDescription [description2] does not match [description]")
|
||||
.contains("Conflicting view registered")
|
||||
.contains(ImmutableView.getSourceInfo(problemView).multiLineDebugString())
|
||||
.contains(ImmutableView.getSourceInfo(simple.getSourceView()).multiLineDebugString())
|
||||
.contains("FROM instrument name")
|
||||
.contains(
|
||||
simpleWithNewDescription.getSourceInstrument().getSourceInfo().multiLineDebugString());
|
||||
|
|
@ -117,7 +121,7 @@ class SourceInfoTest {
|
|||
.contains(ImmutableView.getSourceInfo(problemView).multiLineDebugString())
|
||||
.contains("FROM instrument name2")
|
||||
.contains(simple.getSourceInstrument().getSourceInfo().multiLineDebugString())
|
||||
.contains("- Unit [unit] does not match [unit2]")
|
||||
.contains("- InstrumentUnit [unit] does not match [unit2]")
|
||||
.contains("Original instrument registered with same name but is incompatible.")
|
||||
.contains(
|
||||
simpleWithNewDescription.getSourceInstrument().getSourceInfo().multiLineDebugString());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics.internal.aggregator;
|
||||
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public class AggregationUtil {
|
||||
|
||||
private static final Map<String, Aggregation> aggregationByName;
|
||||
private static final Map<Class<? extends Aggregation>, String> nameByAggregation;
|
||||
|
||||
private static final String AGGREGATION_DEFAULT = "default";
|
||||
private static final String AGGREGATION_SUM = "sum";
|
||||
private static final String AGGREGATION_LAST_VALUE = "last_value";
|
||||
private static final String AGGREGATION_DROP = "drop";
|
||||
private static final String AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM = "explicit_bucket_histogram";
|
||||
|
||||
static {
|
||||
aggregationByName = new HashMap<>();
|
||||
aggregationByName.put(AGGREGATION_DEFAULT, Aggregation.defaultAggregation());
|
||||
aggregationByName.put(AGGREGATION_SUM, Aggregation.sum());
|
||||
aggregationByName.put(AGGREGATION_LAST_VALUE, Aggregation.lastValue());
|
||||
aggregationByName.put(AGGREGATION_DROP, Aggregation.drop());
|
||||
aggregationByName.put(
|
||||
AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM, Aggregation.explicitBucketHistogram());
|
||||
|
||||
nameByAggregation = new HashMap<>();
|
||||
nameByAggregation.put(Aggregation.defaultAggregation().getClass(), AGGREGATION_DEFAULT);
|
||||
nameByAggregation.put(Aggregation.sum().getClass(), AGGREGATION_SUM);
|
||||
nameByAggregation.put(Aggregation.lastValue().getClass(), AGGREGATION_LAST_VALUE);
|
||||
nameByAggregation.put(Aggregation.drop().getClass(), AGGREGATION_DROP);
|
||||
nameByAggregation.put(
|
||||
Aggregation.explicitBucketHistogram().getClass(), AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM);
|
||||
}
|
||||
|
||||
private AggregationUtil() {}
|
||||
|
||||
/**
|
||||
* Return the aggregation for the human-readable {@code name}.
|
||||
*
|
||||
* <p>The inverse of {@link #aggregationName(Aggregation)}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the name is not recognized
|
||||
*/
|
||||
public static Aggregation forName(String name) {
|
||||
Aggregation aggregation = aggregationByName.get(name);
|
||||
if (aggregation == null) {
|
||||
throw new IllegalArgumentException("Unrecognized aggregation name " + name);
|
||||
}
|
||||
return aggregation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the human-readable name of the {@code aggregation}.
|
||||
*
|
||||
* <p>The inverse of {@link #forName(String)}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the aggregation is not recognized
|
||||
*/
|
||||
public static String aggregationName(Aggregation aggregation) {
|
||||
String name = nameByAggregation.get(aggregation.getClass());
|
||||
if (name == null) {
|
||||
throw new IllegalStateException(
|
||||
"Unrecognized aggregation " + aggregation.getClass().getName());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ final class DoubleExponentialHistogramAggregator
|
|||
instrumentationScopeInfo,
|
||||
metricDescriptor.getName(),
|
||||
metricDescriptor.getDescription(),
|
||||
metricDescriptor.getUnit(),
|
||||
metricDescriptor.getSourceInstrument().getUnit(),
|
||||
ExponentialHistogramData.create(
|
||||
temporality,
|
||||
MetricDataUtils.toExponentialHistogramPointList(
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public final class DoubleHistogramAggregator implements Aggregator<HistogramAccu
|
|||
instrumentationScopeInfo,
|
||||
metricDescriptor.getName(),
|
||||
metricDescriptor.getDescription(),
|
||||
metricDescriptor.getUnit(),
|
||||
metricDescriptor.getSourceInstrument().getUnit(),
|
||||
ImmutableHistogramData.create(
|
||||
temporality,
|
||||
MetricDataUtils.toDoubleHistogramPointList(
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public final class DoubleLastValueAggregator implements Aggregator<DoubleAccumul
|
|||
instrumentationScopeInfo,
|
||||
descriptor.getName(),
|
||||
descriptor.getDescription(),
|
||||
descriptor.getUnit(),
|
||||
descriptor.getSourceInstrument().getUnit(),
|
||||
ImmutableGaugeData.create(
|
||||
MetricDataUtils.toDoublePointList(
|
||||
accumulationByLabels,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public final class DoubleSumAggregator extends AbstractSumAggregator<DoubleAccum
|
|||
instrumentationScopeInfo,
|
||||
descriptor.getName(),
|
||||
descriptor.getDescription(),
|
||||
descriptor.getUnit(),
|
||||
descriptor.getSourceInstrument().getUnit(),
|
||||
ImmutableSumData.create(
|
||||
isMonotonic(),
|
||||
temporality,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public final class LongLastValueAggregator implements Aggregator<LongAccumulatio
|
|||
instrumentationScopeInfo,
|
||||
descriptor.getName(),
|
||||
descriptor.getDescription(),
|
||||
descriptor.getUnit(),
|
||||
descriptor.getSourceInstrument().getUnit(),
|
||||
ImmutableGaugeData.create(
|
||||
MetricDataUtils.toLongPointList(
|
||||
accumulationByLabels,
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public final class LongSumAggregator extends AbstractSumAggregator<LongAccumulat
|
|||
instrumentationScopeInfo,
|
||||
descriptor.getName(),
|
||||
descriptor.getDescription(),
|
||||
descriptor.getUnit(),
|
||||
descriptor.getSourceInstrument().getUnit(),
|
||||
ImmutableSumData.create(
|
||||
isMonotonic(),
|
||||
temporality,
|
||||
|
|
|
|||
|
|
@ -21,14 +21,16 @@ import javax.annotation.concurrent.Immutable;
|
|||
@AutoValue
|
||||
@Immutable
|
||||
public abstract class InstrumentDescriptor {
|
||||
|
||||
private final SourceInfo sourceInfo = SourceInfo.fromCurrentStack();
|
||||
|
||||
public static InstrumentDescriptor create(
|
||||
String name,
|
||||
String description,
|
||||
String unit,
|
||||
InstrumentType type,
|
||||
InstrumentValueType valueType) {
|
||||
return new AutoValue_InstrumentDescriptor(
|
||||
name, description, unit, type, valueType, SourceInfo.fromCurrentStack());
|
||||
return new AutoValue_InstrumentDescriptor(name, description, unit, type, valueType);
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
|
@ -41,8 +43,13 @@ public abstract class InstrumentDescriptor {
|
|||
|
||||
public abstract InstrumentValueType getValueType();
|
||||
|
||||
/** Debugging information for this instrument. */
|
||||
public abstract SourceInfo getSourceInfo();
|
||||
/**
|
||||
* Debugging information for this instrument. Ignored from {@link #equals(Object)} and {@link
|
||||
* #toString()}
|
||||
*/
|
||||
public final SourceInfo getSourceInfo() {
|
||||
return sourceInfo;
|
||||
}
|
||||
|
||||
@Memoized
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import com.google.auto.value.AutoValue;
|
|||
import com.google.auto.value.extension.memoized.Memoized;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentValueType;
|
||||
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil;
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import io.opentelemetry.sdk.metrics.view.View;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
|
|
@ -27,7 +27,7 @@ import javax.annotation.concurrent.Immutable;
|
|||
public abstract class MetricDescriptor {
|
||||
|
||||
/**
|
||||
* Constructs a metric descriptor with no source instrument/view.
|
||||
* Constructs a metric descriptor with no instrument and default view.
|
||||
*
|
||||
* <p>Used for testing + empty-storage only.
|
||||
*/
|
||||
|
|
@ -35,8 +35,7 @@ public abstract class MetricDescriptor {
|
|||
return new AutoValue_MetricDescriptor(
|
||||
name,
|
||||
description,
|
||||
unit,
|
||||
Optional.empty(),
|
||||
View.builder().build(),
|
||||
InstrumentDescriptor.create(
|
||||
name, description, unit, InstrumentType.OBSERVABLE_GAUGE, InstrumentValueType.DOUBLE));
|
||||
}
|
||||
|
|
@ -46,21 +45,32 @@ public abstract class MetricDescriptor {
|
|||
String name = (view.getName() == null) ? instrument.getName() : view.getName();
|
||||
String description =
|
||||
(view.getDescription() == null) ? instrument.getDescription() : view.getDescription();
|
||||
return new AutoValue_MetricDescriptor(
|
||||
name, description, instrument.getUnit(), Optional.of(view), instrument);
|
||||
return new AutoValue_MetricDescriptor(name, description, view, instrument);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the descriptor, equal to {@link View#getName()} if not null, else {@link
|
||||
* InstrumentDescriptor#getName()}.
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
/**
|
||||
* The description of the descriptor, equal to {@link View#getDescription()} if not null, else
|
||||
* {@link InstrumentDescriptor#getDescription()}.
|
||||
*/
|
||||
public abstract String getDescription();
|
||||
|
||||
public abstract String getUnit();
|
||||
/** The view that lead to the creation of this metric. */
|
||||
public abstract View getSourceView();
|
||||
|
||||
/** The view that lead to the creation of this metric, if applicable. */
|
||||
public abstract Optional<View> getSourceView();
|
||||
/** The instrument which lead to the creation of this metric. */
|
||||
public abstract InstrumentDescriptor getSourceInstrument();
|
||||
|
||||
/** The {@link AggregationUtil#aggregationName(Aggregation)} of the view aggregation. */
|
||||
public String getAggregationName() {
|
||||
return AggregationUtil.aggregationName(getSourceView().getAggregation());
|
||||
}
|
||||
|
||||
@Memoized
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
|
@ -73,17 +83,24 @@ public abstract class MetricDescriptor {
|
|||
* <ul>
|
||||
* <li>{@link #getName()} is equal
|
||||
* <li>{@link #getDescription()} is equal
|
||||
* <li>{@link #getUnit()} is equal
|
||||
* <li>{@link #getAggregationName()} is equal
|
||||
* <li>{@link InstrumentDescriptor#getName()} is equal
|
||||
* <li>{@link InstrumentDescriptor#getDescription()} is equal
|
||||
* <li>{@link InstrumentDescriptor#getUnit()} is equal
|
||||
* <li>{@link InstrumentDescriptor#getType()} is equal
|
||||
* <li>{@link InstrumentDescriptor#getValueType()} is equal
|
||||
* </ul>
|
||||
*/
|
||||
public boolean isCompatibleWith(MetricDescriptor other) {
|
||||
return Objects.equals(getName(), other.getName())
|
||||
&& Objects.equals(getDescription(), other.getDescription())
|
||||
&& Objects.equals(getUnit(), other.getUnit())
|
||||
&& Objects.equals(getSourceInstrument().getType(), other.getSourceInstrument().getType())
|
||||
&& Objects.equals(
|
||||
getSourceInstrument().getValueType(), other.getSourceInstrument().getValueType());
|
||||
return getName().equals(other.getName())
|
||||
&& getDescription().equals(other.getDescription())
|
||||
&& getAggregationName().equals(other.getAggregationName())
|
||||
&& getSourceInstrument().getName().equals(other.getSourceInstrument().getName())
|
||||
&& getSourceInstrument()
|
||||
.getDescription()
|
||||
.equals(other.getSourceInstrument().getDescription())
|
||||
&& getSourceInstrument().getUnit().equals(other.getSourceInstrument().getUnit())
|
||||
&& getSourceInstrument().getType().equals(other.getSourceInstrument().getType())
|
||||
&& getSourceInstrument().getValueType().equals(other.getSourceInstrument().getValueType());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@ import io.opentelemetry.sdk.metrics.internal.view.ImmutableView;
|
|||
public final class DebugUtils {
|
||||
private DebugUtils() {}
|
||||
|
||||
static String duplicateMetricErrorMessage(DuplicateMetricStorageException ex) {
|
||||
return duplicateMetricErrorMessage(ex.getExisting(), ex.getConflict());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a detailed error message comparing two MetricDescriptors.
|
||||
* Creates a detailed error message comparing two {@link MetricDescriptor}s.
|
||||
*
|
||||
* <p>Called when the metrics with the descriptors have the same name, but {@link
|
||||
* MetricDescriptor#isCompatibleWith(MetricDescriptor)} is {@code false}.
|
||||
*
|
||||
* <p>This should identify all issues between the descriptor and log information on where they are
|
||||
* defined. Users should be able to find/fix issues based on this error.
|
||||
|
|
@ -43,11 +42,9 @@ public final class DebugUtils {
|
|||
// or a view on a raw instrument.
|
||||
if (!conflict.getName().equals(conflict.getSourceInstrument().getName())) {
|
||||
// Record the source view.
|
||||
result.append("\tVIEW defined\n");
|
||||
conflict
|
||||
.getSourceView()
|
||||
.ifPresent(v -> result.append(ImmutableView.getSourceInfo(v).multiLineDebugString()));
|
||||
result
|
||||
.append("\tVIEW defined\n")
|
||||
.append(ImmutableView.getSourceInfo(conflict.getSourceView()).multiLineDebugString())
|
||||
.append("\tFROM instrument ")
|
||||
.append(conflict.getSourceInstrument().getName())
|
||||
.append("\n")
|
||||
|
|
@ -59,6 +56,14 @@ public final class DebugUtils {
|
|||
}
|
||||
// Add information on what's at conflict.
|
||||
result.append("Causes\n");
|
||||
if (!existing.getName().equals(conflict.getName())) {
|
||||
result
|
||||
.append("- Name [")
|
||||
.append(conflict.getName())
|
||||
.append("] does not match [")
|
||||
.append(existing.getName())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing.getDescription().equals(conflict.getDescription())) {
|
||||
result
|
||||
.append("- Description [")
|
||||
|
|
@ -67,12 +72,45 @@ public final class DebugUtils {
|
|||
.append(existing.getDescription())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing.getUnit().equals(conflict.getUnit())) {
|
||||
if (!existing.getAggregationName().equals(conflict.getAggregationName())) {
|
||||
result
|
||||
.append("- Unit [")
|
||||
.append(conflict.getUnit())
|
||||
.append("- Aggregation [")
|
||||
.append(conflict.getAggregationName())
|
||||
.append("] does not match [")
|
||||
.append(existing.getUnit())
|
||||
.append(existing.getAggregationName())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing
|
||||
.getSourceInstrument()
|
||||
.getName()
|
||||
.equals(conflict.getSourceInstrument().getName())) {
|
||||
result
|
||||
.append("- InstrumentName [")
|
||||
.append(conflict.getSourceInstrument().getName())
|
||||
.append("] does not match [")
|
||||
.append(existing.getSourceInstrument().getName())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing
|
||||
.getSourceInstrument()
|
||||
.getDescription()
|
||||
.equals(conflict.getSourceInstrument().getDescription())) {
|
||||
result
|
||||
.append("- InstrumentDescription [")
|
||||
.append(conflict.getSourceInstrument().getDescription())
|
||||
.append("] does not match [")
|
||||
.append(existing.getSourceInstrument().getDescription())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing
|
||||
.getSourceInstrument()
|
||||
.getUnit()
|
||||
.equals(conflict.getSourceInstrument().getUnit())) {
|
||||
result
|
||||
.append("- InstrumentUnit [")
|
||||
.append(conflict.getSourceInstrument().getUnit())
|
||||
.append("] does not match [")
|
||||
.append(existing.getSourceInstrument().getUnit())
|
||||
.append("]\n");
|
||||
}
|
||||
if (!existing
|
||||
|
|
@ -107,12 +145,9 @@ public final class DebugUtils {
|
|||
.append("\n");
|
||||
} else {
|
||||
// Log that the view changed the name.
|
||||
result.append("Conflicting view registered.\n");
|
||||
existing
|
||||
.getSourceView()
|
||||
.ifPresent(
|
||||
view -> result.append(ImmutableView.getSourceInfo(view).multiLineDebugString()));
|
||||
result
|
||||
.append("Conflicting view registered.\n")
|
||||
.append(ImmutableView.getSourceInfo(existing.getSourceView()).multiLineDebugString())
|
||||
.append("FROM instrument ")
|
||||
.append(existing.getSourceInstrument().getName())
|
||||
.append("\n")
|
||||
|
|
|
|||
|
|
@ -17,11 +17,7 @@ import io.opentelemetry.sdk.metrics.internal.export.CollectionInfo;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
|
|
@ -34,8 +30,6 @@ import javax.annotation.concurrent.Immutable;
|
|||
@Immutable
|
||||
public abstract class MeterSharedState {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MeterSharedState.class.getName());
|
||||
|
||||
public static MeterSharedState create(InstrumentationScopeInfo instrumentationScopeInfo) {
|
||||
return new AutoValue_MeterSharedState(instrumentationScopeInfo, new MetricStorageRegistry());
|
||||
}
|
||||
|
|
@ -77,7 +71,7 @@ public abstract class MeterSharedState {
|
|||
public final WriteableMetricStorage registerSynchronousMetricStorage(
|
||||
InstrumentDescriptor instrument, MeterProviderSharedState meterProviderSharedState) {
|
||||
|
||||
List<WriteableMetricStorage> storage =
|
||||
List<SynchronousMetricStorage> storages =
|
||||
meterProviderSharedState
|
||||
.getViewRegistry()
|
||||
.findViews(instrument, getInstrumentationScopeInfo())
|
||||
|
|
@ -87,15 +81,17 @@ public abstract class MeterSharedState {
|
|||
SynchronousMetricStorage.create(
|
||||
view, instrument, meterProviderSharedState.getExemplarFilter()))
|
||||
.filter(m -> !m.isEmpty())
|
||||
.map(this::register)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toList());
|
||||
|
||||
if (storage.size() == 1) {
|
||||
return storage.get(0);
|
||||
List<SynchronousMetricStorage> registeredStorages = new ArrayList<>(storages.size());
|
||||
for (SynchronousMetricStorage storage : storages) {
|
||||
registeredStorages.add(getMetricStorageRegistry().register(storage));
|
||||
}
|
||||
// If the size is 0, we return an, effectively, no-op writer.
|
||||
return new MultiWritableMetricStorage(storage);
|
||||
|
||||
if (registeredStorages.size() == 1) {
|
||||
return registeredStorages.get(0);
|
||||
}
|
||||
return new MultiWritableMetricStorage(registeredStorages);
|
||||
}
|
||||
|
||||
/** Registers new asynchronous storage associated with a given {@code long} instrument. */
|
||||
|
|
@ -117,11 +113,10 @@ public abstract class MeterSharedState {
|
|||
List<AsynchronousMetricStorage<?, ObservableLongMeasurement>> registeredStorages =
|
||||
new ArrayList<>();
|
||||
for (AsynchronousMetricStorage<?, ObservableLongMeasurement> storage : storages) {
|
||||
AsynchronousMetricStorage<?, ObservableLongMeasurement> registeredStorage = register(storage);
|
||||
if (registeredStorage != null) {
|
||||
registeredStorage.addCallback(callback);
|
||||
registeredStorages.add(registeredStorage);
|
||||
}
|
||||
AsynchronousMetricStorage<?, ObservableLongMeasurement> registeredStorage =
|
||||
getMetricStorageRegistry().register(storage);
|
||||
registeredStorage.addCallback(callback);
|
||||
registeredStorages.add(registeredStorage);
|
||||
}
|
||||
return registeredStorages;
|
||||
}
|
||||
|
|
@ -146,24 +141,10 @@ public abstract class MeterSharedState {
|
|||
new ArrayList<>();
|
||||
for (AsynchronousMetricStorage<?, ObservableDoubleMeasurement> storage : storages) {
|
||||
AsynchronousMetricStorage<?, ObservableDoubleMeasurement> registeredStorage =
|
||||
register(storage);
|
||||
if (registeredStorage != null) {
|
||||
registeredStorage.addCallback(callback);
|
||||
registeredStorages.add(registeredStorage);
|
||||
}
|
||||
getMetricStorageRegistry().register(storage);
|
||||
registeredStorage.addCallback(callback);
|
||||
registeredStorages.add(registeredStorage);
|
||||
}
|
||||
return registeredStorages;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <S extends MetricStorage> S register(S storage) {
|
||||
try {
|
||||
return getMetricStorageRegistry().register(storage);
|
||||
} catch (DuplicateMetricStorageException e) {
|
||||
if (logger.isLoggable(Level.WARNING)) {
|
||||
logger.log(Level.WARNING, DebugUtils.duplicateMetricErrorMessage(e), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,71 +5,84 @@
|
|||
|
||||
package io.opentelemetry.sdk.metrics.internal.state;
|
||||
|
||||
import io.opentelemetry.api.internal.GuardedBy;
|
||||
import io.opentelemetry.sdk.metrics.internal.descriptor.MetricDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Responsible for storing metrics (by name) and returning access to input pipeline for instrument
|
||||
* wiring.
|
||||
* Responsible for storing metrics by {@link MetricDescriptor} and returning access to input
|
||||
* pipeline for instrument measurements.
|
||||
*
|
||||
* <p>The rules of the registry:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Only one storage type may be registered per-name. Repeated look-ups per-name will return
|
||||
* the same storage.
|
||||
* <li>The metric descriptor should be "compatible", when returning an existing metric storage,
|
||||
* i.e. same type of metric, same name, description etc.
|
||||
* <li>The registered storage type MUST be either always Asynchronous or always Synchronous. No
|
||||
* mixing and matching.
|
||||
* </ul>
|
||||
* <p>Each descriptor in the registry results in an exported metric stream. Under normal
|
||||
* circumstances each descriptor shares a unique {@link MetricDescriptor#getName()}. When multiple
|
||||
* descriptors share the same name, an identity conflict has occurred. The registry detects identity
|
||||
* conflicts on {@link #register(MetricStorage)} and logs diagnostic information when they occur.
|
||||
* See {@link MetricDescriptor#isCompatibleWith(MetricDescriptor)} for definition of compatibility.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public class MetricStorageRegistry {
|
||||
// TODO: Maybe we store metrics *and* instrument interfaces separately here...
|
||||
private final ConcurrentMap<String, MetricStorage> registry = new ConcurrentHashMap<>();
|
||||
private static final Logger logger = Logger.getLogger(MetricStorageRegistry.class.getName());
|
||||
|
||||
/**
|
||||
* Returns a {@code Collection} view of the registered {@link MetricStorage}.
|
||||
*
|
||||
* @return a {@code Collection} view of the registered {@link MetricStorage}.
|
||||
*/
|
||||
private final Object lock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
private final Map<MetricDescriptor, MetricStorage> registry = new HashMap<>();
|
||||
|
||||
/** Returns a {@link Collection} of the registered {@link MetricStorage}. */
|
||||
public Collection<MetricStorage> getMetrics() {
|
||||
return Collections.unmodifiableCollection(new ArrayList<>(registry.values()));
|
||||
synchronized (lock) {
|
||||
return Collections.unmodifiableCollection(new ArrayList<>(registry.values()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@code Metric} to this registry. Returns the registered storage if no other
|
||||
* metric with the same name is registered or a previously registered metric with same name and
|
||||
* equal with the current metric, otherwise throws an exception.
|
||||
* Registers the metric {@code newStorage} to this registry. If a metric with compatible identity
|
||||
* was previously registered, returns the previously registered storage. If a metric with the same
|
||||
* name (case-insensitive) but incompatible {@link MetricDescriptor} was previously registered,
|
||||
* logs a diagnostic warning and returns the {@code newStorage}.
|
||||
*
|
||||
* @param storage the metric storage to use or discard.
|
||||
* @return the given metric storage if no metric with same name already registered, otherwise the
|
||||
* previous registered instrument.
|
||||
* @throws IllegalArgumentException if instrument cannot be registered.
|
||||
* @param newStorage the metric storage to use or discard.
|
||||
* @return the {@code newStorage} if no compatible metric is already registered, otherwise the
|
||||
* previously registered storage.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <I extends MetricStorage> I register(I storage) {
|
||||
MetricDescriptor descriptor = storage.getMetricDescriptor();
|
||||
MetricStorage oldOrNewStorage =
|
||||
registry.computeIfAbsent(descriptor.getName().toLowerCase(), key -> storage);
|
||||
// Metric didn't already exist in registry, return.
|
||||
if (storage == oldOrNewStorage) {
|
||||
return (I) oldOrNewStorage;
|
||||
public <I extends MetricStorage> I register(I newStorage) {
|
||||
MetricDescriptor descriptor = newStorage.getMetricDescriptor();
|
||||
I oldOrNewStorage;
|
||||
List<MetricStorage> storages;
|
||||
synchronized (lock) {
|
||||
oldOrNewStorage = (I) registry.computeIfAbsent(descriptor, key -> newStorage);
|
||||
// If storage was NOT added to the registry, its description was a perfect match to one
|
||||
// previously registered and we can skip detecting identity conflicts
|
||||
if (newStorage != oldOrNewStorage || !logger.isLoggable(Level.WARNING)) {
|
||||
return oldOrNewStorage;
|
||||
}
|
||||
storages = new ArrayList<>(registry.values());
|
||||
}
|
||||
// Make sure the storage is compatible.
|
||||
if (!oldOrNewStorage.getMetricDescriptor().isCompatibleWith(descriptor)) {
|
||||
throw new DuplicateMetricStorageException(
|
||||
oldOrNewStorage.getMetricDescriptor(),
|
||||
descriptor,
|
||||
"Metric with same name and different descriptor already created.");
|
||||
// Else, we need to look for identity conflicts with previously registered storages
|
||||
for (MetricStorage storage : storages) {
|
||||
// Skip the newly registered storage
|
||||
if (storage == newStorage) {
|
||||
continue;
|
||||
}
|
||||
MetricDescriptor existing = storage.getMetricDescriptor();
|
||||
// Check compatibility of metrics which share the same case-insensitive name
|
||||
if (existing.getName().equalsIgnoreCase(descriptor.getName())
|
||||
&& !existing.isCompatibleWith(descriptor)) {
|
||||
logger.log(Level.WARNING, DebugUtils.duplicateMetricErrorMessage(existing, descriptor));
|
||||
break; // Only log information about the first conflict found to reduce noise
|
||||
}
|
||||
}
|
||||
// Metric already existed, and is compatible with new storage.
|
||||
return (I) oldOrNewStorage;
|
||||
// Finally, return the storage
|
||||
return oldOrNewStorage;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
class MultiWritableMetricStorage implements WriteableMetricStorage {
|
||||
private final List<WriteableMetricStorage> underlyingMetrics;
|
||||
private final List<? extends WriteableMetricStorage> underlyingMetrics;
|
||||
|
||||
MultiWritableMetricStorage(List<WriteableMetricStorage> metrics) {
|
||||
MultiWritableMetricStorage(List<? extends WriteableMetricStorage> metrics) {
|
||||
this.underlyingMetrics = metrics;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
|
||||
|
||||
import io.github.netmikey.logunit.api.LogCapturer;
|
||||
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
|
||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MetricStorageRegistry;
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
|
||||
import io.opentelemetry.sdk.metrics.view.View;
|
||||
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||
import java.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class IdentityTest {
|
||||
|
||||
@RegisterExtension
|
||||
LogCapturer logs = LogCapturer.create().captureForType(MetricStorageRegistry.class);
|
||||
|
||||
private InMemoryMetricReader reader;
|
||||
private SdkMeterProviderBuilder builder;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
reader = InMemoryMetricReader.createDelta();
|
||||
builder =
|
||||
SdkMeterProvider.builder()
|
||||
.registerMetricReader(reader)
|
||||
.setMinimumCollectionInterval(Duration.ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterSameInstrumentNoViews() {
|
||||
// Instruments are the same if their name, type, value type, description, and unit are all
|
||||
// equal.
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
meterProvider.get("meter2").counterBuilder("counter2").ofDoubles().build().add(10);
|
||||
meterProvider.get("meter2").counterBuilder("counter2").ofDoubles().build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter2")
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
meterProvider
|
||||
.get("meter3")
|
||||
.counterBuilder("counter3")
|
||||
.setDescription("description3")
|
||||
.build()
|
||||
.add(10);
|
||||
meterProvider
|
||||
.get("meter3")
|
||||
.counterBuilder("counter3")
|
||||
.setDescription("description3")
|
||||
.build()
|
||||
.add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter3"))
|
||||
.hasName("counter3")
|
||||
.hasDescription("description3")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
meterProvider.get("meter4").counterBuilder("counter4").setUnit("unit4").build().add(10);
|
||||
meterProvider.get("meter4").counterBuilder("counter4").setUnit("unit4").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter4"))
|
||||
.hasName("counter4")
|
||||
.hasUnit("unit4")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterDifferentInstrumentNoViews() {
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter2")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterSameInstrumentNoViews() {
|
||||
// Meters are the same if their name, version, and scope are all equals
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter2").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setInstrumentationVersion("version1")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", "version1", null))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setInstrumentationVersion("version1")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setInstrumentationVersion("version1")
|
||||
.setSchemaUrl("schema1")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", "version1", null))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", "version1", "schema1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterConflictingInstrumentDescriptionNoViews() {
|
||||
// Instruments with the same name but different descriptions are in conflict.
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider
|
||||
.get("meter1")
|
||||
.counterBuilder("counter1")
|
||||
.setDescription("description1")
|
||||
.build()
|
||||
.add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterConflictingInstrumentUnitNoViews() {
|
||||
// Instruments with the same name but different units are in conflict.
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").setUnit("unit1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasUnit("unit1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getUnit()).isEqualTo("1");
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterConflictingInstrumentTypeNoViews() {
|
||||
// Instruments with the same name but different types are in conflict.
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").upDownCounterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.isNotMonotonic()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.isMonotonic()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterConflictingInstrumentValueTypeNoViews() {
|
||||
// Instruments with the same name but different instrument value types are in conflict.
|
||||
SdkMeterProvider meterProvider = builder.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").ofDoubles().build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterSameInstrumentSingleView() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterSameInstrumentSingleView() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter2").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterDifferentInstrumentSingleView() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter2")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterDifferentInstrumentViewSelectingInstrumentName() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setName("counter1").build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter2")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterDifferentInstrumentViewSelectingInstrumentType() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").upDownCounterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter2")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterSameInstrumentViewSelectingMeterName() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setMeterName("meter1").build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter2").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterSameInstrumentViewSelectingMeterVersion() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setMeterVersion("version1").build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setInstrumentationVersion("version1")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setInstrumentationVersion("version2")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", "version1", null))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", "version2", null))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterSameInstrumentViewSelectingMeterSchema() {
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setMeterSchemaUrl("schema1").build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setSchemaUrl("schema1")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
meterProvider
|
||||
.meterBuilder("meter1")
|
||||
.setSchemaUrl("schema2")
|
||||
.build()
|
||||
.counterBuilder("counter1")
|
||||
.build()
|
||||
.add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", null, "schema1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(
|
||||
InstrumentationScopeInfo.create("meter1", null, "schema2"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterDifferentInstrumentViewSelectingInstrumentNameAndMeterName() {
|
||||
// A view selecting based on meter name and instrument name should not affect different
|
||||
// instruments in the selected meter, or the same instrument in a different meter.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setMeterName("meter1").setName("counter1").build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter2").build().add(10);
|
||||
meterProvider.get("meter2").upDownCounterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter2")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
},
|
||||
metricData -> {
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10));
|
||||
assertThat(metricData.getDescription()).isBlank();
|
||||
});
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterSameInstrumentConflictingViewDescriptions() {
|
||||
// Registering multiple views that select the same instrument(s) and change the description
|
||||
// produces an identity conflict as description is part of instrument identity.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description2").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description2")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)));
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterSameInstrumentConflictingViewAggregations() {
|
||||
// Registering multiple views that select the same instrument(s) and change the aggregation
|
||||
// produces an identity conflict as aggregation is part of instrument identity.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setAggregation(Aggregation.defaultAggregation()).build())
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setAggregation(Aggregation.lastValue()).build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasLongGauge()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
void sameMeterDifferentInstrumentConflictingViewName() {
|
||||
// A view that selects multiple instruments and sets the name produces an identity conflict. If
|
||||
// it could, views could be used to merge compatible instruments, which is out of scope.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setName("counter-new").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter-new")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter-new")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents())
|
||||
.allSatisfy(
|
||||
logEvent ->
|
||||
assertThat(logEvent.getMessage()).contains("Found duplicate metric definition"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentMeterDifferentInstrumentViewSetsName() {
|
||||
// A view can select multiple instruments and set the name without producing a conflict if the
|
||||
// instruments belong to different meters.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setName("counter-new").build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter2").counterBuilder("counter2").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter-new")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter2"))
|
||||
.hasName("counter-new")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameMeterDifferentInstrumentCompatibleViews() {
|
||||
// Multiple views can select the same instrument(s) without conflict if they produce instruments
|
||||
// with unique identities. For example, one view might change part of the instrument identity
|
||||
// (description, aggregation) and another might change the aggregation and name to avoid
|
||||
// identity conflicts.
|
||||
SdkMeterProvider meterProvider =
|
||||
builder
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
|
||||
View.builder().setDescription("description1").build())
|
||||
.registerView(
|
||||
InstrumentSelector.builder().setName("counter1").build(),
|
||||
View.builder()
|
||||
.setName("counter1-gauge")
|
||||
.setAggregation(Aggregation.lastValue())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
meterProvider.get("meter1").counterBuilder("counter1").build().add(10);
|
||||
|
||||
assertThat(reader.collectAllMetrics())
|
||||
.satisfiesExactlyInAnyOrder(
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1")
|
||||
.hasDescription("description1")
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(20)),
|
||||
metricData ->
|
||||
assertThat(metricData)
|
||||
.hasInstrumentationScope(forMeter("meter1"))
|
||||
.hasName("counter1-gauge")
|
||||
.hasLongGauge()
|
||||
.points()
|
||||
.satisfiesExactly(point -> assertThat(point).hasValue(10)));
|
||||
|
||||
assertThat(logs.getEvents()).hasSize(0);
|
||||
}
|
||||
|
||||
private static InstrumentationScopeInfo forMeter(String meterName) {
|
||||
return InstrumentationScopeInfo.create(meterName);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
package io.opentelemetry.sdk.metrics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.slf4j.event.Level.WARN;
|
||||
|
||||
import io.github.netmikey.logunit.api.LogCapturer;
|
||||
import io.opentelemetry.api.metrics.DoubleCounter;
|
||||
|
|
@ -17,19 +16,20 @@ import io.opentelemetry.api.metrics.LongHistogram;
|
|||
import io.opentelemetry.api.metrics.LongUpDownCounter;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MeterSharedState;
|
||||
import io.opentelemetry.sdk.metrics.internal.state.MetricStorageRegistry;
|
||||
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@SuppressLogger(MeterSharedState.class)
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
class SdkMeterTest {
|
||||
// Meter must have an exporter configured to actual run.
|
||||
private final SdkMeterProvider testMeterProvider =
|
||||
SdkMeterProvider.builder().registerMetricReader(InMemoryMetricReader.create()).build();
|
||||
private final Meter sdkMeter = testMeterProvider.get(getClass().getName());
|
||||
|
||||
@RegisterExtension LogCapturer logs = LogCapturer.create().captureForType(MeterSharedState.class);
|
||||
@RegisterExtension
|
||||
LogCapturer logs = LogCapturer.create().captureForType(MetricStorageRegistry.class);
|
||||
|
||||
@Test
|
||||
void testLongCounter() {
|
||||
|
|
@ -51,12 +51,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.counterBuilder("testLongCounter").build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -69,12 +64,7 @@ class SdkMeterTest {
|
|||
.build();
|
||||
assertThat(longCounter).isNotNull();
|
||||
sdkMeter.counterBuilder("testLongCounter".toUpperCase()).build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -98,12 +88,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.upDownCounterBuilder("testLongUpDownCounter").build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -117,12 +102,7 @@ class SdkMeterTest {
|
|||
assertThat(longUpDownCounter).isNotNull();
|
||||
assertThat(logs.getEvents()).isEmpty();
|
||||
sdkMeter.upDownCounterBuilder("testLongUpDownCounter".toUpperCase()).build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -148,12 +128,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.histogramBuilder("testLongValueRecorder").ofLongs().build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -169,12 +144,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.histogramBuilder("testLongValueRecorder".toUpperCase()).ofLongs().build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -188,12 +158,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.gaugeBuilder("longValueObserver").ofLongs().buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -207,12 +172,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.gaugeBuilder("longValueObserver".toUpperCase()).ofLongs().buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -225,12 +185,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.counterBuilder("testLongSumObserver").buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -243,12 +198,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.counterBuilder("testLongSumObserver".toUpperCase()).buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -261,12 +211,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.upDownCounterBuilder("testLongUpDownSumObserver").buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -281,12 +226,7 @@ class SdkMeterTest {
|
|||
sdkMeter
|
||||
.upDownCounterBuilder("testLongUpDownSumObserver".toUpperCase())
|
||||
.buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -311,12 +251,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.counterBuilder("testDoubleCounter").ofDoubles().build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -341,12 +276,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.upDownCounterBuilder("testDoubleUpDownCounter").ofDoubles().build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -369,12 +299,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.histogramBuilder("testDoubleValueRecorder").build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -388,12 +313,7 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
sdkMeter.counterBuilder("testDoubleSumObserver").ofDoubles().buildWithCallback(x -> {});
|
||||
sdkMeter.histogramBuilder("testDoubleValueRecorder").build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -411,12 +331,7 @@ class SdkMeterTest {
|
|||
.ofDoubles()
|
||||
.buildWithCallback(x -> {});
|
||||
sdkMeter.histogramBuilder("testDoubleValueRecorder").build();
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -429,11 +344,6 @@ class SdkMeterTest {
|
|||
assertThat(logs.getEvents()).isEmpty();
|
||||
|
||||
sdkMeter.gaugeBuilder("doubleValueObserver").buildWithCallback(x -> {});
|
||||
assertThat(
|
||||
logs.assertContains(
|
||||
loggingEvent -> loggingEvent.getLevel().equals(WARN),
|
||||
"Failed to register metric.")
|
||||
.getThrowable())
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.metrics.internal.aggregator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AggregationUtilTest {
|
||||
|
||||
@Test
|
||||
void forName() {
|
||||
assertThat(AggregationUtil.forName("sum")).isEqualTo(Aggregation.sum());
|
||||
assertThat(AggregationUtil.forName("last_value")).isEqualTo(Aggregation.lastValue());
|
||||
assertThat(AggregationUtil.forName("explicit_bucket_histogram"))
|
||||
.isEqualTo(Aggregation.explicitBucketHistogram());
|
||||
assertThat(AggregationUtil.forName("drop")).isEqualTo(Aggregation.drop());
|
||||
assertThatThrownBy(() -> AggregationUtil.forName("foo"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("Unrecognized aggregation name foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void aggregationName() {
|
||||
assertThat(AggregationUtil.aggregationName(Aggregation.defaultAggregation()))
|
||||
.isEqualTo("default");
|
||||
assertThat(AggregationUtil.aggregationName(Aggregation.sum())).isEqualTo("sum");
|
||||
assertThat(AggregationUtil.aggregationName(Aggregation.lastValue())).isEqualTo("last_value");
|
||||
assertThat(AggregationUtil.aggregationName(Aggregation.drop())).isEqualTo("drop");
|
||||
assertThat(AggregationUtil.aggregationName(Aggregation.explicitBucketHistogram()))
|
||||
.isEqualTo("explicit_bucket_histogram");
|
||||
assertThatThrownBy(() -> AggregationUtil.aggregationName(new Aggregation() {}))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("Unrecognized aggregation");
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentValueType;
|
||||
import io.opentelemetry.sdk.metrics.view.Aggregation;
|
||||
import io.opentelemetry.sdk.metrics.view.View;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
|
@ -23,9 +24,9 @@ class MetricDescriptorTest {
|
|||
MetricDescriptor simple = MetricDescriptor.create(view, instrument);
|
||||
assertThat(simple.getName()).isEqualTo("name");
|
||||
assertThat(simple.getDescription()).isEqualTo("description");
|
||||
assertThat(simple.getUnit()).isEqualTo("unit");
|
||||
assertThat(simple.getSourceView()).contains(view);
|
||||
assertThat(simple.getSourceView()).isEqualTo(view);
|
||||
assertThat(simple.getSourceInstrument()).isEqualTo(instrument);
|
||||
assertThat(simple.getAggregationName()).isEqualTo("default");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -37,24 +38,24 @@ class MetricDescriptorTest {
|
|||
MetricDescriptor simple = MetricDescriptor.create(view, instrument);
|
||||
assertThat(simple.getName()).isEqualTo("new_name");
|
||||
assertThat(simple.getDescription()).isEqualTo("new_description");
|
||||
assertThat(simple.getUnit()).isEqualTo("unit");
|
||||
assertThat(simple.getSourceInstrument()).isEqualTo(instrument);
|
||||
assertThat(simple.getSourceView()).contains(view);
|
||||
assertThat(simple.getSourceView()).isEqualTo(view);
|
||||
assertThat(simple.getAggregationName()).isEqualTo("default");
|
||||
}
|
||||
|
||||
@Test
|
||||
void metricDescriptor_isCompatible() {
|
||||
View view = View.builder().build();
|
||||
MetricDescriptor descriptor =
|
||||
MetricDescriptor.create(
|
||||
view,
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.DOUBLE));
|
||||
// Same name, description, unit, instrument type, and value type is compatible
|
||||
InstrumentDescriptor instrument =
|
||||
InstrumentDescriptor.create(
|
||||
"name", "description", "unit", InstrumentType.COUNTER, InstrumentValueType.DOUBLE);
|
||||
MetricDescriptor descriptor = MetricDescriptor.create(view, instrument);
|
||||
// Same name, description, source name, source description, source unit, source type, and source
|
||||
// value type is compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
view,
|
||||
View.builder().build(),
|
||||
InstrumentDescriptor.create(
|
||||
"name",
|
||||
"description",
|
||||
|
|
@ -62,7 +63,23 @@ class MetricDescriptorTest {
|
|||
InstrumentType.COUNTER,
|
||||
InstrumentValueType.DOUBLE))))
|
||||
.isTrue();
|
||||
// Different name is not compatible
|
||||
// Different name overridden by view is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(View.builder().setName("bar").build(), instrument)))
|
||||
.isFalse();
|
||||
// Different description overridden by view is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(View.builder().setDescription("foo").build(), instrument)))
|
||||
.isFalse();
|
||||
// Different aggregation overridden by view is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
View.builder().setAggregation(Aggregation.lastValue()).build(), instrument)))
|
||||
.isFalse();
|
||||
// Different instrument source name is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
|
|
@ -74,7 +91,7 @@ class MetricDescriptorTest {
|
|||
InstrumentType.COUNTER,
|
||||
InstrumentValueType.DOUBLE))))
|
||||
.isFalse();
|
||||
// Different description is not compatible
|
||||
// Different instrument source description is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
|
|
@ -86,7 +103,7 @@ class MetricDescriptorTest {
|
|||
InstrumentType.COUNTER,
|
||||
InstrumentValueType.DOUBLE))))
|
||||
.isFalse();
|
||||
// Different unit is not compatible
|
||||
// Different instrument source unit is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
|
|
@ -98,7 +115,7 @@ class MetricDescriptorTest {
|
|||
InstrumentType.COUNTER,
|
||||
InstrumentValueType.DOUBLE))))
|
||||
.isFalse();
|
||||
// Different instrument type is not compatible
|
||||
// Different instrument source type is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
|
|
@ -110,7 +127,7 @@ class MetricDescriptorTest {
|
|||
InstrumentType.HISTOGRAM,
|
||||
InstrumentValueType.DOUBLE))))
|
||||
.isFalse();
|
||||
// Different instrument value type is not compatible
|
||||
// Different instrument source value type is not compatible
|
||||
assertThat(
|
||||
descriptor.isCompatibleWith(
|
||||
MetricDescriptor.create(
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
package io.opentelemetry.sdk.metrics.internal.state;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.github.netmikey.logunit.api.LogCapturer;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
|
||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||
import io.opentelemetry.sdk.metrics.common.InstrumentValueType;
|
||||
|
|
@ -19,8 +20,10 @@ import io.opentelemetry.sdk.metrics.internal.export.CollectionInfo;
|
|||
import io.opentelemetry.sdk.metrics.view.View;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link MetricStorageRegistry}. */
|
||||
@SuppressLogger(MetricStorageRegistry.class)
|
||||
class MetricStorageRegistryTest {
|
||||
private static final MetricDescriptor SYNC_DESCRIPTOR =
|
||||
descriptor("sync", "description", InstrumentType.COUNTER);
|
||||
|
|
@ -31,6 +34,9 @@ class MetricStorageRegistryTest {
|
|||
private static final MetricDescriptor OTHER_ASYNC_DESCRIPTOR =
|
||||
descriptor("async", "other_description", InstrumentType.OBSERVABLE_GAUGE);
|
||||
|
||||
@RegisterExtension
|
||||
LogCapturer logs = LogCapturer.create().captureForType(MetricStorageRegistry.class);
|
||||
|
||||
private final MetricStorageRegistry metricStorageRegistry = new MetricStorageRegistry();
|
||||
|
||||
@Test
|
||||
|
|
@ -46,11 +52,10 @@ class MetricStorageRegistryTest {
|
|||
void register_SyncIncompatibleDescriptor() {
|
||||
TestMetricStorage storage = new TestMetricStorage(SYNC_DESCRIPTOR);
|
||||
assertThat(metricStorageRegistry.register(storage)).isSameAs(storage);
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> metricStorageRegistry.register(new TestMetricStorage(OTHER_SYNC_DESCRIPTOR)))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
assertThat(logs.getEvents()).isEmpty();
|
||||
assertThat(metricStorageRegistry.register(new TestMetricStorage(OTHER_SYNC_DESCRIPTOR)))
|
||||
.isNotSameAs(storage);
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -66,11 +71,10 @@ class MetricStorageRegistryTest {
|
|||
void register_AsyncIncompatibleDescriptor() {
|
||||
TestMetricStorage storage = new TestMetricStorage(ASYNC_DESCRIPTOR);
|
||||
assertThat(metricStorageRegistry.register(storage)).isSameAs(storage);
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> metricStorageRegistry.register(new TestMetricStorage(OTHER_ASYNC_DESCRIPTOR)))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("Metric with same name and different descriptor already created.");
|
||||
assertThat(logs.getEvents()).isEmpty();
|
||||
assertThat(metricStorageRegistry.register(new TestMetricStorage(OTHER_ASYNC_DESCRIPTOR)))
|
||||
.isNotSameAs(storage);
|
||||
logs.assertContains("Found duplicate metric definition");
|
||||
}
|
||||
|
||||
private static MetricDescriptor descriptor(
|
||||
|
|
|
|||
Loading…
Reference in New Issue