Skip to content

Commit 795ee0f

Browse files
authored
Add MetricRecorder implementation (#11128)
* 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
1 parent da619e2 commit 795ee0f

14 files changed

+781
-85
lines changed

api/src/main/java/io/grpc/DoubleCounterMetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
@Internal
2525
public final class DoubleCounterMetricInstrument extends PartialMetricInstrument {
26-
DoubleCounterMetricInstrument(long index, String name, String description, String unit,
26+
DoubleCounterMetricInstrument(int index, String name, String description, String unit,
2727
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
2828
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
2929
}

api/src/main/java/io/grpc/DoubleHistogramMetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public final class DoubleHistogramMetricInstrument extends PartialMetricInstrument {
2626
private final List<Double> bucketBoundaries;
2727

28-
DoubleHistogramMetricInstrument(long index, String name, String description, String unit,
28+
DoubleHistogramMetricInstrument(int index, String name, String description, String unit,
2929
List<Double> bucketBoundaries, List<String> requiredLabelKeys, List<String> optionalLabelKeys,
3030
boolean enableByDefault) {
3131
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);

api/src/main/java/io/grpc/LongCounterMetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
@Internal
2525
public final class LongCounterMetricInstrument extends PartialMetricInstrument {
26-
LongCounterMetricInstrument(long index, String name, String description, String unit,
26+
LongCounterMetricInstrument(int index, String name, String description, String unit,
2727
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
2828
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
2929
}

api/src/main/java/io/grpc/LongGaugeMetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
@Internal
2525
public final class LongGaugeMetricInstrument extends PartialMetricInstrument {
26-
LongGaugeMetricInstrument(long index, String name, String description, String unit,
26+
LongGaugeMetricInstrument(int index, String name, String description, String unit,
2727
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
2828
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);
2929
}

api/src/main/java/io/grpc/LongHistogramMetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public final class LongHistogramMetricInstrument extends PartialMetricInstrument {
2626
private final List<Long> bucketBoundaries;
2727

28-
LongHistogramMetricInstrument(long index, String name, String description, String unit,
28+
LongHistogramMetricInstrument(int index, String name, String description, String unit,
2929
List<Long> bucketBoundaries, List<String> requiredLabelKeys, List<String> optionalLabelKeys,
3030
boolean enableByDefault) {
3131
super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault);

api/src/main/java/io/grpc/MetricInstrument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public interface MetricInstrument {
2828
*
2929
* @return the index of the metric instrument.
3030
*/
31-
public long getIndex();
31+
public int getIndex();
3232

3333
/**
3434
* Returns the name of the metric.

api/src/main/java/io/grpc/MetricInstrumentRegistry.java

Lines changed: 141 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,36 @@
1616

1717
package io.grpc;
1818

19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import com.google.common.annotations.VisibleForTesting;
23+
import com.google.common.base.Strings;
24+
import java.util.Arrays;
1925
import java.util.Collections;
26+
import java.util.HashSet;
2027
import java.util.List;
2128
import java.util.Set;
22-
import java.util.concurrent.CopyOnWriteArrayList;
23-
import java.util.concurrent.CopyOnWriteArraySet;
29+
import javax.annotation.concurrent.GuardedBy;
2430

2531
/**
2632
* A registry for globally registered metric instruments.
2733
*/
2834
@Internal
2935
public final class MetricInstrumentRegistry {
36+
static final int INITIAL_INSTRUMENT_CAPACITY = 5;
3037
private static MetricInstrumentRegistry instance;
31-
private final List<MetricInstrument> metricInstruments;
32-
private final Set<String> registeredMetricNames;
38+
private final Object lock = new Object();
39+
@GuardedBy("lock")
40+
private final Set<String> registeredMetricNames = new HashSet<>();
41+
@GuardedBy("lock")
42+
private MetricInstrument[] metricInstruments =
43+
new MetricInstrument[INITIAL_INSTRUMENT_CAPACITY];
44+
@GuardedBy("lock")
45+
private int nextAvailableMetricIndex;
3346

34-
private MetricInstrumentRegistry() {
35-
this.metricInstruments = new CopyOnWriteArrayList<>();
36-
this.registeredMetricNames = new CopyOnWriteArraySet<>();
37-
}
47+
@VisibleForTesting
48+
MetricInstrumentRegistry() {}
3849

3950
/**
4051
* Returns the default metric instrument registry.
@@ -50,7 +61,10 @@ public static synchronized MetricInstrumentRegistry getDefaultRegistry() {
5061
* Returns a list of registered metric instruments.
5162
*/
5263
public List<MetricInstrument> getMetricInstruments() {
53-
return Collections.unmodifiableList(metricInstruments);
64+
synchronized (lock) {
65+
return Collections.unmodifiableList(
66+
Arrays.asList(Arrays.copyOfRange(metricInstruments, 0, nextAvailableMetricIndex)));
67+
}
5468
}
5569

5670
/**
@@ -65,20 +79,30 @@ public List<MetricInstrument> getMetricInstruments() {
6579
* @return the newly created DoubleCounterMetricInstrument
6680
* @throws IllegalStateException if a metric with the same name already exists
6781
*/
68-
// TODO(dnvindhya): Evaluate locks over synchronized methods and update if needed
69-
public synchronized DoubleCounterMetricInstrument registerDoubleCounter(String name,
82+
public DoubleCounterMetricInstrument registerDoubleCounter(String name,
7083
String description, String unit, List<String> requiredLabelKeys,
7184
List<String> optionalLabelKeys, boolean enableByDefault) {
72-
if (registeredMetricNames.contains(name)) {
73-
throw new IllegalStateException("Metric with name " + name + " already exists");
85+
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
86+
checkNotNull(description, "description");
87+
checkNotNull(unit, "unit");
88+
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
89+
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
90+
synchronized (lock) {
91+
if (registeredMetricNames.contains(name)) {
92+
throw new IllegalStateException("Metric with name " + name + " already exists");
93+
}
94+
int index = nextAvailableMetricIndex;
95+
if (index + 1 == metricInstruments.length) {
96+
resizeMetricInstruments();
97+
}
98+
DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
99+
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
100+
enableByDefault);
101+
metricInstruments[index] = instrument;
102+
registeredMetricNames.add(name);
103+
nextAvailableMetricIndex += 1;
104+
return instrument;
74105
}
75-
long instrumentIndex = metricInstruments.size();
76-
DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
77-
instrumentIndex, name, description, unit, requiredLabelKeys, optionalLabelKeys,
78-
enableByDefault);
79-
metricInstruments.add(instrument);
80-
registeredMetricNames.add(name);
81-
return instrument;
82106
}
83107

84108
/**
@@ -93,20 +117,30 @@ public synchronized DoubleCounterMetricInstrument registerDoubleCounter(String n
93117
* @return the newly created LongCounterMetricInstrument
94118
* @throws IllegalStateException if a metric with the same name already exists
95119
*/
96-
public synchronized LongCounterMetricInstrument registerLongCounter(String name,
120+
public LongCounterMetricInstrument registerLongCounter(String name,
97121
String description, String unit, List<String> requiredLabelKeys,
98122
List<String> optionalLabelKeys, boolean enableByDefault) {
99-
if (registeredMetricNames.contains(name)) {
100-
throw new IllegalStateException("Metric with name " + name + " already exists");
123+
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
124+
checkNotNull(description, "description");
125+
checkNotNull(unit, "unit");
126+
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
127+
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
128+
synchronized (lock) {
129+
if (registeredMetricNames.contains(name)) {
130+
throw new IllegalStateException("Metric with name " + name + " already exists");
131+
}
132+
int index = nextAvailableMetricIndex;
133+
if (index + 1 == metricInstruments.length) {
134+
resizeMetricInstruments();
135+
}
136+
LongCounterMetricInstrument instrument = new LongCounterMetricInstrument(
137+
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
138+
enableByDefault);
139+
metricInstruments[index] = instrument;
140+
registeredMetricNames.add(name);
141+
nextAvailableMetricIndex += 1;
142+
return instrument;
101143
}
102-
// Acquire lock?
103-
long instrumentIndex = metricInstruments.size();
104-
LongCounterMetricInstrument instrument = new LongCounterMetricInstrument(
105-
instrumentIndex, name, description, unit, requiredLabelKeys, optionalLabelKeys,
106-
enableByDefault);
107-
metricInstruments.add(instrument);
108-
registeredMetricNames.add(name);
109-
return instrument;
110144
}
111145

112146
/**
@@ -122,20 +156,32 @@ public synchronized LongCounterMetricInstrument registerLongCounter(String name,
122156
* @return the newly created DoubleHistogramMetricInstrument
123157
* @throws IllegalStateException if a metric with the same name already exists
124158
*/
125-
public synchronized DoubleHistogramMetricInstrument registerDoubleHistogram(String name,
159+
public DoubleHistogramMetricInstrument registerDoubleHistogram(String name,
126160
String description, String unit, List<Double> bucketBoundaries,
127161
List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
128-
if (registeredMetricNames.contains(name)) {
129-
throw new IllegalStateException("Metric with name " + name + " already exists");
162+
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
163+
checkNotNull(description, "description");
164+
checkNotNull(unit, "unit");
165+
checkNotNull(bucketBoundaries, "bucketBoundaries");
166+
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
167+
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
168+
synchronized (lock) {
169+
if (registeredMetricNames.contains(name)) {
170+
throw new IllegalStateException("Metric with name " + name + " already exists");
171+
}
172+
int index = nextAvailableMetricIndex;
173+
if (index + 1 == metricInstruments.length) {
174+
resizeMetricInstruments();
175+
}
176+
DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument(
177+
index, name, description, unit, bucketBoundaries, requiredLabelKeys,
178+
optionalLabelKeys,
179+
enableByDefault);
180+
metricInstruments[index] = instrument;
181+
registeredMetricNames.add(name);
182+
nextAvailableMetricIndex += 1;
183+
return instrument;
130184
}
131-
long indexToInsertInstrument = metricInstruments.size();
132-
DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument(
133-
indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys,
134-
optionalLabelKeys,
135-
enableByDefault);
136-
metricInstruments.add(instrument);
137-
registeredMetricNames.add(name);
138-
return instrument;
139185
}
140186

141187
/**
@@ -151,20 +197,32 @@ public synchronized DoubleHistogramMetricInstrument registerDoubleHistogram(Stri
151197
* @return the newly created LongHistogramMetricInstrument
152198
* @throws IllegalStateException if a metric with the same name already exists
153199
*/
154-
public synchronized LongHistogramMetricInstrument registerLongHistogram(String name,
200+
public LongHistogramMetricInstrument registerLongHistogram(String name,
155201
String description, String unit, List<Long> bucketBoundaries, List<String> requiredLabelKeys,
156202
List<String> optionalLabelKeys, boolean enableByDefault) {
157-
if (registeredMetricNames.contains(name)) {
158-
throw new IllegalStateException("Metric with name " + name + " already exists");
203+
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
204+
checkNotNull(description, "description");
205+
checkNotNull(unit, "unit");
206+
checkNotNull(bucketBoundaries, "bucketBoundaries");
207+
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
208+
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
209+
synchronized (lock) {
210+
if (registeredMetricNames.contains(name)) {
211+
throw new IllegalStateException("Metric with name " + name + " already exists");
212+
}
213+
int index = nextAvailableMetricIndex;
214+
if (index + 1 == metricInstruments.length) {
215+
resizeMetricInstruments();
216+
}
217+
LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
218+
index, name, description, unit, bucketBoundaries, requiredLabelKeys,
219+
optionalLabelKeys,
220+
enableByDefault);
221+
metricInstruments[index] = instrument;
222+
registeredMetricNames.add(name);
223+
nextAvailableMetricIndex += 1;
224+
return instrument;
159225
}
160-
long indexToInsertInstrument = metricInstruments.size();
161-
LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
162-
indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys,
163-
optionalLabelKeys,
164-
enableByDefault);
165-
metricInstruments.add(instrument);
166-
registeredMetricNames.add(name);
167-
return instrument;
168226
}
169227

170228

@@ -180,18 +238,38 @@ public synchronized LongHistogramMetricInstrument registerLongHistogram(String n
180238
* @return the newly created LongGaugeMetricInstrument
181239
* @throws IllegalStateException if a metric with the same name already exists
182240
*/
183-
public synchronized LongGaugeMetricInstrument registerLongGauge(String name, String description,
241+
public LongGaugeMetricInstrument registerLongGauge(String name, String description,
184242
String unit, List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean
185243
enableByDefault) {
186-
if (registeredMetricNames.contains(name)) {
187-
throw new IllegalStateException("Metric with name " + name + " already exists");
244+
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
245+
checkNotNull(description, "description");
246+
checkNotNull(unit, "unit");
247+
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
248+
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
249+
synchronized (lock) {
250+
if (registeredMetricNames.contains(name)) {
251+
throw new IllegalStateException("Metric with name " + name + " already exists");
252+
}
253+
int index = nextAvailableMetricIndex;
254+
if (index + 1 == metricInstruments.length) {
255+
resizeMetricInstruments();
256+
}
257+
LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
258+
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
259+
enableByDefault);
260+
metricInstruments[index] = instrument;
261+
registeredMetricNames.add(name);
262+
nextAvailableMetricIndex += 1;
263+
return instrument;
188264
}
189-
long indexToInsertInstrument = metricInstruments.size();
190-
LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
191-
indexToInsertInstrument, name, description, unit, requiredLabelKeys, optionalLabelKeys,
192-
enableByDefault);
193-
metricInstruments.add(instrument);
194-
registeredMetricNames.add(name);
195-
return instrument;
265+
}
266+
267+
@GuardedBy("lock")
268+
private void resizeMetricInstruments() {
269+
// Increase the capacity of the metricInstruments array by INITIAL_INSTRUMENT_CAPACITY
270+
int newInstrumentsCapacity = metricInstruments.length + INITIAL_INSTRUMENT_CAPACITY;
271+
MetricInstrument[] resizedMetricInstruments = Arrays.copyOf(metricInstruments,
272+
newInstrumentsCapacity);
273+
metricInstruments = resizedMetricInstruments;
196274
}
197275
}

api/src/main/java/io/grpc/MetricRecorder.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616

1717
package io.grpc;
1818

19-
import io.grpc.DoubleCounterMetricInstrument;
20-
import io.grpc.DoubleHistogramMetricInstrument;
21-
import io.grpc.Internal;
22-
import io.grpc.LongCounterMetricInstrument;
23-
import io.grpc.LongHistogramMetricInstrument;
2419
import java.util.List;
2520

2621
/**
@@ -30,25 +25,25 @@
3025
@Internal
3126
public interface MetricRecorder {
3227
/**
33-
* Records a value for a double-precision counter metric instrument.
28+
* Adds a value for a double-precision counter metric instrument.
3429
*
35-
* @param metricInstrument The counter metric instrument to record the value against.
36-
* @param value The value to record.
30+
* @param metricInstrument The counter metric instrument to add the value against.
31+
* @param value The value to add.
3732
* @param requiredLabelValues A list of required label values for the metric.
3833
* @param optionalLabelValues A list of additional, optional label values for the metric.
3934
*/
40-
default void recordDoubleCounter(DoubleCounterMetricInstrument metricInstrument, double value,
35+
default void addDoubleCounter(DoubleCounterMetricInstrument metricInstrument, double value,
4136
List<String> requiredLabelValues, List<String> optionalLabelValues) {}
4237

4338
/**
44-
* Records a value for a long valued counter metric instrument.
39+
* Adds a value for a long valued counter metric instrument.
4540
*
46-
* @param metricInstrument The counter metric instrument to record the value against.
47-
* @param value The value to record.
41+
* @param metricInstrument The counter metric instrument to add the value against.
42+
* @param value The value to add.
4843
* @param requiredLabelValues A list of required label values for the metric.
4944
* @param optionalLabelValues A list of additional, optional label values for the metric.
5045
*/
51-
default void recordLongCounter(LongCounterMetricInstrument metricInstrument, long value,
46+
default void addLongCounter(LongCounterMetricInstrument metricInstrument, long value,
5247
List<String> requiredLabelValues, List<String> optionalLabelValues) {}
5348

5449
/**

api/src/main/java/io/grpc/MetricSink.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,6 @@ default void recordDoubleHistogram(DoubleHistogramMetricInstrument metricInstrum
9898
default void recordLongHistogram(LongHistogramMetricInstrument metricInstrument, long value,
9999
List<String> requiredLabelValues, List<String> optionalLabelValues) {
100100
}
101+
102+
default void updateMeasures(List<MetricInstrument> instruments) {}
101103
}

0 commit comments

Comments
 (0)