Skip to content

Commit

Permalink
Add MetricRecorder implementation (#11128)
Browse files Browse the repository at this point in the history
* added MetricRecorderImpl and unit tests for MetricInstrumentRegistry

* updated MetricInstrumentRegistry to use array instead of ArrayList

* renamed record<>Counter APIs to add<>Counter. Added check for mismatched label values

* added lock for instruments array
  • Loading branch information
DNVindhya committed Apr 26, 2024
1 parent da619e2 commit 795ee0f
Show file tree
Hide file tree
Showing 14 changed files with 781 additions and 85 deletions.
Expand Up @@ -23,7 +23,7 @@
*/
@Internal
public final class DoubleCounterMetricInstrument extends PartialMetricInstrument {
DoubleCounterMetricInstrument(long index, String name, String description, String unit,
DoubleCounterMetricInstrument(int index, String name, String description, String unit,
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
}
Expand Down
Expand Up @@ -25,7 +25,7 @@
public final class DoubleHistogramMetricInstrument extends PartialMetricInstrument {
private final List<Double> bucketBoundaries;

DoubleHistogramMetricInstrument(long index, String name, String description, String unit,
DoubleHistogramMetricInstrument(int index, String name, String description, String unit,
List<Double> bucketBoundaries, List<String> requiredLabelKeys, List<String> optionalLabelKeys,
boolean enableByDefault) {
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/grpc/LongCounterMetricInstrument.java
Expand Up @@ -23,7 +23,7 @@
*/
@Internal
public final class LongCounterMetricInstrument extends PartialMetricInstrument {
LongCounterMetricInstrument(long index, String name, String description, String unit,
LongCounterMetricInstrument(int index, String name, String description, String unit,
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
}
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/grpc/LongGaugeMetricInstrument.java
Expand Up @@ -23,7 +23,7 @@
*/
@Internal
public final class LongGaugeMetricInstrument extends PartialMetricInstrument {
LongGaugeMetricInstrument(long index, String name, String description, String unit,
LongGaugeMetricInstrument(int index, String name, String description, String unit,
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
}
Expand Down
Expand Up @@ -25,7 +25,7 @@
public final class LongHistogramMetricInstrument extends PartialMetricInstrument {
private final List<Long> bucketBoundaries;

LongHistogramMetricInstrument(long index, String name, String description, String unit,
LongHistogramMetricInstrument(int index, String name, String description, String unit,
List<Long> bucketBoundaries, List<String> requiredLabelKeys, List<String> optionalLabelKeys,
boolean enableByDefault) {
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/grpc/MetricInstrument.java
Expand Up @@ -28,7 +28,7 @@ public interface MetricInstrument {
*
* @return the index of the metric instrument.
*/
public long getIndex();
public int getIndex();

/**
* Returns the name of the metric.
Expand Down
204 changes: 141 additions & 63 deletions api/src/main/java/io/grpc/MetricInstrumentRegistry.java
Expand Up @@ -16,25 +16,36 @@

package io.grpc;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.concurrent.GuardedBy;

/**
* A registry for globally registered metric instruments.
*/
@Internal
public final class MetricInstrumentRegistry {
static final int INITIAL_INSTRUMENT_CAPACITY = 5;
private static MetricInstrumentRegistry instance;
private final List<MetricInstrument> metricInstruments;
private final Set<String> registeredMetricNames;
private final Object lock = new Object();
@GuardedBy("lock")
private final Set<String> registeredMetricNames = new HashSet<>();
@GuardedBy("lock")
private MetricInstrument[] metricInstruments =
new MetricInstrument[INITIAL_INSTRUMENT_CAPACITY];
@GuardedBy("lock")
private int nextAvailableMetricIndex;

private MetricInstrumentRegistry() {
this.metricInstruments = new CopyOnWriteArrayList<>();
this.registeredMetricNames = new CopyOnWriteArraySet<>();
}
@VisibleForTesting
MetricInstrumentRegistry() {}

/**
* Returns the default metric instrument registry.
Expand All @@ -50,7 +61,10 @@ public static synchronized MetricInstrumentRegistry getDefaultRegistry() {
* Returns a list of registered metric instruments.
*/
public List<MetricInstrument> getMetricInstruments() {
return Collections.unmodifiableList(metricInstruments);
synchronized (lock) {
return Collections.unmodifiableList(
Arrays.asList(Arrays.copyOfRange(metricInstruments, 0, nextAvailableMetricIndex)));
}
}

/**
Expand All @@ -65,20 +79,30 @@ public List<MetricInstrument> getMetricInstruments() {
* @return the newly created DoubleCounterMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
// TODO(dnvindhya): Evaluate locks over synchronized methods and update if needed
public synchronized DoubleCounterMetricInstrument registerDoubleCounter(String name,
public DoubleCounterMetricInstrument registerDoubleCounter(String name,
String description, String unit, List<String> requiredLabelKeys,
List<String> optionalLabelKeys, boolean enableByDefault) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
long instrumentIndex = metricInstruments.size();
DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
instrumentIndex, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments.add(instrument);
registeredMetricNames.add(name);
return instrument;
}

/**
Expand All @@ -93,20 +117,30 @@ public synchronized DoubleCounterMetricInstrument registerDoubleCounter(String n
* @return the newly created LongCounterMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
public synchronized LongCounterMetricInstrument registerLongCounter(String name,
public LongCounterMetricInstrument registerLongCounter(String name,
String description, String unit, List<String> requiredLabelKeys,
List<String> optionalLabelKeys, boolean enableByDefault) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
LongCounterMetricInstrument instrument = new LongCounterMetricInstrument(
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
// Acquire lock?
long instrumentIndex = metricInstruments.size();
LongCounterMetricInstrument instrument = new LongCounterMetricInstrument(
instrumentIndex, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments.add(instrument);
registeredMetricNames.add(name);
return instrument;
}

/**
Expand All @@ -122,20 +156,32 @@ public synchronized LongCounterMetricInstrument registerLongCounter(String name,
* @return the newly created DoubleHistogramMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
public synchronized DoubleHistogramMetricInstrument registerDoubleHistogram(String name,
public DoubleHistogramMetricInstrument registerDoubleHistogram(String name,
String description, String unit, List<Double> bucketBoundaries,
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(bucketBoundaries, "bucketBoundaries");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument(
index, name, description, unit, bucketBoundaries, requiredLabelKeys,
optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
long indexToInsertInstrument = metricInstruments.size();
DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument(
indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys,
optionalLabelKeys,
enableByDefault);
metricInstruments.add(instrument);
registeredMetricNames.add(name);
return instrument;
}

/**
Expand All @@ -151,20 +197,32 @@ public synchronized DoubleHistogramMetricInstrument registerDoubleHistogram(Stri
* @return the newly created LongHistogramMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
public synchronized LongHistogramMetricInstrument registerLongHistogram(String name,
public LongHistogramMetricInstrument registerLongHistogram(String name,
String description, String unit, List<Long> bucketBoundaries, List<String> requiredLabelKeys,
List<String> optionalLabelKeys, boolean enableByDefault) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(bucketBoundaries, "bucketBoundaries");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
index, name, description, unit, bucketBoundaries, requiredLabelKeys,
optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
long indexToInsertInstrument = metricInstruments.size();
LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys,
optionalLabelKeys,
enableByDefault);
metricInstruments.add(instrument);
registeredMetricNames.add(name);
return instrument;
}


Expand All @@ -180,18 +238,38 @@ public synchronized LongHistogramMetricInstrument registerLongHistogram(String n
* @return the newly created LongGaugeMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
public synchronized LongGaugeMetricInstrument registerLongGauge(String name, String description,
public LongGaugeMetricInstrument registerLongGauge(String name, String description,
String unit, List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean
enableByDefault) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
long indexToInsertInstrument = metricInstruments.size();
LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
indexToInsertInstrument, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments.add(instrument);
registeredMetricNames.add(name);
return instrument;
}

@GuardedBy("lock")
private void resizeMetricInstruments() {
// Increase the capacity of the metricInstruments array by INITIAL_INSTRUMENT_CAPACITY
int newInstrumentsCapacity = metricInstruments.length + INITIAL_INSTRUMENT_CAPACITY;
MetricInstrument[] resizedMetricInstruments = Arrays.copyOf(metricInstruments,
newInstrumentsCapacity);
metricInstruments = resizedMetricInstruments;
}
}
21 changes: 8 additions & 13 deletions api/src/main/java/io/grpc/MetricRecorder.java
Expand Up @@ -16,11 +16,6 @@

package io.grpc;

import io.grpc.DoubleCounterMetricInstrument;
import io.grpc.DoubleHistogramMetricInstrument;
import io.grpc.Internal;
import io.grpc.LongCounterMetricInstrument;
import io.grpc.LongHistogramMetricInstrument;
import java.util.List;

/**
Expand All @@ -30,25 +25,25 @@
@Internal
public interface MetricRecorder {
/**
* Records a value for a double-precision counter metric instrument.
* Adds a value for a double-precision counter metric instrument.
*
* @param metricInstrument The counter metric instrument to record the value against.
* @param value The value to record.
* @param metricInstrument The counter metric instrument to add the value against.
* @param value The value to add.
* @param requiredLabelValues A list of required label values for the metric.
* @param optionalLabelValues A list of additional, optional label values for the metric.
*/
default void recordDoubleCounter(DoubleCounterMetricInstrument metricInstrument, double value,
default void addDoubleCounter(DoubleCounterMetricInstrument metricInstrument, double value,
List<String> requiredLabelValues, List<String> optionalLabelValues) {}

/**
* Records a value for a long valued counter metric instrument.
* Adds a value for a long valued counter metric instrument.
*
* @param metricInstrument The counter metric instrument to record the value against.
* @param value The value to record.
* @param metricInstrument The counter metric instrument to add the value against.
* @param value The value to add.
* @param requiredLabelValues A list of required label values for the metric.
* @param optionalLabelValues A list of additional, optional label values for the metric.
*/
default void recordLongCounter(LongCounterMetricInstrument metricInstrument, long value,
default void addLongCounter(LongCounterMetricInstrument metricInstrument, long value,
List<String> requiredLabelValues, List<String> optionalLabelValues) {}

/**
Expand Down
2 changes: 2 additions & 0 deletions api/src/main/java/io/grpc/MetricSink.java
Expand Up @@ -98,4 +98,6 @@ default void recordDoubleHistogram(DoubleHistogramMetricInstrument metricInstrum
default void recordLongHistogram(LongHistogramMetricInstrument metricInstrument, long value,
List<String> requiredLabelValues, List<String> optionalLabelValues) {
}

default void updateMeasures(List<MetricInstrument> instruments) {}
}

0 comments on commit 795ee0f

Please sign in to comment.