• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19177

26 Apr 2024 08:47PM UTC coverage: 88.288% (+0.2%) from 88.068%
#19177

push

github

web-flow
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

31381 of 35544 relevant lines covered (88.29%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.71
/../api/src/main/java/io/grpc/MetricInstrumentRegistry.java
1
/*
2
 * Copyright 2024 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc;
18

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;
25
import java.util.Collections;
26
import java.util.HashSet;
27
import java.util.List;
28
import java.util.Set;
29
import javax.annotation.concurrent.GuardedBy;
30

31
/**
32
 * A registry for globally registered metric instruments.
33
 */
34
@Internal
35
public final class MetricInstrumentRegistry {
36
  static final int INITIAL_INSTRUMENT_CAPACITY = 5;
37
  private static MetricInstrumentRegistry instance;
38
  private final Object lock = new Object();
1✔
39
  @GuardedBy("lock")
1✔
40
  private final Set<String> registeredMetricNames = new HashSet<>();
41
  @GuardedBy("lock")
1✔
42
  private MetricInstrument[] metricInstruments =
43
      new MetricInstrument[INITIAL_INSTRUMENT_CAPACITY];
44
  @GuardedBy("lock")
45
  private int nextAvailableMetricIndex;
46

47
  @VisibleForTesting
48
  MetricInstrumentRegistry() {}
1✔
49

50
  /**
51
   * Returns the default metric instrument registry.
52
   */
53
  public static synchronized MetricInstrumentRegistry getDefaultRegistry() {
54
    if (instance == null) {
×
55
      instance = new MetricInstrumentRegistry();
×
56
    }
57
    return instance;
×
58
  }
59

60
  /**
61
   * Returns a list of registered metric instruments.
62
   */
63
  public List<MetricInstrument> getMetricInstruments() {
64
    synchronized (lock) {
1✔
65
      return Collections.unmodifiableList(
1✔
66
          Arrays.asList(Arrays.copyOfRange(metricInstruments, 0, nextAvailableMetricIndex)));
1✔
67
    }
68
  }
69

70
  /**
71
   * Registers a new Double Counter metric instrument.
72
   *
73
   * @param name the name of the metric
74
   * @param description a description of the metric
75
   * @param unit the unit of measurement for the metric
76
   * @param requiredLabelKeys a list of required label keys
77
   * @param optionalLabelKeys a list of optional label keys
78
   * @param enableByDefault whether the metric should be enabled by default
79
   * @return the newly created DoubleCounterMetricInstrument
80
   * @throws IllegalStateException if a metric with the same name already exists
81
   */
82
  public DoubleCounterMetricInstrument registerDoubleCounter(String name,
83
      String description, String unit, List<String> requiredLabelKeys,
84
      List<String> optionalLabelKeys, boolean enableByDefault) {
85
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
86
    checkNotNull(description, "description");
1✔
87
    checkNotNull(unit, "unit");
1✔
88
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
89
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
90
    synchronized (lock) {
1✔
91
      if (registeredMetricNames.contains(name)) {
1✔
92
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
93
      }
94
      int index = nextAvailableMetricIndex;
1✔
95
      if (index + 1 == metricInstruments.length) {
1✔
96
        resizeMetricInstruments();
×
97
      }
98
      DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
1✔
99
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
100
          enableByDefault);
101
      metricInstruments[index] = instrument;
1✔
102
      registeredMetricNames.add(name);
1✔
103
      nextAvailableMetricIndex += 1;
1✔
104
      return instrument;
1✔
105
    }
106
  }
107

108
  /**
109
   * Registers a new Long Counter metric instrument.
110
   *
111
   * @param name the name of the metric
112
   * @param description a description of the metric
113
   * @param unit the unit of measurement for the metric
114
   * @param requiredLabelKeys a list of required label keys
115
   * @param optionalLabelKeys a list of optional label keys
116
   * @param enableByDefault whether the metric should be enabled by default
117
   * @return the newly created LongCounterMetricInstrument
118
   * @throws IllegalStateException if a metric with the same name already exists
119
   */
120
  public LongCounterMetricInstrument registerLongCounter(String name,
121
      String description, String unit, List<String> requiredLabelKeys,
122
      List<String> optionalLabelKeys, boolean enableByDefault) {
123
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
124
    checkNotNull(description, "description");
1✔
125
    checkNotNull(unit, "unit");
1✔
126
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
127
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
128
    synchronized (lock) {
1✔
129
      if (registeredMetricNames.contains(name)) {
1✔
130
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
131
      }
132
      int index = nextAvailableMetricIndex;
1✔
133
      if (index + 1 == metricInstruments.length) {
1✔
134
        resizeMetricInstruments();
×
135
      }
136
      LongCounterMetricInstrument instrument = new LongCounterMetricInstrument(
1✔
137
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
138
          enableByDefault);
139
      metricInstruments[index] = instrument;
1✔
140
      registeredMetricNames.add(name);
1✔
141
      nextAvailableMetricIndex += 1;
1✔
142
      return instrument;
1✔
143
    }
144
  }
145

146
  /**
147
   * Registers a new Double Histogram metric instrument.
148
   *
149
   * @param name the name of the metric
150
   * @param description a description of the metric
151
   * @param unit the unit of measurement for the metric
152
   * @param bucketBoundaries recommended set of explicit bucket boundaries for the histogram
153
   * @param requiredLabelKeys a list of required label keys
154
   * @param optionalLabelKeys a list of optional label keys
155
   * @param enableByDefault whether the metric should be enabled by default
156
   * @return the newly created DoubleHistogramMetricInstrument
157
   * @throws IllegalStateException if a metric with the same name already exists
158
   */
159
  public DoubleHistogramMetricInstrument registerDoubleHistogram(String name,
160
      String description, String unit, List<Double> bucketBoundaries,
161
      List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean enableByDefault) {
162
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
163
    checkNotNull(description, "description");
1✔
164
    checkNotNull(unit, "unit");
1✔
165
    checkNotNull(bucketBoundaries, "bucketBoundaries");
1✔
166
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
167
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
168
    synchronized (lock) {
1✔
169
      if (registeredMetricNames.contains(name)) {
1✔
170
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
171
      }
172
      int index = nextAvailableMetricIndex;
1✔
173
      if (index + 1 == metricInstruments.length) {
1✔
174
        resizeMetricInstruments();
×
175
      }
176
      DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument(
1✔
177
          index, name, description, unit, bucketBoundaries, requiredLabelKeys,
178
          optionalLabelKeys,
179
          enableByDefault);
180
      metricInstruments[index] = instrument;
1✔
181
      registeredMetricNames.add(name);
1✔
182
      nextAvailableMetricIndex += 1;
1✔
183
      return instrument;
1✔
184
    }
185
  }
186

187
  /**
188
   * Registers a new Long Histogram metric instrument.
189
   *
190
   * @param name the name of the metric
191
   * @param description a description of the metric
192
   * @param unit the unit of measurement for the metric
193
   * @param bucketBoundaries recommended set of explicit bucket boundaries for the histogram
194
   * @param requiredLabelKeys a list of required label keys
195
   * @param optionalLabelKeys a list of optional label keys
196
   * @param enableByDefault whether the metric should be enabled by default
197
   * @return the newly created LongHistogramMetricInstrument
198
   * @throws IllegalStateException if a metric with the same name already exists
199
   */
200
  public LongHistogramMetricInstrument registerLongHistogram(String name,
201
      String description, String unit, List<Long> bucketBoundaries, List<String> requiredLabelKeys,
202
      List<String> optionalLabelKeys, boolean enableByDefault) {
203
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
204
    checkNotNull(description, "description");
1✔
205
    checkNotNull(unit, "unit");
1✔
206
    checkNotNull(bucketBoundaries, "bucketBoundaries");
1✔
207
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
208
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
209
    synchronized (lock) {
1✔
210
      if (registeredMetricNames.contains(name)) {
1✔
211
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
212
      }
213
      int index = nextAvailableMetricIndex;
1✔
214
      if (index + 1 == metricInstruments.length) {
1✔
215
        resizeMetricInstruments();
1✔
216
      }
217
      LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
1✔
218
          index, name, description, unit, bucketBoundaries, requiredLabelKeys,
219
          optionalLabelKeys,
220
          enableByDefault);
221
      metricInstruments[index] = instrument;
1✔
222
      registeredMetricNames.add(name);
1✔
223
      nextAvailableMetricIndex += 1;
1✔
224
      return instrument;
1✔
225
    }
226
  }
227

228

229
  /**
230
   * Registers a new Long Gauge metric instrument.
231
   *
232
   * @param name the name of the metric
233
   * @param description a description of the metric
234
   * @param unit the unit of measurement for the metric
235
   * @param requiredLabelKeys a list of required label keys
236
   * @param optionalLabelKeys a list of optional label keys
237
   * @param enableByDefault whether the metric should be enabled by default
238
   * @return the newly created LongGaugeMetricInstrument
239
   * @throws IllegalStateException if a metric with the same name already exists
240
   */
241
  public LongGaugeMetricInstrument registerLongGauge(String name, String description,
242
      String unit, List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean
243
      enableByDefault) {
244
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
245
    checkNotNull(description, "description");
1✔
246
    checkNotNull(unit, "unit");
1✔
247
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
248
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
249
    synchronized (lock) {
1✔
250
      if (registeredMetricNames.contains(name)) {
1✔
251
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
252
      }
253
      int index = nextAvailableMetricIndex;
1✔
254
      if (index + 1 == metricInstruments.length) {
1✔
255
        resizeMetricInstruments();
×
256
      }
257
      LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
1✔
258
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
259
          enableByDefault);
260
      metricInstruments[index] = instrument;
1✔
261
      registeredMetricNames.add(name);
1✔
262
      nextAvailableMetricIndex += 1;
1✔
263
      return instrument;
1✔
264
    }
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;
1✔
271
    MetricInstrument[] resizedMetricInstruments = Arrays.copyOf(metricInstruments,
1✔
272
        newInstrumentsCapacity);
273
    metricInstruments = resizedMetricInstruments;
1✔
274
  }
1✔
275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc