From 497e1552172632107574d67d7c092a3f5a39a980 Mon Sep 17 00:00:00 2001 From: Vindhya Ningegowda Date: Fri, 12 Apr 2024 13:42:40 -0700 Subject: [PATCH] Add Metric Instrument Registry (#11103) * added metric instrument registry --- .../grpc/DoubleCounterMetricInstrument.java | 30 +++ .../grpc/DoubleHistogramMetricInstrument.java | 38 ++++ .../io/grpc/LongCounterMetricInstrument.java | 30 +++ .../io/grpc/LongGaugeMetricInstrument.java | 30 +++ .../grpc/LongHistogramMetricInstrument.java | 38 ++++ .../main/java/io/grpc/MetricInstrument.java | 75 +++++++ .../io/grpc/MetricInstrumentRegistry.java | 197 ++++++++++++++++++ .../java/io/grpc/PartialMetricInstrument.java | 92 ++++++++ 8 files changed, 530 insertions(+) create mode 100644 api/src/main/java/io/grpc/DoubleCounterMetricInstrument.java create mode 100644 api/src/main/java/io/grpc/DoubleHistogramMetricInstrument.java create mode 100644 api/src/main/java/io/grpc/LongCounterMetricInstrument.java create mode 100644 api/src/main/java/io/grpc/LongGaugeMetricInstrument.java create mode 100644 api/src/main/java/io/grpc/LongHistogramMetricInstrument.java create mode 100644 api/src/main/java/io/grpc/MetricInstrument.java create mode 100644 api/src/main/java/io/grpc/MetricInstrumentRegistry.java create mode 100644 api/src/main/java/io/grpc/PartialMetricInstrument.java diff --git a/api/src/main/java/io/grpc/DoubleCounterMetricInstrument.java b/api/src/main/java/io/grpc/DoubleCounterMetricInstrument.java new file mode 100644 index 00000000000..52e16476411 --- /dev/null +++ b/api/src/main/java/io/grpc/DoubleCounterMetricInstrument.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a double-valued counter metric instrument. + */ +@Internal +public final class DoubleCounterMetricInstrument extends PartialMetricInstrument { + DoubleCounterMetricInstrument(long index, String name, String description, String unit, + List requiredLabelKeys, List optionalLabelKeys, boolean enableByDefault) { + super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault); + } +} diff --git a/api/src/main/java/io/grpc/DoubleHistogramMetricInstrument.java b/api/src/main/java/io/grpc/DoubleHistogramMetricInstrument.java new file mode 100644 index 00000000000..e9472e29faa --- /dev/null +++ b/api/src/main/java/io/grpc/DoubleHistogramMetricInstrument.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a double-valued histogram metric instrument. + */ +@Internal +public final class DoubleHistogramMetricInstrument extends PartialMetricInstrument { + private final List bucketBoundaries; + + DoubleHistogramMetricInstrument(long index, String name, String description, String unit, + List bucketBoundaries, List requiredLabelKeys, List optionalLabelKeys, + boolean enableByDefault) { + super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault); + this.bucketBoundaries = bucketBoundaries; + } + + public List getBucketBoundaries() { + return bucketBoundaries; + } +} diff --git a/api/src/main/java/io/grpc/LongCounterMetricInstrument.java b/api/src/main/java/io/grpc/LongCounterMetricInstrument.java new file mode 100644 index 00000000000..233fb757ba4 --- /dev/null +++ b/api/src/main/java/io/grpc/LongCounterMetricInstrument.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a long-valued counter metric instrument. + */ +@Internal +public final class LongCounterMetricInstrument extends PartialMetricInstrument { + LongCounterMetricInstrument(long index, String name, String description, String unit, + List requiredLabelKeys, List optionalLabelKeys, boolean enableByDefault) { + super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault); + } +} diff --git a/api/src/main/java/io/grpc/LongGaugeMetricInstrument.java b/api/src/main/java/io/grpc/LongGaugeMetricInstrument.java new file mode 100644 index 00000000000..1a98c490606 --- /dev/null +++ b/api/src/main/java/io/grpc/LongGaugeMetricInstrument.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a long-valued gauge metric instrument. + */ +@Internal +public final class LongGaugeMetricInstrument extends PartialMetricInstrument { + LongGaugeMetricInstrument(long index, String name, String description, String unit, + List requiredLabelKeys, List optionalLabelKeys, boolean enableByDefault) { + super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault); + } +} diff --git a/api/src/main/java/io/grpc/LongHistogramMetricInstrument.java b/api/src/main/java/io/grpc/LongHistogramMetricInstrument.java new file mode 100644 index 00000000000..919e733c85a --- /dev/null +++ b/api/src/main/java/io/grpc/LongHistogramMetricInstrument.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a long-valued histogram metric instrument. + */ +@Internal +public final class LongHistogramMetricInstrument extends PartialMetricInstrument { + private final List bucketBoundaries; + + LongHistogramMetricInstrument(long index, String name, String description, String unit, + List bucketBoundaries, List requiredLabelKeys, List optionalLabelKeys, + boolean enableByDefault) { + super(index, name, description, unit, requiredLabelKeys, optionalLabelKeys, enableByDefault); + this.bucketBoundaries = bucketBoundaries; + } + + public List getBucketBoundaries() { + return bucketBoundaries; + } +} diff --git a/api/src/main/java/io/grpc/MetricInstrument.java b/api/src/main/java/io/grpc/MetricInstrument.java new file mode 100644 index 00000000000..f4353d651f3 --- /dev/null +++ b/api/src/main/java/io/grpc/MetricInstrument.java @@ -0,0 +1,75 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.List; + +/** + * Represents a metric instrument. Metric instrument contains information used to describe a metric. + */ +@Internal +public interface MetricInstrument { + /** + * Returns the unique index of this metric instrument. + * + * @return the index of the metric instrument. + */ + public long getIndex(); + + /** + * Returns the name of the metric. + * + * @return the name of the metric. + */ + public String getName(); + + /** + * Returns a description of the metric. + * + * @return a description of the metric. + */ + public String getDescription(); + + /** + * Returns the unit of measurement for the metric. + * + * @return the unit of measurement. + */ + public String getUnit(); + + /** + * Returns a list of required label keys for this metric instrument. + * + * @return a list of required label keys. + */ + public List getRequiredLabelKeys(); + + /** + * Returns a list of optional label keys for this metric instrument. + * + * @return a list of optional label keys. + */ + public List getOptionalLabelKeys(); + + /** + * Indicates whether this metric instrument is enabled by default. + * + * @return {@code true} if this metric instrument is enabled by default, + * {@code false} otherwise. + */ + public boolean isEnableByDefault(); +} diff --git a/api/src/main/java/io/grpc/MetricInstrumentRegistry.java b/api/src/main/java/io/grpc/MetricInstrumentRegistry.java new file mode 100644 index 00000000000..0eda6f2d4d0 --- /dev/null +++ b/api/src/main/java/io/grpc/MetricInstrumentRegistry.java @@ -0,0 +1,197 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * A registry for globally registered metric instruments. + */ +@Internal +public final class MetricInstrumentRegistry { + private static MetricInstrumentRegistry instance; + private final List metricInstruments; + private final Set registeredMetricNames; + + private MetricInstrumentRegistry() { + this.metricInstruments = new CopyOnWriteArrayList<>(); + this.registeredMetricNames = new CopyOnWriteArraySet<>(); + } + + /** + * Returns the default metric instrument registry. + */ + public static synchronized MetricInstrumentRegistry getDefaultRegistry() { + if (instance == null) { + instance = new MetricInstrumentRegistry(); + } + return instance; + } + + /** + * Returns a list of registered metric instruments. + */ + public List getMetricInstruments() { + return Collections.unmodifiableList(metricInstruments); + } + + /** + * Registers a new Double Counter metric instrument. + * + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param requiredLabelKeys a list of required label keys + * @param optionalLabelKeys a list of optional label keys + * @param enableByDefault whether the metric should be enabled by default + * @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, + String description, String unit, List requiredLabelKeys, + List optionalLabelKeys, boolean enableByDefault) { + if (registeredMetricNames.contains(name)) { + throw new IllegalStateException("Metric with name " + name + " already exists"); + } + long instrumentIndex = metricInstruments.size(); + DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument( + instrumentIndex, name, description, unit, requiredLabelKeys, optionalLabelKeys, + enableByDefault); + metricInstruments.add(instrument); + registeredMetricNames.add(name); + return instrument; + } + + /** + * Registers a new Long Counter metric instrument. + * + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param requiredLabelKeys a list of required label keys + * @param optionalLabelKeys a list of optional label keys + * @param enableByDefault whether the metric should be enabled by default + * @return the newly created LongCounterMetricInstrument + * @throws IllegalStateException if a metric with the same name already exists + */ + public synchronized LongCounterMetricInstrument registerLongCounter(String name, + String description, String unit, List requiredLabelKeys, + List optionalLabelKeys, boolean enableByDefault) { + if (registeredMetricNames.contains(name)) { + throw new IllegalStateException("Metric with name " + name + " already exists"); + } + // 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; + } + + /** + * Registers a new Double Histogram metric instrument. + * + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param bucketBoundaries recommended set of explicit bucket boundaries for the histogram + * @param requiredLabelKeys a list of required label keys + * @param optionalLabelKeys a list of optional label keys + * @param enableByDefault whether the metric should be enabled by default + * @return the newly created DoubleHistogramMetricInstrument + * @throws IllegalStateException if a metric with the same name already exists + */ + public synchronized DoubleHistogramMetricInstrument registerDoubleHistogram(String name, + String description, String unit, List bucketBoundaries, + List requiredLabelKeys, List optionalLabelKeys, boolean enableByDefault) { + if (registeredMetricNames.contains(name)) { + throw new IllegalStateException("Metric with name " + name + " already exists"); + } + long indexToInsertInstrument = metricInstruments.size(); + DoubleHistogramMetricInstrument instrument = new DoubleHistogramMetricInstrument( + indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys, + optionalLabelKeys, + enableByDefault); + metricInstruments.add(instrument); + registeredMetricNames.add(name); + return instrument; + } + + /** + * Registers a new Long Histogram metric instrument. + * + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param bucketBoundaries recommended set of explicit bucket boundaries for the histogram + * @param requiredLabelKeys a list of required label keys + * @param optionalLabelKeys a list of optional label keys + * @param enableByDefault whether the metric should be enabled by default + * @return the newly created LongHistogramMetricInstrument + * @throws IllegalStateException if a metric with the same name already exists + */ + public synchronized LongHistogramMetricInstrument registerLongHistogram(String name, + String description, String unit, List bucketBoundaries, List requiredLabelKeys, + List optionalLabelKeys, boolean enableByDefault) { + if (registeredMetricNames.contains(name)) { + throw new IllegalStateException("Metric with name " + name + " already exists"); + } + long indexToInsertInstrument = metricInstruments.size(); + LongHistogramMetricInstrument instrument = new LongHistogramMetricInstrument( + indexToInsertInstrument, name, description, unit, bucketBoundaries, requiredLabelKeys, + optionalLabelKeys, + enableByDefault); + metricInstruments.add(instrument); + registeredMetricNames.add(name); + return instrument; + } + + + /** + * Registers a new Long Gauge metric instrument. + * + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param requiredLabelKeys a list of required label keys + * @param optionalLabelKeys a list of optional label keys + * @param enableByDefault whether the metric should be enabled by default + * @return the newly created LongGaugeMetricInstrument + * @throws IllegalStateException if a metric with the same name already exists + */ + public synchronized LongGaugeMetricInstrument registerLongGauge(String name, String description, + String unit, List requiredLabelKeys, List optionalLabelKeys, boolean + enableByDefault) { + if (registeredMetricNames.contains(name)) { + throw new IllegalStateException("Metric with name " + name + " already exists"); + } + long indexToInsertInstrument = metricInstruments.size(); + LongGaugeMetricInstrument instrument = new LongGaugeMetricInstrument( + indexToInsertInstrument, name, description, unit, requiredLabelKeys, optionalLabelKeys, + enableByDefault); + metricInstruments.add(instrument); + registeredMetricNames.add(name); + return instrument; + } +} diff --git a/api/src/main/java/io/grpc/PartialMetricInstrument.java b/api/src/main/java/io/grpc/PartialMetricInstrument.java new file mode 100644 index 00000000000..5cfecf3499c --- /dev/null +++ b/api/src/main/java/io/grpc/PartialMetricInstrument.java @@ -0,0 +1,92 @@ +/* + * Copyright 2024 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import com.google.common.collect.ImmutableList; +import java.util.List; + +/** + * A partial implementation of the {@link MetricInstrument} interface. This class + * provides common fields and functionality for metric instruments. + */ +@Internal +abstract class PartialMetricInstrument implements MetricInstrument { + protected final long index; + protected final String name; + protected final String description; + protected final String unit; + protected final List requiredLabelKeys; + protected final List optionalLabelKeys; + protected final boolean enableByDefault; + + /** + * Constructs a new PartialMetricInstrument with the specified attributes. + * + * @param index the unique index of this metric instrument + * @param name the name of the metric + * @param description a description of the metric + * @param unit the unit of measurement for the metric + * @param requiredLabelKeys a list of required label keys for the metric + * @param optionalLabelKeys a list of optional label keys for the metric + * @param enableByDefault whether the metric should be enabled by default + */ + protected PartialMetricInstrument(long index, String name, String description, String unit, + List requiredLabelKeys, List optionalLabelKeys, boolean enableByDefault) { + this.index = index; + this.name = name; + this.description = description; + this.unit = unit; + this.requiredLabelKeys = ImmutableList.copyOf(requiredLabelKeys); + this.optionalLabelKeys = ImmutableList.copyOf(optionalLabelKeys); + this.enableByDefault = enableByDefault; + } + + @Override + public long getIndex() { + return index; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getUnit() { + return unit; + } + + @Override + public List getRequiredLabelKeys() { + return requiredLabelKeys; + } + + @Override + public List getOptionalLabelKeys() { + return optionalLabelKeys; + } + + @Override + public boolean isEnableByDefault() { + return enableByDefault; + } +}