Move metrics dependencies to artifacts under Maven groupId com.google.monitoring-client

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=180580386
This commit is contained in:
jianglai 2018-01-02 12:47:18 -08:00
parent 0aab48eb9f
commit 07622725bf
94 changed files with 153 additions and 6451 deletions

View file

@ -846,7 +846,7 @@ public final class RegistryConfig {
* Maximum QPS for the Google Cloud Monitoring V3 (aka Stackdriver) API. The QPS limit can be
* adjusted by contacting Cloud Support.
*
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
* @see com.google.monitoring.metrics.stackdriver.StackdriverWriter
*/
@Provides
@Config("stackdriverMaxQps")
@ -858,7 +858,7 @@ public final class RegistryConfig {
* Maximum number of points that can be sent to Stackdriver in a single {@code
* TimeSeries.Create} API call.
*
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
* @see com.google.monitoring.metrics.stackdriver.StackdriverWriter
*/
@Provides
@Config("stackdriverMaxPointsPerRequest")
@ -867,10 +867,10 @@ public final class RegistryConfig {
}
/**
* The reporting interval for {@link google.registry.monitoring.metrics.Metric} instances to be
* sent to a {@link google.registry.monitoring.metrics.MetricWriter}.
* The reporting interval for {@link com.google.monitoring.metrics.Metric} instances to be sent
* to a {@link com.google.monitoring.metrics.MetricWriter}.
*
* @see google.registry.monitoring.metrics.MetricReporter
* @see com.google.monitoring.metrics.MetricReporter
*/
@Provides
@Config("metricsWriteInterval")

View file

@ -23,7 +23,6 @@ java_library(
"//java/google/registry/config",
"//java/google/registry/dns/writer",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/request/lock",
@ -34,6 +33,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@javax_servlet_api",
"@joda_time",
],

View file

@ -17,14 +17,14 @@ package google.registry.dns;
import static google.registry.request.RequestParameters.PARAM_TLD;
import com.google.common.collect.ImmutableSet;
import com.google.monitoring.metrics.DistributionFitter;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.ExponentialFitter;
import com.google.monitoring.metrics.FibonacciFitter;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.config.RegistryEnvironment;
import google.registry.monitoring.metrics.DistributionFitter;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.ExponentialFitter;
import google.registry.monitoring.metrics.FibonacciFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import google.registry.request.Parameter;
import javax.inject.Inject;
import org.joda.time.Duration;

View file

@ -26,7 +26,6 @@ java_library(
"//java/google/registry/config",
"//java/google/registry/dns",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/whitebox",
"//java/google/registry/pricing",
"//java/google/registry/request",
@ -41,6 +40,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@com_googlecode_json_simple",
"@io_bazel_rules_closure//closure/templates",
"@javax_servlet_api",

View file

@ -14,13 +14,13 @@
package google.registry.flows;
import static google.registry.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import static com.google.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.monitoring.whitebox.EppMetric;
import javax.inject.Inject;

View file

@ -15,19 +15,19 @@
package google.registry.flows.async;
import static com.google.appengine.api.taskqueue.QueueConstants.maxLeaseCount;
import static com.google.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import static google.registry.flows.async.AsyncFlowMetrics.OperationType.CONTACT_AND_HOST_DELETE;
import static google.registry.flows.async.AsyncFlowMetrics.OperationType.DNS_REFRESH;
import static google.registry.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.DistributionFitter;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.FibonacciFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.DistributionFitter;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.FibonacciFitter;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.NonFinalForTesting;

View file

@ -14,7 +14,6 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/google/registry/config",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/util",
"//java/google/registry/xml",
"//third_party/objectify:objectify-v4_1",
@ -23,6 +22,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@com_google_re2j",
"@javax_servlet_api",
"@joda_time",

View file

@ -17,10 +17,10 @@ package google.registry.model.registry.label;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
/** Instrumentation for reserved lists. */
class DomainLabelMetrics {

View file

@ -15,13 +15,13 @@
package google.registry.model.server;
import com.google.common.collect.ImmutableSet;
import com.google.monitoring.metrics.DistributionFitter;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.ExponentialFitter;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.model.server.Lock.LockState;
import google.registry.monitoring.metrics.DistributionFitter;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.ExponentialFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import javax.annotation.Nullable;
import org.joda.time.Duration;

View file

@ -27,7 +27,6 @@ java_library(
"//java/google/registry/keyring/kms",
"//java/google/registry/mapreduce",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/whitebox",
"//java/google/registry/rde",
"//java/google/registry/rde/imports",
@ -41,6 +40,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@javax_servlet_api",
"@joda_time",
"@org_bouncycastle_bcpkix_jdk15on",

View file

@ -14,6 +14,7 @@
package google.registry.module.backend;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Component;
import dagger.Lazy;
import google.registry.bigquery.BigqueryModule;
@ -27,7 +28,6 @@ import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.module.backend.BackendRequestComponent.BackendRequestComponentModule;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.rde.JSchModule;
import google.registry.request.Modules.AppIdentityCredentialModule;

View file

@ -15,8 +15,8 @@
package google.registry.module.backend;
import com.google.appengine.api.LifecycleManager;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Lazy;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.security.Security;

View file

@ -14,7 +14,6 @@ java_library(
"//java/google/registry/flows",
"//java/google/registry/keyring/api",
"//java/google/registry/keyring/kms",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/whitebox",
"//java/google/registry/rdap",
"//java/google/registry/request",
@ -28,6 +27,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@javax_servlet_api",
"@org_bouncycastle_bcpkix_jdk15on",
],

View file

@ -14,6 +14,7 @@
package google.registry.module.frontend;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Component;
import dagger.Lazy;
import google.registry.braintree.BraintreeModule;
@ -23,7 +24,6 @@ import google.registry.flows.custom.CustomLogicFactoryModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.module.frontend.FrontendRequestComponent.FrontendRequestComponentModule;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.request.Modules.AppIdentityCredentialModule;
import google.registry.request.Modules.GoogleCredentialModule;

View file

@ -15,8 +15,8 @@
package google.registry.module.frontend;
import com.google.appengine.api.LifecycleManager;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Lazy;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.security.Security;

View file

@ -1,59 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.MetricSchema.Kind;
abstract class AbstractMetric<V> implements Metric<V> {
private final Class<V> valueClass;
private final MetricSchema metricSchema;
AbstractMetric(
String name,
String description,
String valueDisplayName,
Kind kind,
ImmutableSet<LabelDescriptor> labels,
Class<V> valueClass) {
this.metricSchema = MetricSchema.create(name, description, valueDisplayName, kind, labels);
this.valueClass = valueClass;
}
/** Returns the schema of this metric. */
@Override
public final MetricSchema getMetricSchema() {
return metricSchema;
}
/**
* Returns the type for the value of this metric, which would otherwise be erased at compile-time.
* This is useful for implementors of {@link MetricWriter}.
*/
@Override
public final Class<V> getValueClass() {
return valueClass;
}
@Override
public final String toString() {
return MoreObjects.toStringHelper(this)
.add("valueClass", valueClass)
.add("schema", metricSchema)
.toString();
}
}

View file

@ -1,19 +0,0 @@
package(
default_visibility = ["//visibility:public"],
)
licenses(["notice"]) # Apache 2.0
java_library(
name = "metrics",
srcs = glob(["*.java"]),
deps = [
"@com_google_auto_value",
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_errorprone_error_prone_annotations",
"@com_google_guava",
"@com_google_re2j",
"@joda_time",
],
)

View file

@ -1,217 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.monitoring.metrics.MetricsUtils.DEFAULT_CONCURRENCY_LEVEL;
import static google.registry.monitoring.metrics.MetricsUtils.newConcurrentHashMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.util.concurrent.AtomicLongMap;
import com.google.common.util.concurrent.Striped;
import google.registry.monitoring.metrics.MetricSchema.Kind;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import javax.annotation.concurrent.ThreadSafe;
import org.joda.time.Instant;
/**
* A metric which stores Long values. It is stateful and can be changed in increments.
*
* <p>This metric is generally suitable for counters, such as requests served or errors generated.
*
* <p>The start of the {@link MetricPoint#interval()} of values of instances of this metric will be
* set to the time that the metric was first set or last {@link #reset()}.
*/
@ThreadSafe
public final class Counter extends AbstractMetric<Long>
implements SettableMetric<Long>, IncrementableMetric {
/**
* A map of the {@link Counter} values, with a list of label values as the keys.
*
* <p>This should be modified in a critical section with {@code valueStartTimestamps} so that the
* values are in sync.
*/
private final AtomicLongMap<ImmutableList<String>> values = AtomicLongMap.create();
/**
* A map of the {@link Instant} that each value was created, with a list of label values as the
* keys. The start timestamp (as part of the {@link MetricPoint#interval()} can be used by
* implementations of {@link MetricWriter} to encode resets of monotonic counters.
*/
private final ConcurrentHashMap<ImmutableList<String>, Instant> valueStartTimestamps =
newConcurrentHashMap(DEFAULT_CONCURRENCY_LEVEL);
/**
* A fine-grained lock to ensure that {@code values} and {@code valueStartTimestamps} are modified
* and read in a critical section. The initialization parameter is the concurrency level, set to
* match the default concurrency level of {@link ConcurrentHashMap}.
*
* @see Striped
*/
private final Striped<Lock> valueLocks = Striped.lock(DEFAULT_CONCURRENCY_LEVEL);
/**
* Constructs a new Counter.
*
* <p>Note that the order of the labels is significant.
*/
Counter(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels) {
super(name, description, valueDisplayName, Kind.CUMULATIVE, labels, Long.class);
}
@VisibleForTesting
void incrementBy(long offset, Instant startTimestamp, ImmutableList<String> labelValues) {
Lock lock = valueLocks.get(labelValues);
lock.lock();
try {
values.addAndGet(labelValues, offset);
valueStartTimestamps.putIfAbsent(labelValues, startTimestamp);
} finally {
lock.unlock();
}
}
@Override
public final void incrementBy(long offset, String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
checkArgument(offset >= 0, "The offset provided must be non-negative");
incrementBy(offset, Instant.now(), ImmutableList.copyOf(labelValues));
}
@Override
public final void increment(String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
incrementBy(1L, Instant.now(), ImmutableList.copyOf(labelValues));
}
/**
* Returns a snapshot of the metric's values. The timestamp of each {@link MetricPoint} will be
* the last modification time for that tuple of label values.
*/
@Override
public final ImmutableList<MetricPoint<Long>> getTimestampedValues() {
return getTimestampedValues(Instant.now());
}
@Override
public final int getCardinality() {
return values.size();
}
@VisibleForTesting
final ImmutableList<MetricPoint<Long>> getTimestampedValues(Instant endTimestamp) {
ImmutableList.Builder<MetricPoint<Long>> timestampedValues = new ImmutableList.Builder<>();
for (Entry<ImmutableList<String>, Long> entry : values.asMap().entrySet()) {
ImmutableList<String> labelValues = entry.getKey();
valueLocks.get(labelValues).lock();
Instant startTimestamp;
try {
startTimestamp = valueStartTimestamps.get(labelValues);
} finally {
valueLocks.get(labelValues).unlock();
}
// There is an opportunity for endTimestamp to be less than startTimestamp if
// one of the modification methods is called on a value before the lock for that value is
// acquired but after getTimestampedValues has been invoked. Just set endTimestamp equal to
// startTimestamp if that happens.
endTimestamp = Ordering.natural().max(startTimestamp, endTimestamp);
timestampedValues.add(
MetricPoint.create(this, labelValues, startTimestamp, endTimestamp, entry.getValue()));
}
return timestampedValues.build();
}
@VisibleForTesting
final void set(Long value, Instant startTimestamp, ImmutableList<String> labelValues) {
Lock lock = valueLocks.get(labelValues);
lock.lock();
try {
this.values.put(labelValues, value);
valueStartTimestamps.putIfAbsent(labelValues, startTimestamp);
} finally {
lock.unlock();
}
}
@Override
public final void set(Long value, String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
set(value, Instant.now(), ImmutableList.copyOf(labelValues));
}
@VisibleForTesting
final void reset(Instant startTimestamp) {
// Lock the entire set of values so that all existing values will have a consistent timestamp
// after this call, without the possibility of interleaving with another reset() call.
for (int i = 0; i < valueLocks.size(); i++) {
valueLocks.getAt(i).lock();
}
try {
for (ImmutableList<String> labelValues : values.asMap().keySet()) {
this.values.put(labelValues, 0);
this.valueStartTimestamps.put(labelValues, startTimestamp);
}
} finally {
for (int i = 0; i < valueLocks.size(); i++) {
valueLocks.getAt(i).unlock();
}
}
}
@Override
public final void reset() {
reset(Instant.now());
}
@VisibleForTesting
final void reset(Instant startTimestamp, ImmutableList<String> labelValues) {
Lock lock = valueLocks.get(labelValues);
lock.lock();
try {
this.values.put(labelValues, 0);
this.valueStartTimestamps.put(labelValues, startTimestamp);
} finally {
lock.unlock();
}
}
@Override
public final void reset(String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
reset(Instant.now(), ImmutableList.copyOf(labelValues));
}
}

View file

@ -1,53 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.monitoring.metrics.MetricsUtils.checkDouble;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
/**
* Models a {@link DistributionFitter} with arbitrary sized intervals.
*
* <p>If only only one boundary is provided, then the fitter will consist of an overflow and
* underflow interval separated by that boundary.
*/
@AutoValue
public abstract class CustomFitter implements DistributionFitter {
/**
* Create a new {@link CustomFitter} with the given interval boundaries.
*
* @param boundaries is a sorted list of interval boundaries
* @throws IllegalArgumentException if {@code boundaries} is empty or not sorted in ascending
* order, or if a value in the set is infinite, {@code NaN}, or {@code -0.0}.
*/
public static CustomFitter create(ImmutableSet<Double> boundaries) {
checkArgument(boundaries.size() > 0, "boundaries must not be empty");
checkArgument(Ordering.natural().isOrdered(boundaries), "boundaries must be sorted");
for (Double d : boundaries) {
checkDouble(d);
}
return new AutoValue_CustomFitter(ImmutableSortedSet.copyOf(boundaries));
}
@Override
public abstract ImmutableSortedSet<Double> boundaries();
}

View file

@ -1,46 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.collect.ImmutableRangeMap;
/**
* Models a distribution of double-precision floating point sample data, and provides summary
* statistics of the distribution. This class also models the probability density function (PDF) of
* the distribution with a histogram.
*
* <p>The summary statistics provided are the mean and sumOfSquaredDeviation of the distribution.
*
* <p>The histogram fitting function is provided via a {@link DistributionFitter} implementation.
*
* @see DistributionFitter
*/
public interface Distribution {
/** Returns the mean of this distribution. */
double mean();
/** Returns the sum of squared deviations from the mean of this distribution. */
double sumOfSquaredDeviation();
/** Returns the count of samples in this distribution. */
long count();
/** Returns a histogram of the distribution's values. */
ImmutableRangeMap<Double, Long> intervalCounts();
/** Returns the {@link DistributionFitter} of this distribution. */
DistributionFitter distributionFitter();
}

View file

@ -1,33 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.collect.ImmutableSortedSet;
/**
* A companion interface to {@link Distribution} which fits samples to a histogram in order to
* estimate the probability density function (PDF) of the {@link Distribution}.
*
* <p>The fitter models the histogram with a set of finite boundaries. The closed-open interval
* [a,b) between two consecutive boundaries represents the domain of permissible values in that
* interval. The values less than the first boundary are in the underflow interval of (-inf, a) and
* values greater or equal to the last boundary in the array are in the overflow interval of [a,
* inf).
*/
public interface DistributionFitter {
/** Returns a sorted set of the boundaries modeled by this {@link DistributionFitter}. */
ImmutableSortedSet<Double> boundaries();
}

View file

@ -1,219 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static google.registry.monitoring.metrics.MetricsUtils.DEFAULT_CONCURRENCY_LEVEL;
import static google.registry.monitoring.metrics.MetricsUtils.newConcurrentHashMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.util.concurrent.Striped;
import google.registry.monitoring.metrics.MetricSchema.Kind;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import org.joda.time.Instant;
/**
* A metric which stores {@link Distribution} values. The values are stateful and meant to be
* updated incrementally via the {@link EventMetric#record(double, String...)} method.
*
* <p>This metric class is generally suitable for recording aggregations of data about a
* quantitative aspect of an event. For example, this metric would be suitable for recording the
* latency distribution for a request over the network.
*
* <p>The {@link MutableDistribution} values tracked by this metric can be reset with {@link
* EventMetric#reset()}.
*/
public class EventMetric extends AbstractMetric<Distribution> {
/**
* Default {@link DistributionFitter} suitable for latency measurements.
*
* <p>The finite range of this fitter is from 1 to 4^16 (4294967296).
*/
public static final DistributionFitter DEFAULT_FITTER = ExponentialFitter.create(16, 4.0, 1.0);
private final ConcurrentHashMap<ImmutableList<String>, Instant> valueStartTimestamps =
newConcurrentHashMap(DEFAULT_CONCURRENCY_LEVEL);
private final ConcurrentHashMap<ImmutableList<String>, MutableDistribution> values =
newConcurrentHashMap(DEFAULT_CONCURRENCY_LEVEL);
private final DistributionFitter distributionFitter;
/**
* A fine-grained lock to ensure that {@code values} and {@code valueStartTimestamps} are modified
* and read in a critical section. The initialization parameter is the concurrency level, set to
* match the default concurrency level of {@link ConcurrentHashMap}.
*
* @see Striped
*/
private final Striped<Lock> valueLocks = Striped.lock(DEFAULT_CONCURRENCY_LEVEL);
EventMetric(
String name,
String description,
String valueDisplayName,
DistributionFitter distributionFitter,
ImmutableSet<LabelDescriptor> labels) {
super(name, description, valueDisplayName, Kind.CUMULATIVE, labels, Distribution.class);
this.distributionFitter = distributionFitter;
}
@Override
public final int getCardinality() {
return values.size();
}
@Override
public final ImmutableList<MetricPoint<Distribution>> getTimestampedValues() {
return getTimestampedValues(Instant.now());
}
@VisibleForTesting
ImmutableList<MetricPoint<Distribution>> getTimestampedValues(Instant endTimestamp) {
ImmutableList.Builder<MetricPoint<Distribution>> timestampedValues =
new ImmutableList.Builder<>();
for (Entry<ImmutableList<String>, MutableDistribution> entry : values.entrySet()) {
ImmutableList<String> labelValues = entry.getKey();
Lock lock = valueLocks.get(labelValues);
lock.lock();
Instant startTimestamp;
ImmutableDistribution distribution;
try {
startTimestamp = valueStartTimestamps.get(labelValues);
distribution = ImmutableDistribution.copyOf(entry.getValue());
} finally {
lock.unlock();
}
// There is an opportunity for endTimestamp to be less than startTimestamp if
// one of the modification methods is called on a value before the lock for that value is
// acquired but after getTimestampedValues has been invoked. Just set endTimestamp equal to
// startTimestamp if that happens.
endTimestamp = Ordering.natural().max(startTimestamp, endTimestamp);
timestampedValues.add(
MetricPoint.create(this, labelValues, startTimestamp, endTimestamp, distribution));
}
return timestampedValues.build();
}
/**
* Adds the given {@code sample} to the {@link Distribution} for the given {@code labelValues}.
*
* <p>If the metric is undefined for given label values, this method will autovivify the {@link
* Distribution}.
*
* <p>The count of {@code labelValues} must be equal to the underlying metric's count of labels.
*/
public void record(double sample, String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
recordMultiple(sample, 1, Instant.now(), ImmutableList.copyOf(labelValues));
}
/**
* Adds {@code count} of the given {@code sample} to the {@link Distribution} for the given {@code
* labelValues}.
*
* <p>If the metric is undefined for given label values, this method will autovivify the {@link
* Distribution}.
*
* <p>The count of {@code labelValues} must be equal to the underlying metric's count of labels.
*/
public void record(double sample, int count, String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
recordMultiple(sample, count, Instant.now(), ImmutableList.copyOf(labelValues));
}
@VisibleForTesting
void recordMultiple(
double sample, int count, Instant startTimestamp, ImmutableList<String> labelValues) {
Lock lock = valueLocks.get(labelValues);
lock.lock();
try {
values.computeIfAbsent(labelValues, k -> new MutableDistribution(distributionFitter));
values.get(labelValues).add(sample, count);
valueStartTimestamps.putIfAbsent(labelValues, startTimestamp);
} finally {
lock.unlock();
}
}
/**
* Atomically resets the value and start timestamp of the metric for all label values.
*
* <p>This is useful if the metric is tracking values that are reset as part of a retrying
* transaction, for example.
*/
public void reset() {
reset(Instant.now());
}
@VisibleForTesting
final void reset(Instant startTime) {
// Lock the entire set of values so that all existing values will have a consistent timestamp
// after this call, without the possibility of interleaving with another reset() call.
for (int i = 0; i < valueLocks.size(); i++) {
valueLocks.getAt(i).lock();
}
try {
for (ImmutableList<String> labelValues : values.keySet()) {
this.values.put(labelValues, new MutableDistribution(distributionFitter));
this.valueStartTimestamps.put(labelValues, startTime);
}
} finally {
for (int i = 0; i < valueLocks.size(); i++) {
valueLocks.getAt(i).unlock();
}
}
}
/**
* Resets the value and start timestamp of the metric for the given label values.
*
* <p>This is useful if the metric is tracking a value that is reset as part of a retrying
* transaction, for example.
*/
public void reset(String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
reset(Instant.now(), ImmutableList.copyOf(labelValues));
}
@VisibleForTesting
final void reset(Instant startTimestamp, ImmutableList<String> labelValues) {
Lock lock = valueLocks.get(labelValues);
lock.lock();
try {
this.values.put(labelValues, new MutableDistribution(distributionFitter));
this.valueStartTimestamps.put(labelValues, startTimestamp);
} finally {
lock.unlock();
}
}
}

View file

@ -1,68 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.monitoring.metrics.MetricsUtils.checkDouble;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSortedSet;
/**
* Models a {@link DistributionFitter} with intervals of exponentially increasing size.
*
* <p>The interval boundaries are defined by {@code scale * Math.pow(base, i)} for {@code i} in
* {@code [0, numFiniteIntervals]}.
*
* <p>For example, an {@link ExponentialFitter} with {@code numFiniteIntervals=3, base=4.0,
* scale=1.5} represents a histogram with intervals {@code (-inf, 1.5), [1.5, 6), [6, 24), [24, 96),
* [96, +inf)}.
*/
@AutoValue
public abstract class ExponentialFitter implements DistributionFitter {
/**
* Create a new {@link ExponentialFitter}.
*
* @param numFiniteIntervals the number of intervals, excluding the underflow and overflow
* intervals
* @param base the base of the exponent
* @param scale a multiplicative factor for the exponential function
* @throws IllegalArgumentException if {@code numFiniteIntervals <= 0}, {@code width <= 0} or
* {@code base <= 1}
*/
public static ExponentialFitter create(int numFiniteIntervals, double base, double scale) {
checkArgument(numFiniteIntervals > 0, "numFiniteIntervals must be greater than 0");
checkArgument(scale != 0, "scale must not be 0");
checkArgument(base > 1, "base must be greater than 1");
checkDouble(base);
checkDouble(scale);
ImmutableSortedSet.Builder<Double> boundaries = ImmutableSortedSet.naturalOrder();
for (int i = 0; i < numFiniteIntervals + 1; i++) {
boundaries.add(scale * Math.pow(base, i));
}
return new AutoValue_ExponentialFitter(base, scale, boundaries.build());
}
public abstract double base();
public abstract double scale();
@Override
public abstract ImmutableSortedSet<Double> boundaries();
}

View file

@ -1,61 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableSortedSet;
/**
* Utility method to create a {@link CustomFitter} with intervals using the Fibonacci sequence.
*
* <p>A Fibonacci fitter is useful in situations where you want more precision on the low end than
* an {@link ExponentialFitter} with exponent base 2 would provide without the hassle of dealing
* with non-integer boundaries, such as would be created by an exponential fitter with a base of
* less than 2. Fibonacci fitters are ideal for integer metrics that are bounded across a certain
* range, e.g. integers between 1 and 1,000.
*
* <p>The interval boundaries are chosen as {@code (-inf, 0), [0, 1), [1, 2), [2, 3), [3, 5), [5,
* 8), [8, 13)}, etc., up to {@code [fibonacciFloor(maxBucketSize), inf)}.
*/
public final class FibonacciFitter {
/**
* Returns a new {@link CustomFitter} with bounds corresponding to the Fibonacci sequence.
*
* @param maxBucketSize the maximum bucket size to create (rounded down to the nearest Fibonacci
* number)
* @throws IllegalArgumentException if {@code maxBucketSize <= 0}
*/
public static CustomFitter create(long maxBucketSize) {
checkArgument(maxBucketSize > 0, "maxBucketSize must be greater than 0");
ImmutableSortedSet.Builder<Double> boundaries = ImmutableSortedSet.naturalOrder();
boundaries.add(Double.valueOf(0));
long i = 1;
long j = 2;
long k = 3;
while (i <= maxBucketSize) {
boundaries.add(Double.valueOf(i));
i = j;
j = k;
k = i + j;
}
return CustomFitter.create(boundaries.build());
}
private FibonacciFitter() {}
}

View file

@ -1,73 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.monitoring.metrics.MetricsUtils.checkDouble;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableRangeMap;
import javax.annotation.concurrent.ThreadSafe;
/**
* An immutable {@link Distribution}. Instances of this class can used to create {@link MetricPoint}
* instances, and should be used when exporting values to a {@link MetricWriter}.
*
* @see MutableDistribution
*/
@ThreadSafe
@AutoValue
public abstract class ImmutableDistribution implements Distribution {
public static ImmutableDistribution copyOf(Distribution distribution) {
return new AutoValue_ImmutableDistribution(
distribution.mean(),
distribution.sumOfSquaredDeviation(),
distribution.count(),
distribution.intervalCounts(),
distribution.distributionFitter());
}
@VisibleForTesting
static ImmutableDistribution create(
double mean,
double sumOfSquaredDeviation,
long count,
ImmutableRangeMap<Double, Long> intervalCounts,
DistributionFitter distributionFitter) {
checkDouble(mean);
checkDouble(sumOfSquaredDeviation);
checkArgument(count >= 0);
return new AutoValue_ImmutableDistribution(
mean, sumOfSquaredDeviation, count, intervalCounts, distributionFitter);
}
@Override
public abstract double mean();
@Override
public abstract double sumOfSquaredDeviation();
@Override
public abstract long count();
@Override
public abstract ImmutableRangeMap<Double, Long> intervalCounts();
@Override
public abstract DistributionFitter distributionFitter();
}

View file

@ -1,67 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
/**
* A {@link Metric} which can be incremented.
*
* <p>This is a view into a {@link Counter} to provide compile-time checking to disallow arbitrarily
* setting the metric, which is useful for metrics which should be monotonically increasing.
*/
public interface IncrementableMetric extends Metric<Long> {
/**
* Increments a metric by 1 for the given label values.
*
* <p>Use this method rather than {@link IncrementableMetric#incrementBy(long, String...)} if the
* increment value is 1, as it will be slightly more performant.
*
* <p>If the metric is undefined for given label values, it will be incremented from zero.
*
* <p>The metric's timestamp will be updated to the current time for the given label values.
*
* <p>The count of {@code labelValues} must be equal to the underlying metric's count of labels.
*/
void increment(String... labelValues);
/**
* Increments a metric by the given non-negative offset for the given label values.
*
* <p>If the metric is undefined for given label values, it will be incremented from zero.
*
* <p>The metric's timestamp will be updated to the current time for the given label values.
*
* <p>The count of {@code labelValues} must be equal to the underlying metric's count of labels.
*
* @throws IllegalArgumentException if the offset is negative.
*/
void incrementBy(long offset, String... labelValues);
/**
* Resets the value and start timestamp of the metric for the given label values.
*
* <p>This is useful if the counter is tracking a value that is reset as part of a retrying
* transaction, for example.
*/
void reset(String... labelValues);
/**
* Atomically resets the value and start timestamp of the metric for all label values.
*
* <p>This is useful if the counter is tracking values that are reset as part of a retrying
* transaction, for example.
*/
void reset();
}

View file

@ -1,57 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.re2j.Pattern;
/**
* Definition of a metric label.
*
* <p>If a metric is created with labels, corresponding label values must be provided when setting
* values on the metric.
*/
@AutoValue
public abstract class LabelDescriptor {
private static final Pattern ALLOWED_LABEL_PATTERN = Pattern.compile("\\w+");
LabelDescriptor() {}
/**
* Returns a new {@link LabelDescriptor}.
*
* @param name identifier for label
* @param description human-readable description of label
* @throws IllegalArgumentException if {@code name} isn't {@code \w+} or {@code description} is
* blank
*/
public static LabelDescriptor create(String name, String description) {
checkArgument(!name.isEmpty(), "Name must not be empty");
checkArgument(!description.isEmpty(), "Description must not be empty");
checkArgument(
ALLOWED_LABEL_PATTERN.matches(name),
"Label name must match the regex %s",
ALLOWED_LABEL_PATTERN);
return new AutoValue_LabelDescriptor(name, description);
}
public abstract String name();
public abstract String description();
}

View file

@ -1,64 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.monitoring.metrics.MetricsUtils.checkDouble;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSortedSet;
/**
* Models a {@link DistributionFitter} with equally sized intervals.
*
* <p>The interval boundaries are defined by {@code width * i + offset} for {@code i} in {@code [0,
* numFiniteIntervals}.
*
* <p>For example, a {@link LinearFitter} with {@code numFiniteIntervals=2, width=10, offset=5}
* represents a histogram with intervals {@code (-inf, 5), [5, 15), [15, 25), [25, +inf)}.
*/
@AutoValue
public abstract class LinearFitter implements DistributionFitter {
/**
* Create a new {@link LinearFitter}.
*
* @param numFiniteIntervals the number of intervals, excluding the underflow and overflow
* intervals
* @param width the width of each interval
* @param offset the start value of the first interval
* @throws IllegalArgumentException if {@code numFiniteIntervals <= 0} or {@code width <= 0}
*/
public static LinearFitter create(int numFiniteIntervals, double width, double offset) {
checkArgument(numFiniteIntervals > 0, "numFiniteIntervals must be greater than 0");
checkArgument(width > 0, "width must be greater than 0");
checkDouble(offset);
ImmutableSortedSet.Builder<Double> boundaries = ImmutableSortedSet.naturalOrder();
for (int i = 0; i < numFiniteIntervals + 1; i++) {
boundaries.add(width * i + offset);
}
return new AutoValue_LinearFitter(width, offset, boundaries.build());
}
public abstract double width();
public abstract double offset();
@Override
public abstract ImmutableSortedSet<Double> boundaries();
}

View file

@ -1,43 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.collect.ImmutableList;
/**
* A Metric which stores timestamped values.
*
* <p>This is a read-only view.
*/
public interface Metric<V> {
/**
* Returns the latest {@link MetricPoint} instances for every label-value combination tracked for
* this metric.
*/
ImmutableList<MetricPoint<V>> getTimestampedValues();
/** Returns the count of values being stored with this metric. */
int getCardinality();
/** Returns the schema of this metric. */
MetricSchema getMetricSchema();
/**
* Returns the type for the value of this metric, which would otherwise be erased at compile-time.
* This is useful for implementors of {@link MetricWriter}.
*/
Class<V> getValueClass();
}

View file

@ -1,96 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AbstractExecutionThreadService;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Background service to asynchronously push bundles of {@link MetricPoint} instances to a {@link
* MetricWriter}.
*/
class MetricExporter extends AbstractExecutionThreadService {
private static final Logger logger = Logger.getLogger(MetricReporter.class.getName());
private final BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue;
private final MetricWriter writer;
private final ThreadFactory threadFactory;
MetricExporter(
BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue,
MetricWriter writer,
ThreadFactory threadFactory) {
this.writeQueue = writeQueue;
this.writer = writer;
this.threadFactory = threadFactory;
}
@Override
protected void run() throws Exception {
logger.info("Started up MetricExporter");
while (isRunning()) {
Optional<ImmutableList<MetricPoint<?>>> batch = writeQueue.take();
logger.info("Got a batch of points from the writeQueue");
if (batch.isPresent()) {
logger.info("Batch contains data, writing to MetricWriter");
try {
for (MetricPoint<?> point : batch.get()) {
writer.write(point);
}
writer.flush();
} catch (IOException exception) {
logger.log(
Level.SEVERE, "Threw an exception while writing or flushing metrics", exception);
}
} else {
logger.info("Received a poison pill, stopping now");
// An absent optional indicates that the Reporter wants this service to shut down.
return;
}
}
}
@Override
protected Executor executor() {
final ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
// Make sure the ExecutorService terminates when this service does.
addListener(
new Listener() {
@Override
public void terminated(State from) {
executor.shutdown();
}
@Override
public void failed(State from, Throwable failure) {
executor.shutdown();
}
},
directExecutor());
return executor;
}
}

View file

@ -1,81 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
/** Static store of metrics internal to this client library. */
final class MetricMetrics {
/** A counter representing the total number of push intervals since the start of the process. */
static final IncrementableMetric pushIntervals =
MetricRegistryImpl.getDefault()
.newIncrementableMetric(
"/metrics/push_intervals",
"Count of push intervals.",
"Push Intervals",
ImmutableSet.of());
private static final ImmutableSet<LabelDescriptor> LABELS =
ImmutableSet.of(
LabelDescriptor.create("kind", "Metric Kind"),
LabelDescriptor.create("valueType", "Metric Value Type"));
/**
* A counter representing the total number of points pushed. Has {@link MetricSchema.Kind} and
* Metric value classes as LABELS.
*/
static final IncrementableMetric pushedPoints =
MetricRegistryImpl.getDefault()
.newIncrementableMetric(
"/metrics/points_pushed",
"Count of points pushed to Monitoring API.",
"Points Pushed",
LABELS);
/** A gauge representing a snapshot of the number of active timeseries being reported. */
@SuppressWarnings("unused")
private static final Metric<Long> timeseriesCount =
MetricRegistryImpl.getDefault()
.newGauge(
"/metrics/timeseries_count",
"Count of Timeseries being pushed to Monitoring API",
"Timeseries Count",
LABELS,
() -> {
HashMap<ImmutableList<String>, Long> timeseriesCount = new HashMap<>();
for (Metric<?> metric : MetricRegistryImpl.getDefault().getRegisteredMetrics()) {
ImmutableList<String> key =
ImmutableList.of(
metric.getMetricSchema().kind().toString(),
metric.getValueClass().toString());
int cardinality = metric.getCardinality();
if (!timeseriesCount.containsKey(key)) {
timeseriesCount.put(key, (long) cardinality);
} else {
timeseriesCount.put(key, timeseriesCount.get(key) + cardinality);
}
}
return ImmutableMap.copyOf(timeseriesCount);
},
Long.class);
private MetricMetrics() {}
}

View file

@ -1,85 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import org.joda.time.Instant;
import org.joda.time.Interval;
/**
* Value type class to store a snapshot of a {@link Metric} value for a given label value tuple and
* time {@link Interval}.
*/
@AutoValue
public abstract class MetricPoint<V> implements Comparable<MetricPoint<V>> {
/**
* Returns a new {@link MetricPoint} representing a value at a specific {@link Instant}.
*
* <p>Callers should insure that the count of {@code labelValues} matches the count of labels for
* the given metric.
*/
@VisibleForTesting
public static <V> MetricPoint<V> create(
Metric<V> metric, ImmutableList<String> labelValues, Instant timestamp, V value) {
MetricsUtils.checkLabelValuesLength(metric, labelValues);
return new AutoValue_MetricPoint<>(
metric, labelValues, new Interval(timestamp, timestamp), value);
}
/**
* Returns a new {@link MetricPoint} representing a value over an {@link Interval} from {@code
* startTime} to {@code endTime}.
*
* <p>Callers should insure that the count of {@code labelValues} matches the count of labels for
* the given metric.
*/
@VisibleForTesting
public static <V> MetricPoint<V> create(
Metric<V> metric,
ImmutableList<String> labelValues,
Instant startTime,
Instant endTime,
V value) {
MetricsUtils.checkLabelValuesLength(metric, labelValues);
return new AutoValue_MetricPoint<>(
metric, labelValues, new Interval(startTime, endTime), value);
}
public abstract Metric<V> metric();
public abstract ImmutableList<String> labelValues();
public abstract Interval interval();
public abstract V value();
@Override
public int compareTo(MetricPoint<V> other) {
int minLength = Math.min(this.labelValues().size(), other.labelValues().size());
for (int index = 0; index < minLength; index++) {
int comparisonResult =
this.labelValues().get(index).compareTo(other.labelValues().get(index));
if (comparisonResult != 0) {
return comparisonResult;
}
}
return Integer.compare(this.labelValues().size(), other.labelValues().size());
}
}

View file

@ -1,140 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/** An interface to create and keep track of metrics. */
public interface MetricRegistry {
/**
* Returns a new Gauge metric.
*
* <p>The metric's values are computed at sample time via the supplied callback function. The
* metric will be registered at the time of creation and collected for subsequent write intervals.
*
* <p>Since the metric's values are computed by a pre-defined callback function, this method only
* returns a read-only {@link Metric} view.
*
* @param name name of the metric. Should be in the form of '/foo/bar'.
* @param description human readable description of the metric. Must not be empty.
* @param valueDisplayName human readable description of the metric's value type. Must not be
* empty.
* @param labels list of the metric's labels. The labels (if there are any) must be of type
* STRING.
* @param metricCallback {@link Supplier} to compute the on-demand values of the metric. The
* function should be lightweight to compute and must be thread-safe. The map keys, which are
* lists of strings, must match in quantity and order with the provided labels.
* @param valueClass type hint to allow for compile-time encoding. Must match <V>.
* @param <V> value type of the metric. Must be one of {@link Boolean}, {@link Double}, {@link
* Long}, or {@link String}.
* @throws IllegalStateException if a metric of the same name is already registered.
*/
<V> Metric<V> newGauge(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Supplier<ImmutableMap<ImmutableList<String>, V>> metricCallback,
Class<V> valueClass);
/**
* Returns a new {@link SettableMetric}.
*
* <p>The metric's value is stateful and can be set to different values over time.
*
* <p>The metric is thread-safe.
*
* <p>The metric will be registered at the time of creation and collected for subsequent write
* intervals.
*
* @param name name of the metric. Should be in the form of '/foo/bar'.
* @param description human readable description of the metric.
* @param valueDisplayName human readable description of the metric's value type.
* @param labels list of the metric's labels. The labels (if there are any) must be of type
* STRING.
* @param valueClass type hint to allow for compile-time encoding. Must match <V>.
* @param <V> value type of the metric. Must be one of {@link Boolean}, {@link Double}, {@link
* Long}, or {@link String}.
* @throws IllegalStateException if a metric of the same name is already registered.
*/
<V> SettableMetric<V> newSettableMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Class<V> valueClass);
/**
* Returns a new {@link IncrementableMetric}.
*
* <p>The metric's values are {@link Long}, and can be incremented, and decremented. The metric is
* thread-safe. The metric will be registered at the time of creation and collected for subsequent
* write intervals.
*
* <p>This metric type is generally intended for monotonically increasing values, although the
* metric can in fact be incremented by negative numbers.
*
* @param name name of the metric. Should be in the form of '/foo/bar'.
* @param description human readable description of the metric.
* @param valueDisplayName human readable description of the metric's value type.
* @param labels list of the metric's labels. The labels (if there are any) must be of type
* STRING.
* @throws IllegalStateException if a metric of the same name is already registered.
*/
IncrementableMetric newIncrementableMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels);
/**
* Returns a new {@link EventMetric}.
*
* <p>This metric type is intended for recording aspects of an "event" -- things like latency or
* payload size.
*
* <p>The metric's values are {@link Distribution} instances which are updated via {@link
* EventMetric#record(double, String...)}.
*
* <p>The metric is thread-safe. The metric will be registered at the time of creation and
* collected for subsequent write intervals.
*
* @param name name of the metric. Should be in the form of '/foo/bar'.
* @param description human readable description of the metric.
* @param valueDisplayName human readable description of the metric's value type.
* @param labels list of the metric's labels.
* @param distributionFitter fit to apply to the underlying {@link Distribution} instances of this
* metric.
* @throws IllegalStateException if a metric of the same name is already registered.
*/
EventMetric newEventMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
DistributionFitter distributionFitter);
/**
* Fetches a snapshot of the currently registered metrics
*
* <p>Users who wish to manually sample and write metrics without relying on the scheduled {@link
* MetricReporter} can use this method to gather the list of metrics to report.
*/
ImmutableList<Metric<?>> getRegisteredMetrics();
}

View file

@ -1,163 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
/** A singleton registry of {@link Metric}s. */
@ThreadSafe
public final class MetricRegistryImpl implements MetricRegistry {
private static final Logger logger = Logger.getLogger(MetricRegistryImpl.class.getName());
private static final MetricRegistryImpl INSTANCE = new MetricRegistryImpl();
/** The canonical registry for metrics. The map key is the metric name. */
private final ConcurrentHashMap<String, Metric<?>> registeredMetrics = new ConcurrentHashMap<>();
/**
* Production code must use {@link #getDefault}, since this returns the {@link MetricRegistry}
* that {@link MetricReporter} uses. Test code that does not use {@link MetricReporter} can use
* this constructor to get an isolated instance of the registry.
*/
@VisibleForTesting
public MetricRegistryImpl() {}
public static MetricRegistryImpl getDefault() {
return INSTANCE;
}
/**
* Creates a new event metric.
*
* <p>Note that the order of the labels is significant.
*/
@Override
public EventMetric newEventMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
DistributionFitter distributionFitter) {
EventMetric metric =
new EventMetric(name, description, valueDisplayName, distributionFitter, labels);
registerMetric(name, metric);
logger.info("Registered new event metric: " + name);
return metric;
}
/**
* Creates a new gauge metric.
*
* <p>Note that the order of the labels is significant.
*/
@Override
@CanIgnoreReturnValue
public <V> Metric<V> newGauge(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Supplier<ImmutableMap<ImmutableList<String>, V>> metricCallback,
Class<V> valueClass) {
VirtualMetric<V> metric =
new VirtualMetric<>(
name, description, valueDisplayName, labels, metricCallback, valueClass);
registerMetric(name, metric);
logger.info("Registered new callback metric: " + name);
return metric;
}
/**
* Creates a new settable metric.
*
* <p>Note that the order of the labels is significant.
*/
@Override
public <V> SettableMetric<V> newSettableMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Class<V> valueClass) {
StoredMetric<V> metric =
new StoredMetric<>(name, description, valueDisplayName, labels, valueClass);
registerMetric(name, metric);
logger.info("Registered new stored metric: " + name);
return metric;
}
/**
* Creates a new incrementable metric.
*
* <p>Note that the order of the labels is significant.
*/
@Override
public IncrementableMetric newIncrementableMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels) {
Counter metric = new Counter(name, description, valueDisplayName, labels);
registerMetric(name, metric);
logger.info("Registered new counter: " + name);
return metric;
}
@Override
public ImmutableList<Metric<?>> getRegisteredMetrics() {
return ImmutableList.copyOf(registeredMetrics.values());
}
/**
* Unregisters a metric.
*
* <p>This is a no-op if the metric is not already registered.
*
* <p>{@link MetricWriter} implementations should not send unregistered metrics on subsequent
* write intervals.
*/
@VisibleForTesting
public void unregisterMetric(String name) {
registeredMetrics.remove(name);
logger.info("Unregistered metric: " + name);
}
@VisibleForTesting
public void unregisterAllMetrics() {
registeredMetrics.clear();
}
/** Registers a metric. */
@VisibleForTesting
void registerMetric(String name, Metric<?> metric) {
Metric<?> previousMetric = registeredMetrics.putIfAbsent(name, metric);
checkState(previousMetric == null, "Duplicate metric of same name: %s", name);
}
}

View file

@ -1,184 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AbstractScheduledService;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Engine to write metrics to a {@link MetricWriter} on a regular periodic basis.
*
* <p>In the Producer/Consumer pattern, this class is the Producer and {@link MetricExporter} is the
* consumer.
*/
public class MetricReporter extends AbstractScheduledService {
private static final Logger logger = Logger.getLogger(MetricReporter.class.getName());
private final long writeInterval;
private final MetricRegistry metricRegistry;
private final BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue;
private MetricExporter metricExporter;
private final MetricWriter metricWriter;
private final ThreadFactory threadFactory;
/**
* Returns a new MetricReporter.
*
* @param metricWriter {@link MetricWriter} implementation to write metrics to.
* @param writeInterval time period between metric writes, in seconds.
* @param threadFactory factory to use when creating background threads.
*/
public MetricReporter(
MetricWriter metricWriter, long writeInterval, ThreadFactory threadFactory) {
this(
metricWriter,
writeInterval,
threadFactory,
MetricRegistryImpl.getDefault(),
new ArrayBlockingQueue<>(1000));
}
@VisibleForTesting
MetricReporter(
MetricWriter metricWriter,
long writeInterval,
ThreadFactory threadFactory,
MetricRegistry metricRegistry,
BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue) {
checkArgument(writeInterval > 0, "writeInterval must be greater than zero");
this.metricWriter = metricWriter;
this.writeInterval = writeInterval;
this.threadFactory = threadFactory;
this.metricRegistry = metricRegistry;
this.writeQueue = writeQueue;
this.metricExporter = new MetricExporter(writeQueue, metricWriter, threadFactory);
}
@Override
protected void runOneIteration() {
logger.info("Running background metric push");
if (metricExporter.state() == State.FAILED) {
startMetricExporter();
}
ImmutableList.Builder<MetricPoint<?>> points = new ImmutableList.Builder<>();
/*
TODO(shikhman): Right now timestamps are recorded for each datapoint, which may use more storage
on the backend than if one timestamp were recorded for a batch. This should be configurable.
*/
for (Metric<?> metric : metricRegistry.getRegisteredMetrics()) {
points.addAll(metric.getTimestampedValues());
logger.fine(String.format("Enqueued metric %s", metric));
MetricMetrics.pushedPoints.increment(
metric.getMetricSchema().kind().name(), metric.getValueClass().toString());
}
if (!writeQueue.offer(Optional.of(points.build()))) {
logger.severe("writeQueue full, dropped a reporting interval of points");
}
MetricMetrics.pushIntervals.increment();
}
@Override
protected void shutDown() {
// Make sure to run one iteration on shutdown so that short-lived programs still report at
// least once.
runOneIteration();
// Offer a poision pill to inform the exporter to stop.
writeQueue.offer(Optional.empty());
try {
metricExporter.awaitTerminated(10, TimeUnit.SECONDS);
logger.info("Shut down MetricExporter");
} catch (IllegalStateException exception) {
logger.log(
Level.SEVERE,
"Failed to shut down MetricExporter because it was FAILED",
metricExporter.failureCause());
} catch (TimeoutException exception) {
logger.log(Level.SEVERE, "Failed to shut down MetricExporter within the timeout", exception);
}
}
@Override
protected void startUp() {
startMetricExporter();
}
@Override
protected Scheduler scheduler() {
// Start writing after waiting for one writeInterval.
return Scheduler.newFixedDelaySchedule(writeInterval, writeInterval, TimeUnit.SECONDS);
}
@Override
protected ScheduledExecutorService executor() {
final ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor(threadFactory);
// Make sure the ExecutorService terminates when this service does.
addListener(
new Listener() {
@Override
public void terminated(State from) {
executor.shutdown();
}
@Override
public void failed(State from, Throwable failure) {
executor.shutdown();
}
},
directExecutor());
return executor;
}
private void startMetricExporter() {
switch (metricExporter.state()) {
case NEW:
metricExporter.startAsync();
break;
case FAILED:
logger.log(
Level.SEVERE,
"MetricExporter died unexpectedly, restarting",
metricExporter.failureCause());
this.metricExporter = new MetricExporter(writeQueue, metricWriter, threadFactory);
this.metricExporter.startAsync();
break;
default:
throw new IllegalStateException(
"MetricExporter not FAILED or NEW, should not be calling startMetricExporter");
}
}
}

View file

@ -1,74 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
/** The description of a metric's schema. */
@AutoValue
public abstract class MetricSchema {
MetricSchema() {}
/**
* Returns an instance of {@link MetricSchema}.
*
* @param name must have a URL-like hierarchical name, for example "/cpu/utilization".
* @param description a human readable description of the metric. Must not be blank.
* @param valueDisplayName a human readable description of the metric's value. Must not be blank.
* @param kind the kind of metric.
* @param labels an ordered set of mandatory metric labels. For example, a metric may track error
* code as a label. If labels are set, corresponding label values must be provided when values
* are set. The set of labels may be empty.
*/
@VisibleForTesting
public static MetricSchema create(
String name,
String description,
String valueDisplayName,
Kind kind,
ImmutableSet<LabelDescriptor> labels) {
checkArgument(!name.isEmpty(), "Name must not be blank");
checkArgument(!description.isEmpty(), "Description must not be blank");
checkArgument(!valueDisplayName.isEmpty(), "Value Display Name must not be empty");
checkArgument(name.startsWith("/"), "Name must be URL-like and start with a '/'");
// TODO(b/30916431): strengthen metric name validation.
return new AutoValue_MetricSchema(name, description, valueDisplayName, kind, labels);
}
public abstract String name();
public abstract String description();
public abstract String valueDisplayName();
public abstract Kind kind();
public abstract ImmutableSet<LabelDescriptor> labels();
/**
* The kind of metric. CUMULATIVE metrics have values relative to an initial value, and GAUGE
* metrics have values which are standalone.
*/
public enum Kind {
CUMULATIVE,
GAUGE,
}
}

View file

@ -1,36 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import java.io.Flushable;
import java.io.IOException;
/** An interface for exporting Metrics. */
public interface MetricWriter extends Flushable {
/**
* Writes a {@link MetricPoint} to the writer's destination.
*
* <p>The write may be asynchronous.
*
* @throws IOException if the provided metric cannot be represented by the writer or if the metric
* cannot be flushed.
*/
<V> void write(MetricPoint<V> metricPoint) throws IOException;
/** Forces the writer to synchronously write all buffered metric values. */
@Override
void flush() throws IOException;
}

View file

@ -1,71 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableList;
import java.util.concurrent.ConcurrentHashMap;
/** Static helper methods for the Metrics library. */
final class MetricsUtils {
private static final Double NEGATIVE_ZERO = -0.0;
private static final String LABEL_SIZE_ERROR =
"The count of labelValues must be equal to the underlying Metric's count of labels.";
/**
* The below constants replicate the default initial capacity, load factor, and concurrency level
* for {@link ConcurrentHashMap} as of Java SE 7. They are recorded here so that a {@link
* com.google.common.util.concurrent.Striped} object can be constructed with a concurrency level
* matching the default concurrency level of a {@link ConcurrentHashMap}.
*/
private static final int HASHMAP_INITIAL_CAPACITY = 16;
private static final float HASHMAP_LOAD_FACTOR = 0.75f;
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
private MetricsUtils() {}
/**
* Check that the given {@code labelValues} match in count with the count of {@link
* LabelDescriptor} instances on the given {@code metric}
*
* @throws IllegalArgumentException if there is a count mismatch.
*/
static void checkLabelValuesLength(Metric<?> metric, String[] labelValues) {
checkArgument(labelValues.length == metric.getMetricSchema().labels().size(), LABEL_SIZE_ERROR);
}
/**
* Check that the given {@code labelValues} match in count with the count of {@link
* LabelDescriptor} instances on the given {@code metric}
*
* @throws IllegalArgumentException if there is a count mismatch.
*/
static void checkLabelValuesLength(Metric<?> metric, ImmutableList<String> labelValues) {
checkArgument(labelValues.size() == metric.getMetricSchema().labels().size(), LABEL_SIZE_ERROR);
}
/** Check that the given double is not infinite, {@code NaN}, or {@code -0.0}. */
static void checkDouble(double value) {
checkArgument(
!Double.isInfinite(value) && !Double.isNaN(value) && !NEGATIVE_ZERO.equals(value),
"value must be finite, not NaN, and not -0.0");
}
static <K, V> ConcurrentHashMap<K, V> newConcurrentHashMap(int concurrencyLevel) {
return new ConcurrentHashMap<>(HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, concurrencyLevel);
}
}

View file

@ -1,117 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.monitoring.metrics.MetricsUtils.checkDouble;
import com.google.common.collect.ImmutableRangeMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.collect.Range;
import com.google.common.collect.TreeRangeMap;
import com.google.common.primitives.Doubles;
import java.util.Map;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A mutable {@link Distribution}. Instances of this class <b>should not</b> be used to construct
* {@link MetricPoint} instances as {@link MetricPoint} instances are supposed to represent
* immutable values.
*
* @see ImmutableDistribution
*/
@NotThreadSafe
public final class MutableDistribution implements Distribution {
private final TreeRangeMap<Double, Long> intervalCounts;
private final DistributionFitter distributionFitter;
private double sumOfSquaredDeviation = 0.0;
private double mean = 0.0;
private long count = 0;
/** Constructs an empty Distribution with the specified {@link DistributionFitter}. */
public MutableDistribution(DistributionFitter distributionFitter) {
this.distributionFitter = checkNotNull(distributionFitter);
ImmutableSortedSet<Double> boundaries = distributionFitter.boundaries();
checkArgument(boundaries.size() > 0);
checkArgument(Ordering.natural().isOrdered(boundaries));
this.intervalCounts = TreeRangeMap.create();
double[] boundariesArray = Doubles.toArray(distributionFitter.boundaries());
// Add underflow and overflow intervals
this.intervalCounts.put(Range.lessThan(boundariesArray[0]), 0L);
this.intervalCounts.put(Range.atLeast(boundariesArray[boundariesArray.length - 1]), 0L);
// Add finite intervals
for (int i = 1; i < boundariesArray.length; i++) {
this.intervalCounts.put(Range.closedOpen(boundariesArray[i - 1], boundariesArray[i]), 0L);
}
}
public void add(double value) {
add(value, 1L);
}
public void add(double value, long numSamples) {
checkArgument(numSamples >= 0, "numSamples must be non-negative");
checkDouble(value);
// having numSamples = 0 works as expected (does nothing) even if we let it continue, but we
// can short-circuit it by returning early.
if (numSamples == 0) {
return;
}
Map.Entry<Range<Double>, Long> entry = intervalCounts.getEntry(value);
intervalCounts.put(entry.getKey(), entry.getValue() + numSamples);
this.count += numSamples;
// Update mean and sumOfSquaredDeviation using Welford's method
// See Knuth, "The Art of Computer Programming", Vol. 2, page 232, 3rd edition
double delta = value - mean;
mean += delta * numSamples / count;
sumOfSquaredDeviation += delta * (value - mean) * numSamples;
}
@Override
public double mean() {
return mean;
}
@Override
public double sumOfSquaredDeviation() {
return sumOfSquaredDeviation;
}
@Override
public long count() {
return count;
}
@Override
public ImmutableRangeMap<Double, Long> intervalCounts() {
return ImmutableRangeMap.copyOf(intervalCounts);
}
@Override
public DistributionFitter distributionFitter() {
return distributionFitter;
}
}

View file

@ -1,25 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
/** A {@link Metric} which can be set to different values over time. */
public interface SettableMetric<V> extends Metric<V> {
/**
* Sets the metric's value for a given set of label values. The count of labelValues must equal to
* the underlying metric's count of labels.
*/
void set(V value, String... labelValues);
}

View file

@ -1,89 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.MetricSchema.Kind;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.concurrent.ThreadSafe;
import org.joda.time.Instant;
/**
* A metric which is stateful.
*
* <p>The values are stored and set over time. This metric is generally suitable for state
* indicators, such as indicating that a server is in a RUNNING state or in a STOPPED state.
*
* <p>See {@link Counter} for a subclass which is suitable for stateful incremental values.
*
* <p>The {@link MetricPoint#interval()} of values of instances of this metric will always have a
* start time equal to the end time, since the metric value represents a point-in-time snapshot with
* no relationship to prior values.
*/
@ThreadSafe
public class StoredMetric<V> extends AbstractMetric<V> implements SettableMetric<V> {
private final ConcurrentHashMap<ImmutableList<String>, V> values = new ConcurrentHashMap<>();
StoredMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Class<V> valueClass) {
super(name, description, valueDisplayName, Kind.GAUGE, labels, valueClass);
}
@VisibleForTesting
final void set(V value, ImmutableList<String> labelValues) {
this.values.put(labelValues, value);
}
@Override
public final void set(V value, String... labelValues) {
MetricsUtils.checkLabelValuesLength(this, labelValues);
set(value, ImmutableList.copyOf(labelValues));
}
/**
* Returns a snapshot of the metric's values. The timestamp of each MetricPoint will be the last
* modification time for that tuple of label values.
*/
@Override
public final ImmutableList<MetricPoint<V>> getTimestampedValues() {
return getTimestampedValues(Instant.now());
}
@Override
public final int getCardinality() {
return values.size();
}
@VisibleForTesting
final ImmutableList<MetricPoint<V>> getTimestampedValues(Instant timestamp) {
ImmutableList.Builder<MetricPoint<V>> timestampedValues = new Builder<>();
for (Entry<ImmutableList<String>, V> entry : values.entrySet()) {
timestampedValues.add(
MetricPoint.create(this, entry.getKey(), timestamp, timestamp, entry.getValue()));
}
return timestampedValues.build();
}
}

View file

@ -1,91 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.MetricSchema.Kind;
import java.util.Map.Entry;
import javax.annotation.concurrent.ThreadSafe;
import org.joda.time.Instant;
/**
* A metric whose value is computed at sample-time.
*
* <p>This pattern works well for gauge-like metrics, such as CPU usage, memory usage, and file
* descriptor counts.
*
* <p>The {@link MetricPoint#interval()} of values of instances of this metric will always have a
* start time equal to the end time, since the metric value represents a point-in-time snapshot with
* no relationship to prior values.
*/
@ThreadSafe
public final class VirtualMetric<V> extends AbstractMetric<V> {
private final Supplier<ImmutableMap<ImmutableList<String>, V>> valuesSupplier;
/**
* Local cache of the count of values so that we don't have to evaluate the callback function to
* get the metric's cardinality.
*/
private volatile int cardinality;
VirtualMetric(
String name,
String description,
String valueDisplayName,
ImmutableSet<LabelDescriptor> labels,
Supplier<ImmutableMap<ImmutableList<String>, V>> valuesSupplier,
Class<V> valueClass) {
super(name, description, valueDisplayName, Kind.GAUGE, labels, valueClass);
this.valuesSupplier = valuesSupplier;
}
/**
* Returns a snapshot of the metric's values. This will evaluate the stored callback function. The
* timestamp for each MetricPoint will be the current time.
*/
@Override
public ImmutableList<MetricPoint<V>> getTimestampedValues() {
return getTimestampedValues(Instant.now());
}
/**
* Returns the cached value of the cardinality of this metric. The cardinality is computed when
* the metric is evaluated. If the metric has never been evaluated, the cardinality is zero.
*/
@Override
public int getCardinality() {
return cardinality;
}
@VisibleForTesting
ImmutableList<MetricPoint<V>> getTimestampedValues(Instant timestamp) {
ImmutableMap<ImmutableList<String>, V> values = valuesSupplier.get();
ImmutableList.Builder<MetricPoint<V>> metricPoints = new ImmutableList.Builder<>();
for (Entry<ImmutableList<String>, V> entry : values.entrySet()) {
metricPoints.add(
MetricPoint.create(this, entry.getKey(), timestamp, timestamp, entry.getValue()));
}
cardinality = values.size();
return metricPoints.build();
}
}

View file

@ -1,213 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics.contrib;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import google.registry.monitoring.metrics.Metric;
import google.registry.monitoring.metrics.MetricPoint;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Base truth subject for asserting things about {@link Metric} instances.
*
* <p>For use with the Google <a href="https://google.github.io/truth/">Truth</a> framework.
*/
abstract class AbstractMetricSubject<T, S extends AbstractMetricSubject<T, S>>
extends Subject<S, Metric<T>> {
/** And chainer to allow fluent assertions. */
public static class And<S extends AbstractMetricSubject<?, S>> {
private final S subject;
And(S subject) {
this.subject = subject;
}
public S and() {
return subject;
}
}
@SuppressWarnings("unchecked")
And<S> andChainer() {
return new And<>((S) this);
}
/**
* List of label value tuples about which an assertion has been made so far.
*
* <p>Used to track what tuples have been seen, in order to support hasNoOtherValues() assertions.
*/
protected final Set<ImmutableList<String>> expectedNondefaultLabelTuples = new HashSet<>();
/** Converts a metric point to a nice string representation for use in error messages. */
protected String convertMetricPoint(MetricPoint<T> metricPoint) {
return String.format(
"%s => %s",
Joiner.on(':').join(metricPoint.labelValues()),
getMessageRepresentation(metricPoint.value()));
}
protected AbstractMetricSubject(FailureMetadata metadata, Metric<T> actual) {
super(metadata, checkNotNull(actual));
}
/**
* Returns the string representation of the subject.
*
* <p>For metrics, it makes sense to use the metric name, as given in the schema.
*/
@Override
public String actualCustomStringRepresentation() {
return actual().getMetricSchema().name();
}
/**
* Asserts that the metric has a given value for the specified label values.
*
* @param value the value which the metric should have
* @param labels the labels for which the value is being asserted; the number and order of labels
* should match the definition of the metric
*/
public And<S> hasValueForLabels(T value, String... labels) {
MetricPoint<T> metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
if (metricPoint == null) {
failWithBadResults(
"has a value for labels",
Joiner.on(':').join(labels),
"has labeled values",
Ordering.<MetricPoint<T>>natural()
.sortedCopy(actual().getTimestampedValues())
.stream()
.map(this::convertMetricPoint)
.collect(toImmutableList()));
}
if (!metricPoint.value().equals(value)) {
failWithBadResults(
String.format("has a value of %s for labels", getMessageRepresentation(value)),
Joiner.on(':').join(labels),
"has a value of",
getMessageRepresentation(metricPoint.value()));
}
expectedNondefaultLabelTuples.add(ImmutableList.copyOf(labels));
return andChainer();
}
/**
* Asserts that the metric has any (non-default) value for the specified label values.
*
* @param labels the labels for which the value is being asserted; the number and order of labels
* should match the definition of the metric
*/
public And<S> hasAnyValueForLabels(String... labels) {
MetricPoint<T> metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
if (metricPoint == null) {
failWithBadResults(
"has a value for labels",
Joiner.on(':').join(labels),
"has labeled values",
Ordering.<MetricPoint<T>>natural()
.sortedCopy(actual().getTimestampedValues())
.stream()
.map(this::convertMetricPoint)
.collect(toImmutableList()));
}
if (hasDefaultValue(metricPoint)) {
failWithBadResults(
"has a non-default value for labels",
Joiner.on(':').join(labels),
"has a value of",
getMessageRepresentation(metricPoint.value()));
}
expectedNondefaultLabelTuples.add(ImmutableList.copyOf(labels));
return andChainer();
}
/** Asserts that the metric does not have a (non-default) value for the specified label values. */
protected And<S> doesNotHaveAnyValueForLabels(String... labels) {
MetricPoint<T> metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
if (metricPoint != null) {
failWithBadResults(
"has no value for labels",
Joiner.on(':').join(labels),
"has a value of",
getMessageRepresentation(metricPoint.value()));
}
return andChainer();
}
/**
* Asserts that the metric has no (non-default) values other than those about which an assertion
* has already been made.
*/
public And<S> hasNoOtherValues() {
for (MetricPoint<T> metricPoint : actual().getTimestampedValues()) {
if (!expectedNondefaultLabelTuples.contains(metricPoint.labelValues())) {
if (!hasDefaultValue(metricPoint)) {
failWithBadResults(
"has",
"no other nondefault values",
"has labeled values",
Ordering.<MetricPoint<T>>natural()
.sortedCopy(actual().getTimestampedValues())
.stream()
.map(this::convertMetricPoint)
.collect(toImmutableList()));
}
return andChainer();
}
}
return andChainer();
}
private @Nullable MetricPoint<T> findMetricPointForLabels(ImmutableList<String> labels) {
if (actual().getMetricSchema().labels().size() != labels.size()) {
return null;
}
for (MetricPoint<T> metricPoint : actual().getTimestampedValues()) {
if (metricPoint.labelValues().equals(labels)) {
return metricPoint;
}
}
return null;
}
/**
* Returns a string representation of a metric point value, for use in error messages.
*
* <p>Subclass can override this method if the string needs extra processing.
*/
protected String getMessageRepresentation(T value) {
return String.valueOf(value);
}
/**
* Returns true if the metric point has a non-default value.
*
* <p>This should be overridden by subclasses. E.g. for incrementable metrics, the method should
* return true if the value is not zero, and so on.
*/
protected abstract boolean hasDefaultValue(MetricPoint<T> metricPoint);
}

View file

@ -1,18 +0,0 @@
package(
default_testonly = 1,
default_visibility = ["//visibility:public"],
)
licenses(["notice"]) # Apache 2.0
java_library(
name = "contrib",
srcs = glob(["*.java"]),
deps = [
"//java/google/registry/monitoring/metrics",
"@com_google_code_findbugs_jsr305",
"@com_google_guava",
"@com_google_truth",
"@com_google_truth_extensions_truth_java8_extension",
],
)

View file

@ -1,125 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics.contrib;
import static com.google.common.truth.Truth.assertAbout;
import com.google.common.base.Joiner;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import com.google.common.truth.FailureMetadata;
import google.registry.monitoring.metrics.Distribution;
import google.registry.monitoring.metrics.ImmutableDistribution;
import google.registry.monitoring.metrics.Metric;
import google.registry.monitoring.metrics.MetricPoint;
import google.registry.monitoring.metrics.MutableDistribution;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Truth subject for the {@link Metric<Distribution>} class.
*
* <p>For use with the Google <a href="https://google.github.io/truth/">Truth</a> framework. Usage:
*
* <pre> assertThat(myDistributionMetric)
* .hasAnyValueForLabels("label1", "label2", "label3")
* .and()
* .hasNoOtherValues();
* assertThat(myDistributionMetric)
* .doesNotHaveAnyValueForLabels("label1", "label2");
* assertThat(myDistributionMetric)
* .hasDataSetForLabels(ImmutableSet.of(data1, data2, data3), "label1", "label2");
* </pre>
*
* <p>The assertions treat an empty distribution as no value at all. This is not how the data is
* actually stored; event metrics do in fact have an empty distribution after they are reset. But
* it's difficult to write assertions about expected metric data when any number of empty
* distributions can also be present, so they are screened out for convenience.
*/
public final class DistributionMetricSubject
extends AbstractMetricSubject<Distribution, DistributionMetricSubject> {
/** {@link Subject.Factory} for assertions about {@link Metric<Distribution>} objects. */
/** Static assertThat({@link Metric<Distribution>}) shortcut method. */
public static DistributionMetricSubject assertThat(@Nullable Metric<Distribution> metric) {
return assertAbout(DistributionMetricSubject::new).that(metric);
}
private DistributionMetricSubject(FailureMetadata metadata, Metric<Distribution> actual) {
super(metadata, actual);
}
/**
* Returns an indication to {@link AbstractMetricSubject#hasNoOtherValues} on whether a {@link
* MetricPoint} has a non-empty distribution.
*/
@Override
protected boolean hasDefaultValue(MetricPoint<Distribution> metricPoint) {
return metricPoint.value().count() == 0;
}
/** Returns an appropriate string representation of a metric value for use in error messages. */
@Override
protected String getMessageRepresentation(Distribution distribution) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<Range<Double>, Long> entry :
distribution.intervalCounts().asMapOfRanges().entrySet()) {
if (entry.getValue() != 0L) {
if (first) {
first = false;
} else {
sb.append(',');
}
if (entry.getKey().hasLowerBound()) {
sb.append((entry.getKey().lowerBoundType() == BoundType.CLOSED) ? '[' : '(');
sb.append(entry.getKey().lowerEndpoint());
}
sb.append("..");
if (entry.getKey().hasUpperBound()) {
sb.append(entry.getKey().upperEndpoint());
sb.append((entry.getKey().upperBoundType() == BoundType.CLOSED) ? ']' : ')');
}
sb.append('=');
sb.append(entry.getValue());
}
}
sb.append('}');
return sb.toString();
}
/**
* Asserts that the distribution for the given label can be constructed from the given data set.
*
* <p>Note that this only tests that the distribution has the same binned histogram, along with
* the same mean, and sum of squared deviation as it would if it had recorded the specified data
* points. It could have in fact collected different data points that resulted in the same
* distribution, but that information is lost to us and cannot be tested.
*/
public And<DistributionMetricSubject> hasDataSetForLabels(
ImmutableSet<? extends Number> dataSet, String... labels) {
ImmutableList<MetricPoint<Distribution>> metricPoints = actual().getTimestampedValues();
if (metricPoints.isEmpty()) {
failWithBadResults(
"has a distribution for labels", Joiner.on(':').join(labels), "has", "no values");
}
MutableDistribution targetDistribution =
new MutableDistribution(metricPoints.get(0).value().distributionFitter());
dataSet.forEach(data -> targetDistribution.add(data.doubleValue()));
return hasValueForLabels(ImmutableDistribution.copyOf(targetDistribution), labels);
}
}

View file

@ -1,76 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics.contrib;
import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import google.registry.monitoring.metrics.Metric;
import google.registry.monitoring.metrics.MetricPoint;
import javax.annotation.Nullable;
/**
* Truth subject for the {@link Metric<Long>} class.
*
* <p>For use with the Google <a href="https://google.github.io/truth/">Truth</a> framework. Usage:
*
* <pre> assertThat(myLongMetric)
* .hasValueForLabels(5, "label1", "label2", "label3")
* .and()
* .hasAnyValueForLabels("label1", "label2", "label4")
* .and()
* .hasNoOtherValues();
* assertThat(myLongMetric)
* .doesNotHaveAnyValueForLabels("label1", "label2");
* </pre>
*
* <p>The assertions treat a value of 0 as no value at all. This is not how the data is actually
* stored; zero is a valid value for incrementable metrics, and they do in fact have a value of zero
* after they are reset. But it's difficult to write assertions about expected metric data when any
* number of zero values can also be present, so they are screened out for convenience.
*/
public final class LongMetricSubject extends AbstractMetricSubject<Long, LongMetricSubject> {
/**
* Static assertThat({@link Metric<Long>}) shortcut method.
*
* @see Subject.Factory for assertions about {@link Metric<Long>} objects.
*/
public static LongMetricSubject assertThat(@Nullable Metric<Long> metric) {
return assertAbout(LongMetricSubject::new).that(metric);
}
private LongMetricSubject(FailureMetadata metadata, Metric<Long> actual) {
super(metadata, actual);
}
/**
* Asserts that the metric has a given value for the specified label values. This is a convenience
* method that takes a long instead of a Long, for ease of use.
*/
public And<LongMetricSubject> hasValueForLabels(long value, String... labels) {
return hasValueForLabels(Long.valueOf(value), labels);
}
/**
* Returns an indication to {@link AbstractMetricSubject#hasNoOtherValues} on whether a {@link
* MetricPoint} has a non-zero value.
*/
@Override
protected boolean hasDefaultValue(MetricPoint<Long> metricPoint) {
return metricPoint.value() == 0L;
}
}

View file

@ -1,16 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
@javax.annotation.ParametersAreNonnullByDefault
package google.registry.monitoring.metrics;

View file

@ -1,24 +0,0 @@
package(
default_visibility = ["//visibility:public"],
)
licenses(["notice"]) # Apache 2.0
java_library(
name = "stackdriver",
srcs = glob(["*.java"]),
deps = [
"//java/google/registry/monitoring/metrics",
"@com_google_api_client",
"@com_google_apis_google_api_services_monitoring",
"@com_google_appengine_api_1_0_sdk",
"@com_google_auto_value",
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_errorprone_error_prone_annotations",
"@com_google_guava",
"@com_google_http_client",
"@com_google_re2j",
"@joda_time",
],
)

View file

@ -1,405 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.monitoring.metrics.stackdriver;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.monitoring.v3.Monitoring;
import com.google.api.services.monitoring.v3.model.BucketOptions;
import com.google.api.services.monitoring.v3.model.CreateTimeSeriesRequest;
import com.google.api.services.monitoring.v3.model.Distribution;
import com.google.api.services.monitoring.v3.model.Explicit;
import com.google.api.services.monitoring.v3.model.Exponential;
import com.google.api.services.monitoring.v3.model.LabelDescriptor;
import com.google.api.services.monitoring.v3.model.Linear;
import com.google.api.services.monitoring.v3.model.Metric;
import com.google.api.services.monitoring.v3.model.MetricDescriptor;
import com.google.api.services.monitoring.v3.model.MonitoredResource;
import com.google.api.services.monitoring.v3.model.Point;
import com.google.api.services.monitoring.v3.model.TimeInterval;
import com.google.api.services.monitoring.v3.model.TimeSeries;
import com.google.api.services.monitoring.v3.model.TypedValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.RateLimiter;
import google.registry.monitoring.metrics.CustomFitter;
import google.registry.monitoring.metrics.DistributionFitter;
import google.registry.monitoring.metrics.ExponentialFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LinearFitter;
import google.registry.monitoring.metrics.MetricPoint;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import google.registry.monitoring.metrics.MetricSchema.Kind;
import google.registry.monitoring.metrics.MetricWriter;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;
import java.util.logging.Logger;
import javax.annotation.concurrent.NotThreadSafe;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/**
* Metrics writer for Google Cloud Monitoring V3
*
* <p>This class communicates with the API via HTTP. In order to increase throughput and minimize
* CPU, it buffers points to be written until it has {@code maxPointsPerRequest} points buffered or
* until {@link #flush()} is called.
*
* @see <a href="https://cloud.google.com/monitoring/api/v3/">Introduction to the Stackdriver
* Monitoring API</a>
*/
// TODO(shikhman): add retry logic
@NotThreadSafe
public class StackdriverWriter implements MetricWriter {
/**
* A counter representing the total number of points pushed. Has {@link Kind} and metric value
* types as labels.
*/
private static final IncrementableMetric pushedPoints =
MetricRegistryImpl.getDefault()
.newIncrementableMetric(
"/metrics/stackdriver/points_pushed",
"Count of points pushed to Stackdriver Monitoring API.",
"Points Pushed",
ImmutableSet.of(
google.registry.monitoring.metrics.LabelDescriptor.create("kind", "Metric Kind"),
google.registry.monitoring.metrics.LabelDescriptor.create(
"valueType", "Metric Value Type")));
private static final String METRIC_DOMAIN = "custom.googleapis.com";
private static final String LABEL_VALUE_TYPE = "STRING";
private static final DateTimeFormatter DATETIME_FORMATTER = ISODateTimeFormat.dateTime();
private static final Logger logger = Logger.getLogger(StackdriverWriter.class.getName());
// A map of native type to the equivalent Stackdriver metric type.
private static final ImmutableMap<Class<?>, String> ENCODED_METRIC_TYPES =
new ImmutableMap.Builder<Class<?>, String>()
.put(Long.class, "INT64")
.put(Double.class, "DOUBLE")
.put(Boolean.class, "BOOL")
.put(String.class, "STRING")
.put(google.registry.monitoring.metrics.Distribution.class, "DISTRIBUTION")
.build();
// A map of native kind to the equivalent Stackdriver metric kind.
private static final ImmutableMap<String, String> ENCODED_METRIC_KINDS =
new ImmutableMap.Builder<String, String>()
.put(Kind.GAUGE.name(), "GAUGE")
.put(Kind.CUMULATIVE.name(), "CUMULATIVE")
.build();
private static final String FLUSH_OVERFLOW_ERROR = "Cannot flush more than 200 points at a time";
private static final String METRIC_KIND_ERROR =
"Unrecognized metric kind, must be one of "
+ Joiner.on(",").join(ENCODED_METRIC_KINDS.keySet());
private static final String METRIC_TYPE_ERROR =
"Unrecognized metric type, must be one of "
+ Joiner.on(" ").join(ENCODED_METRIC_TYPES.keySet());
private static final String METRIC_LABEL_COUNT_ERROR =
"Metric label value count does not match its MetricDescriptor's label count";
private final MonitoredResource monitoredResource;
private final Queue<TimeSeries> timeSeriesBuffer;
/**
* A local cache of MetricDescriptors. A metric's metadata (name, kind, type, label definitions)
* must be registered before points for the metric can be pushed.
*/
private final HashMap<google.registry.monitoring.metrics.Metric<?>, MetricDescriptor>
registeredDescriptors = new HashMap<>();
private final String projectResource;
private final Monitoring monitoringClient;
private final int maxPointsPerRequest;
private final RateLimiter rateLimiter;
/**
* Constructs a {@link StackdriverWriter}.
*
* <p>The monitoringClient must have read and write permissions to the Cloud Monitoring API v3 on
* the provided project.
*/
public StackdriverWriter(
Monitoring monitoringClient,
String project,
MonitoredResource monitoredResource,
int maxQps,
int maxPointsPerRequest) {
this.monitoringClient = checkNotNull(monitoringClient);
this.projectResource = "projects/" + checkNotNull(project);
this.monitoredResource = monitoredResource;
this.maxPointsPerRequest = maxPointsPerRequest;
this.timeSeriesBuffer = new ArrayDeque<>(maxPointsPerRequest);
this.rateLimiter = RateLimiter.create(maxQps);
}
@VisibleForTesting
static ImmutableList<LabelDescriptor> encodeLabelDescriptors(
ImmutableSet<google.registry.monitoring.metrics.LabelDescriptor> labelDescriptors) {
List<LabelDescriptor> stackDriverLabelDescriptors = new ArrayList<>(labelDescriptors.size());
for (google.registry.monitoring.metrics.LabelDescriptor labelDescriptor : labelDescriptors) {
stackDriverLabelDescriptors.add(
new LabelDescriptor()
.setKey(labelDescriptor.name())
.setDescription(labelDescriptor.description())
.setValueType(LABEL_VALUE_TYPE));
}
return ImmutableList.copyOf(stackDriverLabelDescriptors);
}
@VisibleForTesting
static MetricDescriptor encodeMetricDescriptor(
google.registry.monitoring.metrics.Metric<?> metric) {
return new MetricDescriptor()
.setType(METRIC_DOMAIN + metric.getMetricSchema().name())
.setDescription(metric.getMetricSchema().description())
.setDisplayName(metric.getMetricSchema().valueDisplayName())
.setValueType(ENCODED_METRIC_TYPES.get(metric.getValueClass()))
.setLabels(encodeLabelDescriptors(metric.getMetricSchema().labels()))
.setMetricKind(ENCODED_METRIC_KINDS.get(metric.getMetricSchema().kind().name()));
}
/** Encodes and writes a metric point to Stackdriver. The point may be buffered. */
@Override
public <V> void write(google.registry.monitoring.metrics.MetricPoint<V> point)
throws IOException {
checkNotNull(point);
TimeSeries timeSeries = getEncodedTimeSeries(point);
timeSeriesBuffer.add(timeSeries);
logger.fine(String.format("Enqueued metric %s for writing", timeSeries.getMetric().getType()));
if (timeSeriesBuffer.size() == maxPointsPerRequest) {
flush();
}
}
/** Flushes all buffered metric points to Stackdriver. This call is blocking. */
@Override
public void flush() throws IOException {
checkState(timeSeriesBuffer.size() <= 200, FLUSH_OVERFLOW_ERROR);
// Return early; Stackdriver throws errors if we attempt to send empty requests.
if (timeSeriesBuffer.isEmpty()) {
logger.fine("Attempted to flush with no pending points, doing nothing");
return;
}
ImmutableList<TimeSeries> timeSeriesList = ImmutableList.copyOf(timeSeriesBuffer);
timeSeriesBuffer.clear();
CreateTimeSeriesRequest request = new CreateTimeSeriesRequest().setTimeSeries(timeSeriesList);
rateLimiter.acquire();
monitoringClient.projects().timeSeries().create(projectResource, request).execute();
for (TimeSeries timeSeries : timeSeriesList) {
pushedPoints.increment(timeSeries.getMetricKind(), timeSeries.getValueType());
}
logger.info(String.format("Flushed %d metrics to Stackdriver", timeSeriesList.size()));
}
/**
* Registers a metric's {@link MetricDescriptor} with the Monitoring API.
*
* @param metric the metric to be registered.
* @see <a href="https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.metricDescriptors">Stackdriver MetricDescriptor API</a>
*/
@VisibleForTesting
MetricDescriptor registerMetric(final google.registry.monitoring.metrics.Metric<?> metric)
throws IOException {
if (registeredDescriptors.containsKey(metric)) {
logger.fine(
String.format("Using existing metric descriptor %s", metric.getMetricSchema().name()));
return registeredDescriptors.get(metric);
}
MetricDescriptor descriptor = encodeMetricDescriptor(metric);
rateLimiter.acquire();
// We try to create a descriptor, but it may have been created already, so we re-fetch it on
// failure
try {
descriptor =
monitoringClient
.projects()
.metricDescriptors()
.create(projectResource, descriptor)
.execute();
logger.info(String.format("Registered new metric descriptor %s", descriptor.getType()));
} catch (GoogleJsonResponseException jsonException) {
// Not the error we were expecting, just give up
if (!"ALREADY_EXISTS".equals(jsonException.getStatusMessage())) {
throw jsonException;
}
descriptor =
monitoringClient
.projects()
.metricDescriptors()
.get(projectResource + "/metricDescriptors/" + descriptor.getType())
.execute();
logger.info(
String.format("Fetched existing metric descriptor %s", metric.getMetricSchema().name()));
}
registeredDescriptors.put(metric, descriptor);
return descriptor;
}
private static TimeInterval encodeTimeInterval(Interval nativeInterval, Kind metricKind) {
TimeInterval encodedInterval =
new TimeInterval().setStartTime(DATETIME_FORMATTER.print(nativeInterval.getStart()));
DateTime endTimestamp =
nativeInterval.toDurationMillis() == 0 && metricKind != Kind.GAUGE
? nativeInterval.getEnd().plusMillis(1)
: nativeInterval.getEnd();
return encodedInterval.setEndTime(DATETIME_FORMATTER.print(endTimestamp));
}
private static BucketOptions encodeBucketOptions(DistributionFitter fitter) {
BucketOptions bucketOptions = new BucketOptions();
if (fitter instanceof LinearFitter) {
LinearFitter linearFitter = (LinearFitter) fitter;
bucketOptions.setLinearBuckets(
new Linear()
.setNumFiniteBuckets(linearFitter.boundaries().size() - 1)
.setWidth(linearFitter.width())
.setOffset(linearFitter.offset()));
} else if (fitter instanceof ExponentialFitter) {
ExponentialFitter exponentialFitter = (ExponentialFitter) fitter;
bucketOptions.setExponentialBuckets(
new Exponential()
.setNumFiniteBuckets(exponentialFitter.boundaries().size() - 1)
.setGrowthFactor(exponentialFitter.base())
.setScale(exponentialFitter.scale()));
} else if (fitter instanceof CustomFitter) {
bucketOptions.setExplicitBuckets(new Explicit().setBounds(fitter.boundaries().asList()));
} else {
throw new IllegalArgumentException("Illegal DistributionFitter type");
}
return bucketOptions;
}
private static List<Long> encodeDistributionPoints(
google.registry.monitoring.metrics.Distribution distribution) {
return distribution.intervalCounts().asMapOfRanges().values().asList();
}
private static Distribution encodeDistribution(
google.registry.monitoring.metrics.Distribution nativeDistribution) {
return new Distribution()
.setMean(nativeDistribution.mean())
.setCount(nativeDistribution.count())
.setSumOfSquaredDeviation(nativeDistribution.sumOfSquaredDeviation())
.setBucketOptions(encodeBucketOptions(nativeDistribution.distributionFitter()))
.setBucketCounts(encodeDistributionPoints(nativeDistribution));
}
/**
* Encodes a {@link MetricPoint} into a Stackdriver {@link TimeSeries}.
*
* <p>This method will register the underlying {@link google.registry.monitoring.metrics.Metric}
* as a Stackdriver {@link MetricDescriptor}.
*
* @see <a href="https://cloud.google.com/monitoring/api/ref_v3/rest/v3/TimeSeries">
* Stackdriver TimeSeries API</a>
*/
@VisibleForTesting
<V> TimeSeries getEncodedTimeSeries(google.registry.monitoring.metrics.MetricPoint<V> point)
throws IOException {
google.registry.monitoring.metrics.Metric<V> metric = point.metric();
try {
checkArgument(
ENCODED_METRIC_KINDS.containsKey(metric.getMetricSchema().kind().name()),
METRIC_KIND_ERROR);
checkArgument(ENCODED_METRIC_TYPES.containsKey(metric.getValueClass()), METRIC_TYPE_ERROR);
} catch (IllegalArgumentException e) {
throw new IOException(e.getMessage());
}
MetricDescriptor descriptor = registerMetric(metric);
if (point.labelValues().size() != point.metric().getMetricSchema().labels().size()) {
throw new IOException(METRIC_LABEL_COUNT_ERROR);
}
V value = point.value();
TypedValue encodedValue = new TypedValue();
Class<?> valueClass = metric.getValueClass();
if (valueClass == Long.class) {
encodedValue.setInt64Value((Long) value);
} else if (valueClass == Double.class) {
encodedValue.setDoubleValue((Double) value);
} else if (valueClass == Boolean.class) {
encodedValue.setBoolValue((Boolean) value);
} else if (valueClass == String.class) {
encodedValue.setStringValue((String) value);
} else if (valueClass == google.registry.monitoring.metrics.Distribution.class) {
encodedValue.setDistributionValue(
encodeDistribution((google.registry.monitoring.metrics.Distribution) value));
} else {
// This is unreachable because the precondition checks will catch all NotSerializable
// exceptions.
throw new IllegalArgumentException("Invalid metric valueClass: " + metric.getValueClass());
}
Point encodedPoint =
new Point()
.setInterval(encodeTimeInterval(point.interval(), metric.getMetricSchema().kind()))
.setValue(encodedValue);
List<LabelDescriptor> encodedLabels = descriptor.getLabels();
// The MetricDescriptors returned by the GCM API have null fields rather than empty lists
encodedLabels = encodedLabels == null ? ImmutableList.of() : encodedLabels;
ImmutableMap.Builder<String, String> labelValues = new ImmutableMap.Builder<>();
int i = 0;
for (LabelDescriptor labelDescriptor : encodedLabels) {
labelValues.put(labelDescriptor.getKey(), point.labelValues().get(i++));
}
Metric encodedMetric =
new Metric().setType(descriptor.getType()).setLabels(labelValues.build());
return new TimeSeries()
.setMetric(encodedMetric)
.setPoints(ImmutableList.of(encodedPoint))
.setResource(monitoredResource)
// these two attributes are ignored by the API, we set them here to use elsewhere
// for internal metrics.
.setMetricKind(descriptor.getMetricKind())
.setValueType(descriptor.getValueType());
}
}

View file

@ -11,8 +11,6 @@ java_library(
"//java/google/registry/bigquery",
"//java/google/registry/config",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/metrics/stackdriver",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util",
@ -26,6 +24,8 @@ java_library(
"@com_google_dagger",
"@com_google_guava",
"@com_google_http_client",
"@com_google_monitoring_client_metrics",
"@com_google_monitoring_client_stackdriver",
"@javax_servlet_api",
"@joda_time",
],

View file

@ -23,12 +23,12 @@ import com.google.api.services.monitoring.v3.model.MonitoredResource;
import com.google.appengine.api.modules.ModulesService;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.monitoring.metrics.MetricReporter;
import com.google.monitoring.metrics.MetricWriter;
import com.google.monitoring.metrics.stackdriver.StackdriverWriter;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.monitoring.metrics.MetricWriter;
import google.registry.monitoring.metrics.stackdriver.StackdriverWriter;
import java.util.Set;
import java.util.function.Function;
import org.joda.time.Duration;

View file

@ -17,8 +17,6 @@ java_library(
]),
deps = [
"//java/google/registry/config",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/metrics/stackdriver",
"//java/google/registry/util",
"@com_beust_jcommander",
"@com_google_api_client",
@ -28,6 +26,8 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@com_google_monitoring_client_stackdriver",
"@io_netty_buffer",
"@io_netty_codec",
"@io_netty_codec_http",

View file

@ -20,12 +20,12 @@ import com.google.api.services.monitoring.v3.Monitoring;
import com.google.api.services.monitoring.v3.model.MonitoredResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.monitoring.metrics.MetricReporter;
import com.google.monitoring.metrics.MetricWriter;
import com.google.monitoring.metrics.stackdriver.StackdriverWriter;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import google.registry.monitoring.metrics.MetricReporter;
import google.registry.monitoring.metrics.MetricWriter;
import google.registry.monitoring.metrics.stackdriver.StackdriverWriter;
import javax.inject.Singleton;
/** Module that provides necessary bindings to instantiate a {@link MetricReporter} */

View file

@ -19,7 +19,7 @@ import static google.registry.proxy.handler.RelayHandler.RELAY_CHANNEL_KEY;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.monitoring.metrics.MetricReporter;
import com.google.monitoring.metrics.MetricReporter;
import google.registry.proxy.Protocol.BackendProtocol;
import google.registry.proxy.Protocol.FrontendProtocol;
import google.registry.proxy.ProxyModule.ProxyComponent;

View file

@ -15,13 +15,13 @@
package google.registry.proxy.metric;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.CustomFitter;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.ExponentialFitter;
import google.registry.monitoring.metrics.FibonacciFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.CustomFitter;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.ExponentialFitter;
import com.google.monitoring.metrics.FibonacciFitter;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.util.NonFinalForTesting;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;

View file

@ -18,10 +18,10 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.Metric;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.Metric;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.util.NonFinalForTesting;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;

View file

@ -11,7 +11,6 @@ java_library(
"//java/google/registry/config",
"//java/google/registry/flows",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/ui/server/registrar",
@ -22,6 +21,7 @@ java_library(
"@com_google_dagger",
"@com_google_guava",
"@com_google_http_client_jackson2",
"@com_google_monitoring_client_metrics",
"@com_google_re2j",
"@com_googlecode_json_simple",
"@javax_servlet_api",

View file

@ -17,12 +17,12 @@ package google.registry.rdap;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.DistributionFitter;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.FibonacciFitter;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.DistributionFitter;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.FibonacciFitter;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import java.util.Optional;

View file

@ -74,6 +74,9 @@ def domain_registry_repositories(
omit_com_google_http_client=False,
omit_com_google_http_client_appengine=False,
omit_com_google_http_client_jackson2=False,
omit_com_google_monitoring_client_metrics=False,
omit_com_google_monitoring_client_stackdriver=False,
omit_com_google_monitoring_client_contrib=False,
omit_com_google_oauth_client=False,
omit_com_google_oauth_client_appengine=False,
omit_com_google_oauth_client_java6=False,
@ -256,6 +259,12 @@ def domain_registry_repositories(
com_google_http_client_appengine()
if not omit_com_google_http_client_jackson2:
com_google_http_client_jackson2()
if not omit_com_google_monitoring_client_metrics:
com_google_monitoring_client_metrics()
if not omit_com_google_monitoring_client_stackdriver:
com_google_monitoring_client_stackdriver()
if not omit_com_google_monitoring_client_contrib:
com_google_monitoring_client_contrib()
if not omit_com_google_oauth_client:
com_google_oauth_client()
if not omit_com_google_oauth_client_appengine:
@ -518,6 +527,56 @@ def com_google_api_client_jackson2():
],
)
def com_google_monitoring_client_metrics():
java_import_external(
name = "com_google_monitoring_client_metrics",
licenses = ["notice"], # The Apache Software License, Version 2.0
jar_sha256 = "955fdd758b13f00aad48675a10060cad868b96c83c3a42e7a204c102d6d97cbf",
jar_urls = [
"http://maven.ibiblio.org/maven2/com/google/monitoring-client/metrics/1.0.3/metrics-1.0.3.jar",
"http://repo1.maven.org/maven2/com/google/monitoring-client/metrics/1.0.3/metrics-1.0.3.jar",
],
deps = [
"@com_google_guava",
"@com_google_auto_value",
"@com_google_errorprone_error_prone_annotations",
"@joda_time",
"@com_google_re2j",
],
)
def com_google_monitoring_client_stackdriver():
java_import_external(
name = "com_google_monitoring_client_stackdriver",
licenses = ["notice"], # The Apache Software License, Version 2.0
jar_sha256 = "89e90fc245249b6e480167033573ceaa5397b6648844331f72f68c5938789c80",
jar_urls = [
"http://repo1.maven.org/maven2/com/google/monitoring-client/stackdriver/1.0.3/stackdriver-1.0.3.jar",
"http://maven.ibiblio.org/maven2/com/google/monitoring-client/stackdriver/1.0.3/stackdriver-1.0.3.jar",
],
deps = [
"@com_google_guava",
"@com_google_apis_google_api_services_monitoring",
"@com_google_monitoring_client_metrics",
],
)
def com_google_monitoring_client_contrib():
java_import_external(
name = "com_google_monitoring_client_contrib",
licenses = ["notice"], # The Apache Software License, Version 2.0
jar_sha256 = "b61f4a2738cc687ea6680488db51ee2409066e2aa59caf9b547fc3e19efe6e43",
jar_urls = [
"http://maven.ibiblio.org/maven2/com/google/monitoring-client/contrib/1.0.3/contrib-1.0.3.jar",
"http://repo1.maven.org/maven2/com/google/monitoring-client/contrib/1.0.3/contrib-1.0.3.jar",
],
testonly_ = True,
deps = [
"@com_google_truth",
"@com_google_monitoring_client_metrics",
],
)
def com_google_api_client_java6():
java_import_external(
name = "com_google_api_client_java6",

View file

@ -10,7 +10,6 @@ java_library(
deps = [
"//java/google/registry/config",
"//java/google/registry/model",
"//java/google/registry/monitoring/metrics",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util",
@ -21,6 +20,7 @@ java_library(
"@com_google_code_findbugs_jsr305",
"@com_google_dagger",
"@com_google_guava",
"@com_google_monitoring_client_metrics",
"@javax_servlet_api",
"@joda_time",
],

View file

@ -15,14 +15,14 @@
package google.registry.whois;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import static com.google.monitoring.metrics.EventMetric.DEFAULT_FITTER;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import google.registry.monitoring.metrics.EventMetric;
import google.registry.monitoring.metrics.IncrementableMetric;
import google.registry.monitoring.metrics.LabelDescriptor;
import google.registry.monitoring.metrics.MetricRegistryImpl;
import com.google.monitoring.metrics.EventMetric;
import com.google.monitoring.metrics.IncrementableMetric;
import com.google.monitoring.metrics.LabelDescriptor;
import com.google.monitoring.metrics.MetricRegistryImpl;
import google.registry.util.Clock;
import java.util.Optional;
import javax.inject.Inject;