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

grpc / grpc-java / #19975

08 Sep 2025 09:55PM UTC coverage: 88.547% (+0.01%) from 88.535%
#19975

push

github

web-flow
otel: subchannel metrics A94 (#12202)

Implements [A94](https://github.com/grpc/proposal/pull/485/files) except for the exact reason for disconnect_error

34806 of 39308 relevant lines covered (88.55%)

0.89 hits per line

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

96.43
/../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 com.google.errorprone.annotations.concurrent.GuardedBy;
25
import java.util.Arrays;
26
import java.util.Collections;
27
import java.util.HashSet;
28
import java.util.List;
29
import java.util.Set;
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) {
1✔
55
      instance = new MetricInstrumentRegistry();
1✔
56
    }
57
    return instance;
1✔
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
      // TODO(dnvindhya): add limit for number of optional labels allowed
99
      DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
1✔
100
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
101
          enableByDefault);
102
      metricInstruments[index] = instrument;
1✔
103
      registeredMetricNames.add(name);
1✔
104
      nextAvailableMetricIndex += 1;
1✔
105
      return instrument;
1✔
106
    }
107
  }
108

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

147
  /**
148
   * Registers a new Long Up Down Counter metric instrument.
149
   *
150
   * @param name              the name of the metric
151
   * @param description       a description of the metric
152
   * @param unit              the unit of measurement for the metric
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 LongUpDownCounterMetricInstrument
157
   * @throws IllegalStateException if a metric with the same name already exists
158
   */
159
  public LongUpDownCounterMetricInstrument registerLongUpDownCounter(String name,
160
                                                                     String description,
161
                                                                     String unit,
162
                                                                     List<String> requiredLabelKeys,
163
                                                                     List<String> optionalLabelKeys,
164
                                                                     boolean enableByDefault) {
165
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
166
    checkNotNull(description, "description");
1✔
167
    checkNotNull(unit, "unit");
1✔
168
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
169
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
170
    synchronized (lock) {
1✔
171
      if (registeredMetricNames.contains(name)) {
1✔
172
        throw new IllegalStateException("Metric with name " + name + " already exists");
×
173
      }
174
      int index = nextAvailableMetricIndex;
1✔
175
      if (index + 1 == metricInstruments.length) {
1✔
176
        resizeMetricInstruments();
×
177
      }
178
      LongUpDownCounterMetricInstrument instrument = new LongUpDownCounterMetricInstrument(
1✔
179
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
180
          enableByDefault);
181
      metricInstruments[index] = instrument;
1✔
182
      registeredMetricNames.add(name);
1✔
183
      nextAvailableMetricIndex += 1;
1✔
184
      return instrument;
1✔
185
    }
186
  }
187

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

229
  /**
230
   * Registers a new Long Histogram 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 bucketBoundaries recommended set of explicit bucket boundaries for the histogram
236
   * @param requiredLabelKeys a list of required label keys
237
   * @param optionalLabelKeys a list of optional label keys
238
   * @param enableByDefault whether the metric should be enabled by default
239
   * @return the newly created LongHistogramMetricInstrument
240
   * @throws IllegalStateException if a metric with the same name already exists
241
   */
242
  public LongHistogramMetricInstrument registerLongHistogram(String name,
243
      String description, String unit, List<Long> bucketBoundaries, List<String> requiredLabelKeys,
244
      List<String> optionalLabelKeys, boolean enableByDefault) {
245
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
246
    checkNotNull(description, "description");
1✔
247
    checkNotNull(unit, "unit");
1✔
248
    checkNotNull(bucketBoundaries, "bucketBoundaries");
1✔
249
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
250
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
251
    synchronized (lock) {
1✔
252
      if (registeredMetricNames.contains(name)) {
1✔
253
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
254
      }
255
      int index = nextAvailableMetricIndex;
1✔
256
      if (index + 1 == metricInstruments.length) {
1✔
257
        resizeMetricInstruments();
1✔
258
      }
259
      LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument(
1✔
260
          index, name, description, unit, bucketBoundaries, requiredLabelKeys,
261
          optionalLabelKeys,
262
          enableByDefault);
263
      metricInstruments[index] = instrument;
1✔
264
      registeredMetricNames.add(name);
1✔
265
      nextAvailableMetricIndex += 1;
1✔
266
      return instrument;
1✔
267
    }
268
  }
269

270

271
  /**
272
   * Registers a new Long Gauge metric instrument.
273
   *
274
   * @param name the name of the metric
275
   * @param description a description of the metric
276
   * @param unit the unit of measurement for the metric
277
   * @param requiredLabelKeys a list of required label keys
278
   * @param optionalLabelKeys a list of optional label keys
279
   * @param enableByDefault whether the metric should be enabled by default
280
   * @return the newly created LongGaugeMetricInstrument
281
   * @throws IllegalStateException if a metric with the same name already exists
282
   */
283
  public LongGaugeMetricInstrument registerLongGauge(String name, String description,
284
      String unit, List<String> requiredLabelKeys, List<String> optionalLabelKeys, boolean
285
      enableByDefault) {
286
    checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
1✔
287
    checkNotNull(description, "description");
1✔
288
    checkNotNull(unit, "unit");
1✔
289
    checkNotNull(requiredLabelKeys, "requiredLabelKeys");
1✔
290
    checkNotNull(optionalLabelKeys, "optionalLabelKeys");
1✔
291
    synchronized (lock) {
1✔
292
      if (registeredMetricNames.contains(name)) {
1✔
293
        throw new IllegalStateException("Metric with name " + name + " already exists");
1✔
294
      }
295
      int index = nextAvailableMetricIndex;
1✔
296
      if (index + 1 == metricInstruments.length) {
1✔
297
        resizeMetricInstruments();
1✔
298
      }
299
      LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument(
1✔
300
          index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
301
          enableByDefault);
302
      metricInstruments[index] = instrument;
1✔
303
      registeredMetricNames.add(name);
1✔
304
      nextAvailableMetricIndex += 1;
1✔
305
      return instrument;
1✔
306
    }
307
  }
308

309
  @GuardedBy("lock")
310
  private void resizeMetricInstruments() {
311
    // Increase the capacity of the metricInstruments array by INITIAL_INSTRUMENT_CAPACITY
312
    int newInstrumentsCapacity = metricInstruments.length + INITIAL_INSTRUMENT_CAPACITY;
1✔
313
    MetricInstrument[] resizedMetricInstruments = Arrays.copyOf(metricInstruments,
1✔
314
        newInstrumentsCapacity);
315
    metricInstruments = resizedMetricInstruments;
1✔
316
  }
1✔
317
}
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