mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 16:07:15 +02:00
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:
parent
0aab48eb9f
commit
07622725bf
94 changed files with 153 additions and 6451 deletions
|
@ -846,7 +846,7 @@ public final class RegistryConfig {
|
||||||
* Maximum QPS for the Google Cloud Monitoring V3 (aka Stackdriver) API. The QPS limit can be
|
* Maximum QPS for the Google Cloud Monitoring V3 (aka Stackdriver) API. The QPS limit can be
|
||||||
* adjusted by contacting Cloud Support.
|
* adjusted by contacting Cloud Support.
|
||||||
*
|
*
|
||||||
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
|
* @see com.google.monitoring.metrics.stackdriver.StackdriverWriter
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("stackdriverMaxQps")
|
@Config("stackdriverMaxQps")
|
||||||
|
@ -858,7 +858,7 @@ public final class RegistryConfig {
|
||||||
* Maximum number of points that can be sent to Stackdriver in a single {@code
|
* Maximum number of points that can be sent to Stackdriver in a single {@code
|
||||||
* TimeSeries.Create} API call.
|
* TimeSeries.Create} API call.
|
||||||
*
|
*
|
||||||
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
|
* @see com.google.monitoring.metrics.stackdriver.StackdriverWriter
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("stackdriverMaxPointsPerRequest")
|
@Config("stackdriverMaxPointsPerRequest")
|
||||||
|
@ -867,10 +867,10 @@ public final class RegistryConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reporting interval for {@link google.registry.monitoring.metrics.Metric} instances to be
|
* The reporting interval for {@link com.google.monitoring.metrics.Metric} instances to be sent
|
||||||
* sent to a {@link google.registry.monitoring.metrics.MetricWriter}.
|
* to a {@link com.google.monitoring.metrics.MetricWriter}.
|
||||||
*
|
*
|
||||||
* @see google.registry.monitoring.metrics.MetricReporter
|
* @see com.google.monitoring.metrics.MetricReporter
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("metricsWriteInterval")
|
@Config("metricsWriteInterval")
|
||||||
|
|
|
@ -23,7 +23,6 @@ java_library(
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/dns/writer",
|
"//java/google/registry/dns/writer",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request/auth",
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/request/lock",
|
"//java/google/registry/request/lock",
|
||||||
|
@ -34,6 +33,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
],
|
],
|
||||||
|
|
|
@ -17,14 +17,14 @@ package google.registry.dns;
|
||||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
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.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 google.registry.request.Parameter;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
|
@ -26,7 +26,6 @@ java_library(
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/dns",
|
"//java/google/registry/dns",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/whitebox",
|
"//java/google/registry/monitoring/whitebox",
|
||||||
"//java/google/registry/pricing",
|
"//java/google/registry/pricing",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
|
@ -41,6 +40,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@com_googlecode_json_simple",
|
"@com_googlecode_json_simple",
|
||||||
"@io_bazel_rules_closure//closure/templates",
|
"@io_bazel_rules_closure//closure/templates",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
|
|
||||||
package google.registry.flows;
|
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 com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.monitoring.whitebox.EppMetric;
|
import google.registry.monitoring.whitebox.EppMetric;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,19 @@
|
||||||
package google.registry.flows.async;
|
package google.registry.flows.async;
|
||||||
|
|
||||||
import static com.google.appengine.api.taskqueue.QueueConstants.maxLeaseCount;
|
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.CONTACT_AND_HOST_DELETE;
|
||||||
import static google.registry.flows.async.AsyncFlowMetrics.OperationType.DNS_REFRESH;
|
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 static google.registry.util.FormattingLogger.getLoggerForCallerClass;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.DistributionFitter;
|
import com.google.monitoring.metrics.DistributionFitter;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.FibonacciFitter;
|
import com.google.monitoring.metrics.FibonacciFitter;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
import google.registry.util.FormattingLogger;
|
import google.registry.util.FormattingLogger;
|
||||||
import google.registry.util.NonFinalForTesting;
|
import google.registry.util.NonFinalForTesting;
|
||||||
|
|
|
@ -14,7 +14,6 @@ java_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"//java/google/registry/xml",
|
"//java/google/registry/xml",
|
||||||
"//third_party/objectify:objectify-v4_1",
|
"//third_party/objectify:objectify-v4_1",
|
||||||
|
@ -23,6 +22,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@com_google_re2j",
|
"@com_google_re2j",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
|
|
|
@ -17,10 +17,10 @@ package google.registry.model.registry.label;
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
|
|
||||||
/** Instrumentation for reserved lists. */
|
/** Instrumentation for reserved lists. */
|
||||||
class DomainLabelMetrics {
|
class DomainLabelMetrics {
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
package google.registry.model.server;
|
package google.registry.model.server;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
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.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 javax.annotation.Nullable;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ java_library(
|
||||||
"//java/google/registry/keyring/kms",
|
"//java/google/registry/keyring/kms",
|
||||||
"//java/google/registry/mapreduce",
|
"//java/google/registry/mapreduce",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/whitebox",
|
"//java/google/registry/monitoring/whitebox",
|
||||||
"//java/google/registry/rde",
|
"//java/google/registry/rde",
|
||||||
"//java/google/registry/rde/imports",
|
"//java/google/registry/rde/imports",
|
||||||
|
@ -41,6 +40,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
"@org_bouncycastle_bcpkix_jdk15on",
|
"@org_bouncycastle_bcpkix_jdk15on",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.module.backend;
|
package google.registry.module.backend;
|
||||||
|
|
||||||
|
import com.google.monitoring.metrics.MetricReporter;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import google.registry.bigquery.BigqueryModule;
|
import google.registry.bigquery.BigqueryModule;
|
||||||
|
@ -27,7 +28,6 @@ import google.registry.groups.GroupssettingsModule;
|
||||||
import google.registry.keyring.api.KeyModule;
|
import google.registry.keyring.api.KeyModule;
|
||||||
import google.registry.keyring.kms.KmsModule;
|
import google.registry.keyring.kms.KmsModule;
|
||||||
import google.registry.module.backend.BackendRequestComponent.BackendRequestComponentModule;
|
import google.registry.module.backend.BackendRequestComponent.BackendRequestComponentModule;
|
||||||
import google.registry.monitoring.metrics.MetricReporter;
|
|
||||||
import google.registry.monitoring.whitebox.StackdriverModule;
|
import google.registry.monitoring.whitebox.StackdriverModule;
|
||||||
import google.registry.rde.JSchModule;
|
import google.registry.rde.JSchModule;
|
||||||
import google.registry.request.Modules.AppIdentityCredentialModule;
|
import google.registry.request.Modules.AppIdentityCredentialModule;
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
package google.registry.module.backend;
|
package google.registry.module.backend;
|
||||||
|
|
||||||
import com.google.appengine.api.LifecycleManager;
|
import com.google.appengine.api.LifecycleManager;
|
||||||
|
import com.google.monitoring.metrics.MetricReporter;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import google.registry.monitoring.metrics.MetricReporter;
|
|
||||||
import google.registry.util.FormattingLogger;
|
import google.registry.util.FormattingLogger;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
|
|
@ -14,7 +14,6 @@ java_library(
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
"//java/google/registry/keyring/api",
|
"//java/google/registry/keyring/api",
|
||||||
"//java/google/registry/keyring/kms",
|
"//java/google/registry/keyring/kms",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/whitebox",
|
"//java/google/registry/monitoring/whitebox",
|
||||||
"//java/google/registry/rdap",
|
"//java/google/registry/rdap",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
|
@ -28,6 +27,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@org_bouncycastle_bcpkix_jdk15on",
|
"@org_bouncycastle_bcpkix_jdk15on",
|
||||||
],
|
],
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.module.frontend;
|
package google.registry.module.frontend;
|
||||||
|
|
||||||
|
import com.google.monitoring.metrics.MetricReporter;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import google.registry.braintree.BraintreeModule;
|
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.api.KeyModule;
|
||||||
import google.registry.keyring.kms.KmsModule;
|
import google.registry.keyring.kms.KmsModule;
|
||||||
import google.registry.module.frontend.FrontendRequestComponent.FrontendRequestComponentModule;
|
import google.registry.module.frontend.FrontendRequestComponent.FrontendRequestComponentModule;
|
||||||
import google.registry.monitoring.metrics.MetricReporter;
|
|
||||||
import google.registry.monitoring.whitebox.StackdriverModule;
|
import google.registry.monitoring.whitebox.StackdriverModule;
|
||||||
import google.registry.request.Modules.AppIdentityCredentialModule;
|
import google.registry.request.Modules.AppIdentityCredentialModule;
|
||||||
import google.registry.request.Modules.GoogleCredentialModule;
|
import google.registry.request.Modules.GoogleCredentialModule;
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
package google.registry.module.frontend;
|
package google.registry.module.frontend;
|
||||||
|
|
||||||
import com.google.appengine.api.LifecycleManager;
|
import com.google.appengine.api.LifecycleManager;
|
||||||
|
import com.google.monitoring.metrics.MetricReporter;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import google.registry.monitoring.metrics.MetricReporter;
|
|
||||||
import google.registry.util.FormattingLogger;
|
import google.registry.util.FormattingLogger;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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() {}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() {}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,8 +11,6 @@ java_library(
|
||||||
"//java/google/registry/bigquery",
|
"//java/google/registry/bigquery",
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/stackdriver",
|
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request/auth",
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
|
@ -26,6 +24,8 @@ java_library(
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_http_client",
|
"@com_google_http_client",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
|
"@com_google_monitoring_client_stackdriver",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
],
|
],
|
||||||
|
|
|
@ -23,12 +23,12 @@ import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
||||||
import com.google.appengine.api.modules.ModulesService;
|
import com.google.appengine.api.modules.ModulesService;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
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.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
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.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
|
@ -17,8 +17,6 @@ java_library(
|
||||||
]),
|
]),
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/stackdriver",
|
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"@com_beust_jcommander",
|
"@com_beust_jcommander",
|
||||||
"@com_google_api_client",
|
"@com_google_api_client",
|
||||||
|
@ -28,6 +26,8 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
|
"@com_google_monitoring_client_stackdriver",
|
||||||
"@io_netty_buffer",
|
"@io_netty_buffer",
|
||||||
"@io_netty_codec",
|
"@io_netty_codec",
|
||||||
"@io_netty_codec_http",
|
"@io_netty_codec_http",
|
||||||
|
|
|
@ -20,12 +20,12 @@ import com.google.api.services.monitoring.v3.Monitoring;
|
||||||
import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
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.Component;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
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;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
/** Module that provides necessary bindings to instantiate a {@link MetricReporter} */
|
/** Module that provides necessary bindings to instantiate a {@link MetricReporter} */
|
||||||
|
|
|
@ -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.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
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.BackendProtocol;
|
||||||
import google.registry.proxy.Protocol.FrontendProtocol;
|
import google.registry.proxy.Protocol.FrontendProtocol;
|
||||||
import google.registry.proxy.ProxyModule.ProxyComponent;
|
import google.registry.proxy.ProxyModule.ProxyComponent;
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
package google.registry.proxy.metric;
|
package google.registry.proxy.metric;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.CustomFitter;
|
import com.google.monitoring.metrics.CustomFitter;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.ExponentialFitter;
|
import com.google.monitoring.metrics.ExponentialFitter;
|
||||||
import google.registry.monitoring.metrics.FibonacciFitter;
|
import com.google.monitoring.metrics.FibonacciFitter;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.util.NonFinalForTesting;
|
import google.registry.util.NonFinalForTesting;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
|
|
@ -18,10 +18,10 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.Metric;
|
import com.google.monitoring.metrics.Metric;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.util.NonFinalForTesting;
|
import google.registry.util.NonFinalForTesting;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.group.ChannelGroup;
|
import io.netty.channel.group.ChannelGroup;
|
||||||
|
|
|
@ -11,7 +11,6 @@ java_library(
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request/auth",
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/ui/server/registrar",
|
"//java/google/registry/ui/server/registrar",
|
||||||
|
@ -22,6 +21,7 @@ java_library(
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_http_client_jackson2",
|
"@com_google_http_client_jackson2",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@com_google_re2j",
|
"@com_google_re2j",
|
||||||
"@com_googlecode_json_simple",
|
"@com_googlecode_json_simple",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
|
|
|
@ -17,12 +17,12 @@ package google.registry.rdap;
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.DistributionFitter;
|
import com.google.monitoring.metrics.DistributionFitter;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.FibonacciFitter;
|
import com.google.monitoring.metrics.FibonacciFitter;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
||||||
import google.registry.request.Action;
|
import google.registry.request.Action;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -74,6 +74,9 @@ def domain_registry_repositories(
|
||||||
omit_com_google_http_client=False,
|
omit_com_google_http_client=False,
|
||||||
omit_com_google_http_client_appengine=False,
|
omit_com_google_http_client_appengine=False,
|
||||||
omit_com_google_http_client_jackson2=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=False,
|
||||||
omit_com_google_oauth_client_appengine=False,
|
omit_com_google_oauth_client_appengine=False,
|
||||||
omit_com_google_oauth_client_java6=False,
|
omit_com_google_oauth_client_java6=False,
|
||||||
|
@ -256,6 +259,12 @@ def domain_registry_repositories(
|
||||||
com_google_http_client_appengine()
|
com_google_http_client_appengine()
|
||||||
if not omit_com_google_http_client_jackson2:
|
if not omit_com_google_http_client_jackson2:
|
||||||
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:
|
if not omit_com_google_oauth_client:
|
||||||
com_google_oauth_client()
|
com_google_oauth_client()
|
||||||
if not omit_com_google_oauth_client_appengine:
|
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():
|
def com_google_api_client_java6():
|
||||||
java_import_external(
|
java_import_external(
|
||||||
name = "com_google_api_client_java6",
|
name = "com_google_api_client_java6",
|
||||||
|
|
|
@ -10,7 +10,6 @@ java_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request/auth",
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
|
@ -21,6 +20,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
package google.registry.whois;
|
package google.registry.whois;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
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.auto.value.AutoValue;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
|
@ -28,7 +28,6 @@ java_library(
|
||||||
"//java/google/registry/dns",
|
"//java/google/registry/dns",
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/whitebox",
|
"//java/google/registry/monitoring/whitebox",
|
||||||
"//java/google/registry/pricing",
|
"//java/google/registry/pricing",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
|
@ -46,6 +45,7 @@ java_library(
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_guava_testlib",
|
"@com_google_guava_testlib",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@com_google_re2j",
|
"@com_google_re2j",
|
||||||
"@com_google_truth",
|
"@com_google_truth",
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
"@com_google_truth_extensions_truth_java8_extension",
|
||||||
|
|
|
@ -20,8 +20,8 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
import com.google.monitoring.metrics.EventMetric;
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.InjectRule;
|
import google.registry.testing.InjectRule;
|
||||||
import google.registry.testing.ShardableTestCase;
|
import google.registry.testing.ShardableTestCase;
|
||||||
|
|
|
@ -23,7 +23,6 @@ java_library(
|
||||||
"//java/google/registry/dns/writer",
|
"//java/google/registry/dns/writer",
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics/contrib",
|
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"//java/google/registry/xml",
|
"//java/google/registry/xml",
|
||||||
"//javatests/google/registry/testing",
|
"//javatests/google/registry/testing",
|
||||||
|
@ -34,6 +33,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_guava_testlib",
|
"@com_google_guava_testlib",
|
||||||
|
"@com_google_monitoring_client_contrib",
|
||||||
"@com_google_truth",
|
"@com_google_truth",
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
"@com_google_truth_extensions_truth_java8_extension",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
|
|
|
@ -16,6 +16,8 @@ package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth8.assertThat;
|
import static com.google.common.truth.Truth8.assertThat;
|
||||||
|
import static com.google.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
||||||
|
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.BLOOM_FILTER_NEGATIVE;
|
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.BLOOM_FILTER_NEGATIVE;
|
||||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_NEGATIVE;
|
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_NEGATIVE;
|
||||||
|
@ -28,8 +30,6 @@ import static google.registry.model.registry.label.PremiumListUtils.deletePremiu
|
||||||
import static google.registry.model.registry.label.PremiumListUtils.doesPremiumListExist;
|
import static google.registry.model.registry.label.PremiumListUtils.doesPremiumListExist;
|
||||||
import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice;
|
import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice;
|
||||||
import static google.registry.model.registry.label.PremiumListUtils.savePremiumListAndEntries;
|
import static google.registry.model.registry.label.PremiumListUtils.savePremiumListAndEntries;
|
||||||
import static google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
import static google.registry.testing.DatastoreHelper.createTld;
|
||||||
import static google.registry.testing.DatastoreHelper.loadPremiumListEntries;
|
import static google.registry.testing.DatastoreHelper.loadPremiumListEntries;
|
||||||
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
||||||
|
|
|
@ -16,6 +16,8 @@ package google.registry.model.registry.label;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
|
import static com.google.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
||||||
|
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListChecks;
|
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListChecks;
|
||||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListHits;
|
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListHits;
|
||||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListProcessingTime;
|
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListProcessingTime;
|
||||||
|
@ -28,8 +30,6 @@ import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_
|
||||||
import static google.registry.model.registry.label.ReservedList.getAllowedNameservers;
|
import static google.registry.model.registry.label.ReservedList.getAllowedNameservers;
|
||||||
import static google.registry.model.registry.label.ReservedList.getReservationTypes;
|
import static google.registry.model.registry.label.ReservedList.getReservationTypes;
|
||||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||||
import static google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
|
||||||
import static google.registry.testing.DatastoreHelper.createTld;
|
import static google.registry.testing.DatastoreHelper.createTld;
|
||||||
import static google.registry.testing.DatastoreHelper.persistReservedList;
|
import static google.registry.testing.DatastoreHelper.persistReservedList;
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package(
|
|
||||||
default_testonly = 1,
|
|
||||||
default_visibility = ["//java/google/registry:registry_project"],
|
|
||||||
)
|
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
|
|
||||||
|
|
||||||
java_library(
|
|
||||||
name = "metrics",
|
|
||||||
srcs = glob(["*.java"]),
|
|
||||||
deps = [
|
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//third_party/junit",
|
|
||||||
"@com_google_guava",
|
|
||||||
"@com_google_truth",
|
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
|
||||||
"@joda_time",
|
|
||||||
"@junit",
|
|
||||||
"@org_mockito_all",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
GenTestRules(
|
|
||||||
name = "GeneratedTestRules",
|
|
||||||
test_files = glob(["*Test.java"]),
|
|
||||||
deps = [
|
|
||||||
":metrics",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link Counter}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class CounterTest {
|
|
||||||
@Test
|
|
||||||
public void testGetCardinality_reflectsCurrentCardinality() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
assertThat(counter.getCardinality()).isEqualTo(0);
|
|
||||||
|
|
||||||
counter.increment("foo");
|
|
||||||
assertThat(counter.getCardinality()).isEqualTo(1);
|
|
||||||
counter.increment("bar");
|
|
||||||
assertThat(counter.getCardinality()).isEqualTo(2);
|
|
||||||
counter.increment("foo");
|
|
||||||
assertThat(counter.getCardinality()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementBy_wrongLabelValueCount_throwsException() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("label1", "bar"), LabelDescriptor.create("label2", "bar")));
|
|
||||||
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> counter.increment("blah"));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains(
|
|
||||||
"The count of labelValues must be equal to the underlying Metric's count of labels.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrement_incrementsValues() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues()).isEmpty();
|
|
||||||
|
|
||||||
// use package-private incrementBy once to set the start timestamp predictably.
|
|
||||||
counter.incrementBy(1, new Instant(1337), ImmutableList.of("test_value1"));
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(counter, ImmutableList.of("test_value1"), new Instant(1337), 1L));
|
|
||||||
|
|
||||||
counter.increment("test_value1");
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(counter, ImmutableList.of("test_value1"), new Instant(1337), 2L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementBy_incrementsValues() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues()).isEmpty();
|
|
||||||
|
|
||||||
counter.incrementBy(1, new Instant(1337), ImmutableList.of("test_value1"));
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(counter, ImmutableList.of("test_value1"), new Instant(1337), 1L));
|
|
||||||
|
|
||||||
counter.set(-10L, new Instant(1337), ImmutableList.of("test_value2"));
|
|
||||||
counter.incrementBy(5, new Instant(1337), ImmutableList.of("test_value2"));
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(counter, ImmutableList.of("test_value1"), new Instant(1337), 1L),
|
|
||||||
MetricPoint.create(counter, ImmutableList.of("test_value2"), new Instant(1337), -5L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementBy_negativeOffset_throwsException() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> counter.incrementBy(-1L, "foo"));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("The offset provided must be non-negative");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testResetAll_resetsAllValuesAndStartTimestamps() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
|
|
||||||
counter.incrementBy(3, new Instant(1337), ImmutableList.of("foo"));
|
|
||||||
counter.incrementBy(5, new Instant(1338), ImmutableList.of("moo"));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1400)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("foo"), new Instant(1337), new Instant(1400), 3L),
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("moo"), new Instant(1338), new Instant(1400), 5L));
|
|
||||||
|
|
||||||
counter.reset(new Instant(1339));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1400)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("foo"), new Instant(1339), new Instant(1400), 0L),
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("moo"), new Instant(1339), new Instant(1400), 0L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReset_resetsValuesAndStartTimestamps() {
|
|
||||||
Counter counter =
|
|
||||||
new Counter(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
|
|
||||||
counter.incrementBy(3, new Instant(1337), ImmutableList.of("foo"));
|
|
||||||
counter.incrementBy(5, new Instant(1338), ImmutableList.of("moo"));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1400)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("foo"), new Instant(1337), new Instant(1400), 3L),
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("moo"), new Instant(1338), new Instant(1400), 5L));
|
|
||||||
|
|
||||||
counter.reset(new Instant(1339), ImmutableList.of("foo"));
|
|
||||||
|
|
||||||
assertThat(counter.getTimestampedValues(new Instant(1400)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("foo"), new Instant(1339), new Instant(1400), 0L),
|
|
||||||
MetricPoint.create(
|
|
||||||
counter, ImmutableList.of("moo"), new Instant(1338), new Instant(1400), 5L));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Tests for {@link CustomFitter}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class CustomFitterTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateCustomFitter_emptyBounds_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> CustomFitter.create(ImmutableSet.of()));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("boundaries must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateCustomFitter_outOfOrderBounds_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> CustomFitter.create(ImmutableSet.of(2.0, 0.0)));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("boundaries must be sorted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateCustomFitter_hasGivenBounds() {
|
|
||||||
CustomFitter fitter = CustomFitter.create(ImmutableSortedSet.of(1.0, 2.0));
|
|
||||||
assertThat(fitter.boundaries()).containsExactly(1.0, 2.0).inOrder();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,314 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableRangeMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Range;
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link EventMetric}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class EventMetricTest {
|
|
||||||
|
|
||||||
private final DistributionFitter distributionFitter = CustomFitter.create(ImmutableSet.of(5.0));
|
|
||||||
private EventMetric metric;
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
metric =
|
|
||||||
new EventMetric(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
distributionFitter,
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCardinality_reflectsCurrentCardinality() {
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(0);
|
|
||||||
|
|
||||||
metric.record(1.0, "foo");
|
|
||||||
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(1);
|
|
||||||
|
|
||||||
metric.record(1.0, "bar");
|
|
||||||
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(2);
|
|
||||||
|
|
||||||
metric.record(1.0, "foo");
|
|
||||||
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementBy_wrongLabelValueCount_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> metric.record(1.0, "blah", "blah"));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains(
|
|
||||||
"The count of labelValues must be equal to the underlying Metric's count of labels.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRecord_updatesDistribution() {
|
|
||||||
assertThat(metric.getTimestampedValues()).isEmpty();
|
|
||||||
|
|
||||||
metric.recordMultiple(1.0, 1, new Instant(1337), ImmutableList.of("test_value1"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("test_value1"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
1.0,
|
|
||||||
0.0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
|
|
||||||
metric.record(10.0, "test_value1");
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("test_value1"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
5.5,
|
|
||||||
40.5,
|
|
||||||
2L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRecord_multipleValues_updatesDistributions() {
|
|
||||||
assertThat(metric.getTimestampedValues()).isEmpty();
|
|
||||||
|
|
||||||
metric.recordMultiple(1.0, 3, new Instant(1337), ImmutableList.of("test_value1"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("test_value1"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
1.0,
|
|
||||||
0,
|
|
||||||
3L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 3L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
|
|
||||||
metric.recordMultiple(2.0, 5, new Instant(1337), ImmutableList.of("test_value1"));
|
|
||||||
metric.recordMultiple(7.0, 10, new Instant(1337), ImmutableList.of("test_value2"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("test_value1"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
1.625,
|
|
||||||
1.875,
|
|
||||||
8L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 8L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("test_value2"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
7.0,
|
|
||||||
0,
|
|
||||||
10L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 10L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testResetAll_resetsAllValuesAndStartTimestamps() {
|
|
||||||
metric.recordMultiple(3.0, 1, new Instant(1336), ImmutableList.of("foo"));
|
|
||||||
metric.recordMultiple(5.0, 1, new Instant(1337), ImmutableList.of("moo"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("foo"),
|
|
||||||
new Instant(1336),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
3.0,
|
|
||||||
0.0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("moo"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
5.0,
|
|
||||||
0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
|
|
||||||
metric.reset(new Instant(1339));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1340)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("foo"),
|
|
||||||
new Instant(1339),
|
|
||||||
new Instant(1340),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("moo"),
|
|
||||||
new Instant(1339),
|
|
||||||
new Instant(1340),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
0.0,
|
|
||||||
0,
|
|
||||||
0L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReset_resetsValueAndStartTimestamp() {
|
|
||||||
metric.recordMultiple(3.0, 1, new Instant(1336), ImmutableList.of("foo"));
|
|
||||||
metric.recordMultiple(5.0, 1, new Instant(1337), ImmutableList.of("moo"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("foo"),
|
|
||||||
new Instant(1336),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
3.0,
|
|
||||||
0.0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("moo"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1338),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
5.0,
|
|
||||||
0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
|
|
||||||
metric.reset(new Instant(1339), ImmutableList.of("foo"));
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1340)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("foo"),
|
|
||||||
new Instant(1339),
|
|
||||||
new Instant(1340),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric,
|
|
||||||
ImmutableList.of("moo"),
|
|
||||||
new Instant(1337),
|
|
||||||
new Instant(1340),
|
|
||||||
ImmutableDistribution.create(
|
|
||||||
5.0,
|
|
||||||
0,
|
|
||||||
1L,
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build(),
|
|
||||||
distributionFitter)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link ExponentialFitter}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class ExponentialFitterTest {
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_zeroNumIntervals_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> ExponentialFitter.create(0, 3.0, 1.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("numFiniteIntervals must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_negativeNumIntervals_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> ExponentialFitter.create(-1, 3.0, 1.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("numFiniteIntervals must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_invalidBase_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> ExponentialFitter.create(3, 0.5, 1.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("base must be greater than 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_zeroScale_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> ExponentialFitter.create(3, 2.0, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("scale must not be 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_NanScale_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> ExponentialFitter.create(3, 2.0, Double.NaN));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateExponentialFitter_hasCorrectBounds() {
|
|
||||||
ExponentialFitter fitter = ExponentialFitter.create(3, 5.0, 2.0);
|
|
||||||
|
|
||||||
assertThat(fitter.boundaries()).containsExactly(2.0, 10.0, 50.0, 250.0).inOrder();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link FibonacciFitter}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class FibonacciFitterTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeNegative_throwsException() {
|
|
||||||
IllegalArgumentException e =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> FibonacciFitter.create(-1));
|
|
||||||
assertThat(e).hasMessageThat().isEqualTo("maxBucketSize must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeZero_throwsException() {
|
|
||||||
IllegalArgumentException e =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> FibonacciFitter.create(0));
|
|
||||||
assertThat(e).hasMessageThat().isEqualTo("maxBucketSize must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeOne_createsTwoBoundaries() {
|
|
||||||
assertThat(FibonacciFitter.create(1).boundaries()).containsExactly(0.0, 1.0).inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeTwo_createsThreeBoundaries() {
|
|
||||||
assertThat(FibonacciFitter.create(2).boundaries()).containsExactly(0.0, 1.0, 2.0).inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeThree_createsFourBoundaries() {
|
|
||||||
assertThat(FibonacciFitter.create(3).boundaries())
|
|
||||||
.containsExactly(0.0, 1.0, 2.0, 3.0)
|
|
||||||
.inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeFour_createsFourBoundaries() {
|
|
||||||
assertThat(FibonacciFitter.create(4).boundaries())
|
|
||||||
.containsExactly(0.0, 1.0, 2.0, 3.0)
|
|
||||||
.inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_maxBucketSizeLarge_createsFibonacciSequenceBoundaries() {
|
|
||||||
ImmutableList<Double> expectedBoundaries =
|
|
||||||
ImmutableList.of(
|
|
||||||
0.0, 1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 21.0, 34.0, 55.0, 89.0, 144.0, 233.0, 377.0, 610.0,
|
|
||||||
987.0);
|
|
||||||
assertThat(FibonacciFitter.create(1000).boundaries())
|
|
||||||
.containsExactlyElementsIn(expectedBoundaries)
|
|
||||||
.inOrder();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link LabelDescriptor}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class LabelDescriptorTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_invalidLabel_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> LabelDescriptor.create("@", "description"));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Label name must match the regex");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_blankNameField_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> LabelDescriptor.create("", "description"));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Name must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_blankDescriptionField_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LabelDescriptor.create("name", ""));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Description must not be empty");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link LinearFitter}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class LinearFitterTest {
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_zeroNumIntervals_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(0, 3.0, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("numFiniteIntervals must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_negativeNumIntervals_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(0, 3.0, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("numFiniteIntervals must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_zeroWidth_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(3, 0.0, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("width must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_negativeWidth_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(3, 0.0, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("width must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_NaNWidth_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(3, Double.NaN, 0.0));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("width must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_NaNOffset_throwsException() throws Exception {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> LinearFitter.create(3, 1.0, Double.NaN));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_hasCorrectBounds() {
|
|
||||||
LinearFitter fitter = LinearFitter.create(1, 10, 0);
|
|
||||||
|
|
||||||
assertThat(fitter.boundaries()).containsExactly(0.0, 10.0).inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_withOffset_hasCorrectBounds() {
|
|
||||||
LinearFitter fitter = LinearFitter.create(1, 10, 5);
|
|
||||||
|
|
||||||
assertThat(fitter.boundaries()).containsExactly(5.0, 15.0).inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateLinearFitter_withOffsetAndMultipleIntervals_hasCorrectBounds() {
|
|
||||||
LinearFitter fitter = LinearFitter.create(3, 10, 5);
|
|
||||||
|
|
||||||
assertThat(fitter.boundaries()).containsExactly(5.0, 15.0, 25.0, 35.0).inOrder();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +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.truth.Truth.assertThat;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.Mockito.doThrow;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.util.concurrent.Service.State;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Matchers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
/** Unit tests for {@link MetricExporter}. */
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class MetricExporterTest {
|
|
||||||
|
|
||||||
@Mock private MetricWriter writer;
|
|
||||||
@Mock private MetricPoint<?> point;
|
|
||||||
private MetricExporter exporter;
|
|
||||||
private BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue;
|
|
||||||
private final Optional<ImmutableList<MetricPoint<?>>> poisonPill = Optional.empty();
|
|
||||||
private final Optional<ImmutableList<MetricPoint<?>>> emptyBatch =
|
|
||||||
Optional.of(ImmutableList.<MetricPoint<?>>of());
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
writeQueue = new ArrayBlockingQueue<>(1);
|
|
||||||
exporter = new MetricExporter(writeQueue, writer, Executors.defaultThreadFactory());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRun_takesFromQueue_whileRunning() throws Exception {
|
|
||||||
exporter.startAsync().awaitRunning();
|
|
||||||
|
|
||||||
insertAndAssert(emptyBatch);
|
|
||||||
// Insert more batches to verify that the exporter hasn't gotten stuck
|
|
||||||
insertAndAssert(emptyBatch);
|
|
||||||
insertAndAssert(emptyBatch);
|
|
||||||
|
|
||||||
assertThat(exporter.state()).isEqualTo(State.RUNNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRun_terminates_afterPoisonPill() throws Exception {
|
|
||||||
exporter.startAsync().awaitRunning();
|
|
||||||
|
|
||||||
insertAndAssert(poisonPill);
|
|
||||||
try {
|
|
||||||
exporter.awaitTerminated(500, TimeUnit.MILLISECONDS);
|
|
||||||
} catch (TimeoutException timeout) {
|
|
||||||
fail("MetricExporter did not reach the TERMINATED state after receiving a poison pill");
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(exporter.state()).isEqualTo(State.TERMINATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRun_staysRunning_afterIOException() throws Exception {
|
|
||||||
Optional<ImmutableList<MetricPoint<?>>> threeBatch =
|
|
||||||
Optional.of(ImmutableList.of(point, point, point));
|
|
||||||
doThrow(new IOException()).when(writer).write(Matchers.<MetricPoint<?>>any());
|
|
||||||
exporter.startAsync();
|
|
||||||
|
|
||||||
insertAndAssert(threeBatch);
|
|
||||||
// Insert another batch in order to block until the exporter has processed the last one
|
|
||||||
insertAndAssert(threeBatch);
|
|
||||||
// Insert another to make sure the exporter hasn't gotten stuck
|
|
||||||
insertAndAssert(threeBatch);
|
|
||||||
|
|
||||||
assertThat(exporter.state()).isNotEqualTo(State.FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRun_writesMetrics() throws Exception {
|
|
||||||
Optional<ImmutableList<MetricPoint<?>>> threeBatch =
|
|
||||||
Optional.of(ImmutableList.of(point, point, point));
|
|
||||||
exporter.startAsync();
|
|
||||||
|
|
||||||
insertAndAssert(threeBatch);
|
|
||||||
// Insert another batch in order to block until the exporter has processed the last one
|
|
||||||
insertAndAssert(threeBatch);
|
|
||||||
|
|
||||||
// Force the exporter to finish so that the verify counts below are deterministic
|
|
||||||
insertAndAssert(poisonPill);
|
|
||||||
try {
|
|
||||||
exporter.awaitTerminated(500, TimeUnit.MILLISECONDS);
|
|
||||||
} catch (TimeoutException timeout) {
|
|
||||||
fail("MetricExporter did not reach the TERMINATED state after receiving a poison pill");
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(exporter.state()).isNotEqualTo(State.FAILED);
|
|
||||||
verify(writer, times(6)).write(point);
|
|
||||||
verify(writer, times(2)).flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to insert into the {@link BlockingQueue} and assert that the item has been
|
|
||||||
* enqueued.
|
|
||||||
*/
|
|
||||||
private void insertAndAssert(Optional<ImmutableList<MetricPoint<?>>> batch) throws Exception {
|
|
||||||
boolean isTaken = writeQueue.offer(batch, 500, TimeUnit.MILLISECONDS);
|
|
||||||
assertThat(isTaken).isTrue();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,167 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
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 org.junit.After;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for {@link MetricRegistryImpl}.
|
|
||||||
*
|
|
||||||
* <p>The MetricRegistryImpl is a singleton, so we have to be careful to empty it after every test
|
|
||||||
* to maintain a blank slate.
|
|
||||||
*/
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class MetricRegistryImplTest {
|
|
||||||
private final LabelDescriptor label =
|
|
||||||
LabelDescriptor.create("test_labelname", "test_labeldescription");
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void clearMetrics() {
|
|
||||||
MetricRegistryImpl.getDefault().unregisterAllMetrics();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegisterAndUnregister_tracksRegistrations() {
|
|
||||||
assertThat(MetricRegistryImpl.getDefault().getRegisteredMetrics()).isEmpty();
|
|
||||||
|
|
||||||
AbstractMetric<?> metric = mock(AbstractMetric.class);
|
|
||||||
MetricRegistryImpl.getDefault().registerMetric("/test/metric", metric);
|
|
||||||
|
|
||||||
assertThat(MetricRegistryImpl.getDefault().getRegisteredMetrics()).containsExactly(metric);
|
|
||||||
|
|
||||||
MetricRegistryImpl.getDefault().unregisterMetric("/test/metric");
|
|
||||||
|
|
||||||
assertThat(MetricRegistryImpl.getDefault().getRegisteredMetrics()).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewGauge_createsGauge() {
|
|
||||||
Metric<?> testGauge =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newGauge(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
ImmutableSet.of(label),
|
|
||||||
() -> ImmutableMap.of(ImmutableList.of("foo"), 1L),
|
|
||||||
Long.class);
|
|
||||||
|
|
||||||
assertThat(testGauge.getValueClass()).isSameAs(Long.class);
|
|
||||||
assertThat(testGauge.getMetricSchema())
|
|
||||||
.isEqualTo(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of(label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewCounter_createsCounter() {
|
|
||||||
IncrementableMetric testCounter =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newIncrementableMetric(
|
|
||||||
"/test_counter",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
ImmutableSet.of(label));
|
|
||||||
|
|
||||||
assertThat(testCounter.getValueClass()).isSameAs(Long.class);
|
|
||||||
assertThat(testCounter.getMetricSchema())
|
|
||||||
.isEqualTo(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/test_counter",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
Kind.CUMULATIVE,
|
|
||||||
ImmutableSet.of(label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewSettableMetric_createsSettableMetric() {
|
|
||||||
SettableMetric<Boolean> testMetric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newSettableMetric(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
ImmutableSet.of(label),
|
|
||||||
Boolean.class);
|
|
||||||
|
|
||||||
assertThat(testMetric.getValueClass()).isSameAs(Boolean.class);
|
|
||||||
assertThat(testMetric.getMetricSchema())
|
|
||||||
.isEqualTo(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of(label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewEventMetric_createsEventMetric() {
|
|
||||||
DistributionFitter fitter = CustomFitter.create(ImmutableSet.of(0.0));
|
|
||||||
EventMetric testMetric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newEventMetric(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
ImmutableSet.of(label),
|
|
||||||
fitter);
|
|
||||||
|
|
||||||
assertThat(testMetric.getValueClass()).isSameAs(Distribution.class);
|
|
||||||
assertThat(testMetric.getMetricSchema())
|
|
||||||
.isEqualTo(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
Kind.CUMULATIVE,
|
|
||||||
ImmutableSet.of(label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegister_duplicateMetric_throwsException() {
|
|
||||||
SettableMetric<Boolean> testMetric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newSettableMetric(
|
|
||||||
"/test_metric",
|
|
||||||
"test_description",
|
|
||||||
"test_valuedisplayname",
|
|
||||||
ImmutableSet.of(label),
|
|
||||||
Boolean.class);
|
|
||||||
MetricRegistryImpl.getDefault().registerMetric("/test/metric", testMetric);
|
|
||||||
|
|
||||||
IllegalStateException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> MetricRegistryImpl.getDefault().registerMetric("/test/metric", testMetric));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Duplicate metric of same name");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +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 org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.InOrder;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
/** Unit tests for {@link MetricReporter}. */
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class MetricReporterTest {
|
|
||||||
|
|
||||||
@Mock MetricRegistry registry;
|
|
||||||
@Mock Metric<?> metric;
|
|
||||||
@Mock ThreadFactory threadFactory;
|
|
||||||
@Mock MetricWriter writer;
|
|
||||||
@Mock MetricSchema metricSchema;
|
|
||||||
@Mock BlockingQueue<Optional<ImmutableList<MetricPoint<?>>>> writeQueue;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRunOneIteration_enqueuesBatch() throws Exception {
|
|
||||||
Metric<?> metric = new Counter("/name", "description", "vdn", ImmutableSet.of());
|
|
||||||
when(registry.getRegisteredMetrics()).thenReturn(ImmutableList.of(metric, metric));
|
|
||||||
MetricReporter reporter = new MetricReporter(writer, 10L, threadFactory, registry, writeQueue);
|
|
||||||
|
|
||||||
reporter.runOneIteration();
|
|
||||||
|
|
||||||
verify(writeQueue).offer(Optional.of(ImmutableList.of()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShutDown_enqueuesBatchAndPoisonPill() throws Exception {
|
|
||||||
// Set up a registry with no metrics.
|
|
||||||
when(registry.getRegisteredMetrics()).thenReturn(ImmutableList.of());
|
|
||||||
MetricReporter reporter =
|
|
||||||
spy(new MetricReporter(writer, 10L, threadFactory, registry, writeQueue));
|
|
||||||
|
|
||||||
reporter.shutDown();
|
|
||||||
|
|
||||||
verify(reporter).runOneIteration();
|
|
||||||
InOrder interactions = Mockito.inOrder(writeQueue);
|
|
||||||
interactions.verify(writeQueue).offer(Optional.of(ImmutableList.of()));
|
|
||||||
interactions.verify(writeQueue).offer(Optional.empty());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import google.registry.monitoring.metrics.MetricSchema.Kind;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link MetricSchema}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class MetricSchemaTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_blankNameField_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() ->
|
|
||||||
MetricSchema.create(
|
|
||||||
"",
|
|
||||||
"description",
|
|
||||||
"valueDisplayName",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of()));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Name must not be blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_blankDescriptionField_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() ->
|
|
||||||
MetricSchema.create(
|
|
||||||
"/name", "", "valueDisplayName", Kind.GAUGE, ImmutableSet.of()));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Description must not be blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_blankValueDisplayNameField_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() -> MetricSchema.create("/name", "description", "", Kind.GAUGE, ImmutableSet.of()));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Value Display Name must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate_nakedNames_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() ->
|
|
||||||
MetricSchema.create(
|
|
||||||
"foo", "description", "valueDisplayName", Kind.GAUGE, ImmutableSet.of()));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("Name must be URL-like and start with a '/'");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,293 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableRangeMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Range;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Tests for {@link MutableDistribution} */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class MutableDistributionTest {
|
|
||||||
|
|
||||||
private MutableDistribution distribution;
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
distribution = new MutableDistribution(CustomFitter.create(ImmutableSet.of(3.0, 5.0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneValue() {
|
|
||||||
distribution.add(5.0);
|
|
||||||
|
|
||||||
assertThat(distribution.count()).isEqualTo(1);
|
|
||||||
assertThat(distribution.mean()).isWithin(0.0).of(5.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation()).isWithin(0.0).of(0);
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(3.0), 0L)
|
|
||||||
.put(Range.closedOpen(3.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_zero() {
|
|
||||||
distribution.add(0.0);
|
|
||||||
|
|
||||||
assertThat(distribution.count()).isEqualTo(1);
|
|
||||||
assertThat(distribution.mean()).isWithin(0.0).of(0.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation()).isWithin(0.0).of(0);
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(3.0), 1L)
|
|
||||||
.put(Range.closedOpen(3.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_multipleOfOneValue() {
|
|
||||||
distribution.add(4.0, 2);
|
|
||||||
|
|
||||||
assertThat(distribution.count()).isEqualTo(2);
|
|
||||||
assertThat(distribution.mean()).isWithin(0.0).of(4.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation()).isWithin(0.0).of(0);
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(3.0), 0L)
|
|
||||||
.put(Range.closedOpen(3.0, 5.0), 2L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_positiveThenNegativeValue() {
|
|
||||||
distribution.add(2.0);
|
|
||||||
distribution.add(-2.0);
|
|
||||||
|
|
||||||
assertThat(distribution.count()).isEqualTo(2);
|
|
||||||
assertThat(distribution.mean()).isWithin(0.0).of(0.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation()).isWithin(0.0).of(8.0);
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(3.0), 2L)
|
|
||||||
.put(Range.closedOpen(3.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_wideRangeOfValues() {
|
|
||||||
distribution.add(2.0);
|
|
||||||
distribution.add(16.0);
|
|
||||||
distribution.add(128.0, 5);
|
|
||||||
distribution.add(1024.0, 0);
|
|
||||||
|
|
||||||
assertThat(distribution.count()).isEqualTo(7);
|
|
||||||
assertThat(distribution.mean()).isWithin(0.0).of(94.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation()).isWithin(0.0).of(20328.0);
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(3.0), 1L)
|
|
||||||
.put(Range.closedOpen(3.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 6L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_negativeZero_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() -> distribution.add(Double.longBitsToDouble(0x80000000)));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_NaN_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> distribution.add(Double.NaN));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_positiveInfinity_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> distribution.add(Double.POSITIVE_INFINITY));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_negativeInfinity_throwsException() {
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class, () -> distribution.add(Double.NEGATIVE_INFINITY));
|
|
||||||
assertThat(thrown).hasMessageThat().contains("value must be finite, not NaN, and not -0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_iteratedFloatingPointValues_hasLowAccumulatedError() {
|
|
||||||
for (int i = 0; i < 500; i++) {
|
|
||||||
distribution.add(1 / 3.0);
|
|
||||||
distribution.add(1 / 7.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for nine significant figures of accuracy.
|
|
||||||
assertThat(distribution.mean()).isWithin(0.000000001).of(5.0 / 21.0);
|
|
||||||
assertThat(distribution.sumOfSquaredDeviation())
|
|
||||||
.isWithin(0.000000001)
|
|
||||||
.of(1000 * 4.0 / (21.0 * 21.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_fitterWithNoFiniteIntervals_underflowValue_returnsUnderflowInterval()
|
|
||||||
throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(5.0)));
|
|
||||||
|
|
||||||
distribution.add(3.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_noFiniteIntervals_overflowValue_returnsOverflowInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(5.0)));
|
|
||||||
|
|
||||||
distribution.add(10.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_noFiniteIntervals_edgeValue_returnsOverflowInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(2.0)));
|
|
||||||
|
|
||||||
distribution.add(2.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(2.0), 0L)
|
|
||||||
.put(Range.atLeast(2.0), 1L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneFiniteInterval_underflowValue_returnsUnderflowInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(1.0, 5.0)));
|
|
||||||
|
|
||||||
distribution.add(0.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(1.0), 1L)
|
|
||||||
.put(Range.closedOpen(1.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneFiniteInterval_overflowValue_returnsOverflowInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(1.0, 5.0)));
|
|
||||||
|
|
||||||
distribution.add(10.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(1.0), 0L)
|
|
||||||
.put(Range.closedOpen(1.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneFiniteInterval_inBoundsValue_returnsInBoundsInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(1.0, 5.0)));
|
|
||||||
|
|
||||||
distribution.add(3.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(1.0), 0L)
|
|
||||||
.put(Range.closedOpen(1.0, 5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneFiniteInterval_firstEdgeValue_returnsFiniteInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(1.0, 5.0)));
|
|
||||||
|
|
||||||
distribution.add(1.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(1.0), 0L)
|
|
||||||
.put(Range.closedOpen(1.0, 5.0), 1L)
|
|
||||||
.put(Range.atLeast(5.0), 0L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAdd_oneFiniteInterval_secondEdgeValue_returnsOverflowInterval() throws Exception {
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(1.0, 5.0)));
|
|
||||||
|
|
||||||
distribution.add(5.0);
|
|
||||||
|
|
||||||
assertThat(distribution.intervalCounts())
|
|
||||||
.isEqualTo(
|
|
||||||
ImmutableRangeMap.<Double, Long>builder()
|
|
||||||
.put(Range.lessThan(1.0), 0L)
|
|
||||||
.put(Range.closedOpen(1.0, 5.0), 0L)
|
|
||||||
.put(Range.atLeast(5.0), 1L)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link StoredMetric}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class StoredMetricTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCardinality_reflectsCurrentCardinality() {
|
|
||||||
StoredMetric<Boolean> smallMetric =
|
|
||||||
new StoredMetric<>("/metric", "description", "vdn", ImmutableSet.of(), Boolean.class);
|
|
||||||
assertThat(smallMetric.getCardinality()).isEqualTo(0);
|
|
||||||
|
|
||||||
smallMetric.set(true);
|
|
||||||
|
|
||||||
assertThat(smallMetric.getCardinality()).isEqualTo(1);
|
|
||||||
|
|
||||||
StoredMetric<Boolean> dimensionalMetric =
|
|
||||||
new StoredMetric<>(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("foo", "bar")),
|
|
||||||
Boolean.class);
|
|
||||||
|
|
||||||
dimensionalMetric.set(true, "test_value1");
|
|
||||||
dimensionalMetric.set(true, "test_value2");
|
|
||||||
|
|
||||||
assertThat(dimensionalMetric.getCardinality()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSet_wrongNumberOfLabels_throwsException() {
|
|
||||||
StoredMetric<Boolean> dimensionalMetric =
|
|
||||||
new StoredMetric<>(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("label1", "bar"), LabelDescriptor.create("label2", "bar")),
|
|
||||||
Boolean.class);
|
|
||||||
|
|
||||||
IllegalArgumentException thrown =
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> dimensionalMetric.set(true, "foo"));
|
|
||||||
assertThat(thrown)
|
|
||||||
.hasMessageThat()
|
|
||||||
.contains(
|
|
||||||
"The count of labelValues must be equal to the underlying Metric's count of labels.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSet_setsValue() {
|
|
||||||
StoredMetric<Boolean> metric =
|
|
||||||
new StoredMetric<>(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")),
|
|
||||||
Boolean.class);
|
|
||||||
|
|
||||||
assertThat(metric.getTimestampedValues()).isEmpty();
|
|
||||||
|
|
||||||
metric.set(true, ImmutableList.of("test_value1"));
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(metric, ImmutableList.of("test_value1"), new Instant(1337), true));
|
|
||||||
|
|
||||||
metric.set(false, ImmutableList.of("test_value1"));
|
|
||||||
metric.set(true, ImmutableList.of("test_value2"));
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1338)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(metric, ImmutableList.of("test_value1"), new Instant(1338), false),
|
|
||||||
MetricPoint.create(metric, ImmutableList.of("test_value2"), new Instant(1338), true));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link VirtualMetric}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class VirtualMetricTest {
|
|
||||||
|
|
||||||
private final VirtualMetric<String> metric =
|
|
||||||
new VirtualMetric<>(
|
|
||||||
"/metric",
|
|
||||||
"description",
|
|
||||||
"vdn",
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "bar")),
|
|
||||||
Suppliers.ofInstance(
|
|
||||||
ImmutableMap.of(
|
|
||||||
ImmutableList.of("label_value1"), "value1",
|
|
||||||
ImmutableList.of("label_value2"), "value2")),
|
|
||||||
String.class);
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCardinality_afterGetTimestampedValues_returnsLastCardinality() {
|
|
||||||
metric.getTimestampedValues();
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetCardinality_beforeGetTimestampedValues_returnsZero() {
|
|
||||||
assertThat(metric.getCardinality()).isEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetTimestampedValues_returnsValues() {
|
|
||||||
assertThat(metric.getTimestampedValues(new Instant(1337)))
|
|
||||||
.containsExactly(
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("label_value1"), new Instant(1337), "value1"),
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("label_value2"), new Instant(1337), "value2"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package(
|
|
||||||
default_testonly = 1,
|
|
||||||
default_visibility = ["//java/google/registry:registry_project"],
|
|
||||||
)
|
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
|
|
||||||
|
|
||||||
java_library(
|
|
||||||
name = "contrib",
|
|
||||||
srcs = glob(["*.java"]),
|
|
||||||
deps = [
|
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/contrib",
|
|
||||||
"//third_party/junit",
|
|
||||||
"@com_google_guava",
|
|
||||||
"@com_google_truth",
|
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
|
||||||
"@junit",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
GenTestRules(
|
|
||||||
name = "GeneratedTestRules",
|
|
||||||
test_files = glob(["*Test.java"]),
|
|
||||||
deps = [
|
|
||||||
":contrib",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -1,189 +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.assertThat;
|
|
||||||
import static google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import google.registry.monitoring.metrics.EventMetric;
|
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class DistributionMetricSubjectTest {
|
|
||||||
|
|
||||||
private static final ImmutableSet<LabelDescriptor> LABEL_DESCRIPTORS =
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("species", "Sheep Species"),
|
|
||||||
LabelDescriptor.create("color", "Sheep Color"));
|
|
||||||
|
|
||||||
private static final EventMetric metric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newEventMetric(
|
|
||||||
"/test/event/sheep",
|
|
||||||
"Sheep Latency",
|
|
||||||
"sheeplatency",
|
|
||||||
LABEL_DESCRIPTORS,
|
|
||||||
EventMetric.DEFAULT_FITTER);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
metric.reset();
|
|
||||||
metric.record(2.5, "Domestic", "Green");
|
|
||||||
metric.record(10, "Bighorn", "Blue");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrongNumberOfLabels_fails() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class, () -> assertThat(metric).hasAnyValueForLabels("Domestic"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/sheep> has a value for labels <Domestic>."
|
|
||||||
+ " It has labeled values <[Bighorn:Blue =>"
|
|
||||||
+ " {[4.0..16.0)=1}, Domestic:Green => {[1.0..4.0)=1}]>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveWrongNumberOfLabels_succeeds() {
|
|
||||||
assertThat(metric).doesNotHaveAnyValueForLabels("Domestic");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasAnyValueForLabels_success() {
|
|
||||||
assertThat(metric)
|
|
||||||
.hasAnyValueForLabels("Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasAnyValueForLabels("Bighorn", "Blue")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveValueForLabels_success() {
|
|
||||||
assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Blue");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveValueForLabels_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() -> assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Green"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/sheep> has no value for labels <Domestic:Green>."
|
|
||||||
+ " It has a value of <{[1.0..4.0)=1}>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnexpectedValue_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() ->
|
|
||||||
assertThat(metric)
|
|
||||||
.hasAnyValueForLabels("Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues());
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/sheep> has <no other nondefault values>."
|
|
||||||
+ " It has labeled values <[Bighorn:Blue =>"
|
|
||||||
+ " {[4.0..16.0)=1}, Domestic:Green => {[1.0..4.0)=1}]>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExpectedDataSet_success() {
|
|
||||||
metric.record(7.5, "Domestic", "Green");
|
|
||||||
assertThat(metric).hasDataSetForLabels(ImmutableSet.of(2.5, 7.5), "Domestic", "Green");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExpectedDataSetsChained_success() {
|
|
||||||
metric.record(7.5, "Domestic", "Green");
|
|
||||||
assertThat(metric)
|
|
||||||
.hasDataSetForLabels(ImmutableSet.of(2.5, 7.5), "Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasDataSetForLabels(ImmutableSet.of(10), "Bighorn", "Blue")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnexpectedDataSet_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() ->
|
|
||||||
assertThat(metric)
|
|
||||||
.hasDataSetForLabels(ImmutableSet.of(2.5, 7.5), "Domestic", "Green"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/sheep> has a value of"
|
|
||||||
+ " {[1.0..4.0)=1,[4.0..16.0)=1} for labels <Domestic:Green>."
|
|
||||||
+ " It has a value of <{[1.0..4.0)=1}>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNonExistentLabels_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() ->
|
|
||||||
assertThat(metric)
|
|
||||||
.hasDataSetForLabels(ImmutableSet.of(2.5, 7.5), "Domestic", "Blue"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/sheep> has a value for labels <Domestic:Blue>."
|
|
||||||
+ " It has labeled values <[Bighorn:Blue => {[4.0..16.0)=1},"
|
|
||||||
+ " Domestic:Green => {[1.0..4.0)=1}]>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEmptyMetric_failure() {
|
|
||||||
EventMetric emptyMetric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newEventMetric(
|
|
||||||
"/test/event/goat",
|
|
||||||
"Sheep Latency",
|
|
||||||
"sheeplatency",
|
|
||||||
LABEL_DESCRIPTORS,
|
|
||||||
EventMetric.DEFAULT_FITTER);
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() ->
|
|
||||||
assertThat(emptyMetric)
|
|
||||||
.hasDataSetForLabels(ImmutableSet.of(2.5, 7.5), "Domestic", "Blue"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/event/goat> has a distribution for labels <Domestic:Blue>."
|
|
||||||
+ " It has <no values>");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +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.assertThat;
|
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.expectThrows;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class LongMetricSubjectTest {
|
|
||||||
|
|
||||||
private static final ImmutableSet<LabelDescriptor> LABEL_DESCRIPTORS =
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("species", "Sheep Species"),
|
|
||||||
LabelDescriptor.create("color", "Sheep Color"));
|
|
||||||
|
|
||||||
private static final IncrementableMetric metric =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newIncrementableMetric(
|
|
||||||
"/test/incrementable/sheep",
|
|
||||||
"Count of Sheep",
|
|
||||||
"sheepcount",
|
|
||||||
LABEL_DESCRIPTORS);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
metric.reset();
|
|
||||||
metric.increment("Domestic", "Green");
|
|
||||||
metric.incrementBy(2, "Bighorn", "Blue");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrongNumberOfLabels_fails() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class, () -> assertThat(metric).hasValueForLabels(1, "Domestic"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/incrementable/sheep> has a value for labels <Domestic>."
|
|
||||||
+ " It has labeled values <[Bighorn:Blue => 2, Domestic:Green => 1]>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveWrongNumberOfLabels_succeeds() {
|
|
||||||
assertThat(metric).doesNotHaveAnyValueForLabels("Domestic");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasValueForLabels_success() {
|
|
||||||
assertThat(metric)
|
|
||||||
.hasValueForLabels(1, "Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasValueForLabels(2, "Bighorn", "Blue")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasAnyValueForLabels_success() {
|
|
||||||
assertThat(metric)
|
|
||||||
.hasAnyValueForLabels("Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasAnyValueForLabels("Bighorn", "Blue")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveValueForLabels_success() {
|
|
||||||
assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Blue");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotHaveValueForLabels_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() -> assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Green"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/incrementable/sheep> has no value for labels <Domestic:Green>."
|
|
||||||
+ " It has a value of <1>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrongValue_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() -> assertThat(metric).hasValueForLabels(2, "Domestic", "Green"));
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/incrementable/sheep> has a value of 2"
|
|
||||||
+ " for labels <Domestic:Green>. It has a value of <1>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnexpectedValue_failure() {
|
|
||||||
AssertionError e =
|
|
||||||
expectThrows(
|
|
||||||
AssertionError.class,
|
|
||||||
() ->
|
|
||||||
assertThat(metric)
|
|
||||||
.hasValueForLabels(1, "Domestic", "Green")
|
|
||||||
.and()
|
|
||||||
.hasNoOtherValues());
|
|
||||||
assertThat(e)
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
"Not true that </test/incrementable/sheep> has <no other nondefault values>."
|
|
||||||
+ " It has labeled values <[Bighorn:Blue => 2, Domestic:Green => 1]>");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package(
|
|
||||||
default_visibility = ["//java/google/registry:registry_project"],
|
|
||||||
)
|
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
java_binary(
|
|
||||||
name = "SheepCounterExample",
|
|
||||||
srcs = ["SheepCounterExample.java"],
|
|
||||||
deps = [
|
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/stackdriver",
|
|
||||||
"@com_google_api_client",
|
|
||||||
"@com_google_apis_google_api_services_monitoring",
|
|
||||||
"@com_google_guava",
|
|
||||||
"@com_google_http_client",
|
|
||||||
"@com_google_http_client_jackson2",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -1,259 +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.example;
|
|
||||||
|
|
||||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
|
||||||
import com.google.api.client.http.HttpTransport;
|
|
||||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
|
||||||
import com.google.api.client.json.JsonFactory;
|
|
||||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
|
||||||
import com.google.api.services.monitoring.v3.Monitoring;
|
|
||||||
import com.google.api.services.monitoring.v3.MonitoringScopes;
|
|
||||||
import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
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.LinearFitter;
|
|
||||||
import google.registry.monitoring.metrics.Metric;
|
|
||||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
|
||||||
import google.registry.monitoring.metrics.MetricReporter;
|
|
||||||
import google.registry.monitoring.metrics.MetricWriter;
|
|
||||||
import google.registry.monitoring.metrics.SettableMetric;
|
|
||||||
import google.registry.monitoring.metrics.stackdriver.StackdriverWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.logging.Handler;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.LogManager;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/** A sample application which uses the Metrics API to count sheep while sleeping. */
|
|
||||||
public final class SheepCounterExample {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The code below for using a custom {@link LogManager} is only necessary to enable logging at JVM
|
|
||||||
* shutdown to show the shutdown logs of {@link MetricReporter} in this small standalone
|
|
||||||
* application.
|
|
||||||
*
|
|
||||||
* <p>It is NOT necessary for normal use of the Metrics library.
|
|
||||||
*/
|
|
||||||
static {
|
|
||||||
// must be called before any Logger method is used.
|
|
||||||
System.setProperty("java.util.logging.manager", DelayedShutdownLogManager.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(SheepCounterExample.class.getName());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time interval, in seconds, between when the {@link MetricReporter} will read {@link Metric}
|
|
||||||
* instances and enqueue them to the {@link MetricWriter}.
|
|
||||||
*
|
|
||||||
* @see MetricReporter
|
|
||||||
*/
|
|
||||||
private static final long METRICS_REPORTING_INTERVAL = 30L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum queries per second to the Stackdriver API. Contact Cloud Support to raise this from
|
|
||||||
* the default value if necessary.
|
|
||||||
*/
|
|
||||||
private static final int STACKDRIVER_MAX_QPS = 30;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of {@link com.google.api.services.monitoring.v3.model.TimeSeries} that can
|
|
||||||
* be bundled into a single {@link
|
|
||||||
* com.google.api.services.monitoring.v3.model.CreateTimeSeriesRequest}. This must be at most 200.
|
|
||||||
* Setting this lower will cause the {@link StackdriverWriter} to {@link
|
|
||||||
* StackdriverWriter#flush()} more frequently.
|
|
||||||
*/
|
|
||||||
private static final int STACKDRIVER_MAX_POINTS_PER_REQUEST = 200;
|
|
||||||
|
|
||||||
// Create some metrics to track your ZZZs.
|
|
||||||
private static final ImmutableList<String> SHEEP_COLORS =
|
|
||||||
ImmutableList.of("Green", "Yellow", "Red", "Blue");
|
|
||||||
private static final ImmutableList<String> SHEEP_SPECIES =
|
|
||||||
ImmutableList.of("Domestic", "Bighorn");
|
|
||||||
private static final ImmutableSet<LabelDescriptor> SHEEP_ATTRIBUTES =
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("color", "Sheep Color"),
|
|
||||||
LabelDescriptor.create("species", "Sheep Species"));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counters are good for tracking monotonically increasing values, like request counts or error
|
|
||||||
* counts. Or, in this case, sheep.
|
|
||||||
*/
|
|
||||||
private static final IncrementableMetric sheepCounter =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newIncrementableMetric(
|
|
||||||
"/sheep", "Counts sheep over time.", "Number of Sheep", SHEEP_ATTRIBUTES);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settable metrics are good for state indicators. For example, you could use one to track the
|
|
||||||
* lifecycle of a {@link com.google.common.util.concurrent.Service}. In this case, we are just
|
|
||||||
* using it to track the sleep state of this application.
|
|
||||||
*/
|
|
||||||
private static final SettableMetric<Boolean> isSleeping =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newSettableMetric(
|
|
||||||
"/is_sleeping",
|
|
||||||
"Tracks sleep state.",
|
|
||||||
"Sleeping?",
|
|
||||||
ImmutableSet.of(),
|
|
||||||
Boolean.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gauge metrics never need to be accessed, so the assignment here is unnecessary. You only need
|
|
||||||
* it if you plan on calling {@link Metric#getTimestampedValues()} to read the values of the
|
|
||||||
* metric in the code yourself.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final Metric<Double> sleepQuality =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newGauge(
|
|
||||||
"/sleep_quality",
|
|
||||||
"Quality of the sleep.",
|
|
||||||
"Quality",
|
|
||||||
ImmutableSet.of(),
|
|
||||||
() -> ImmutableMap.of(ImmutableList.of(), new Random().nextDouble()),
|
|
||||||
Double.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event metrics track aspects of an "event." Here, we track the fluffiness of the sheep we've
|
|
||||||
* seen.
|
|
||||||
*/
|
|
||||||
private static final EventMetric sheepFluffiness =
|
|
||||||
MetricRegistryImpl.getDefault()
|
|
||||||
.newEventMetric(
|
|
||||||
"/sheep_fluffiness",
|
|
||||||
"Measures the fluffiness of seen sheep.",
|
|
||||||
"Fill Power",
|
|
||||||
SHEEP_ATTRIBUTES,
|
|
||||||
LinearFitter.create(5, 20.0, 20.0));
|
|
||||||
|
|
||||||
private static Monitoring createAuthorizedMonitoringClient() throws IOException {
|
|
||||||
// Grab the Application Default Credentials from the environment.
|
|
||||||
// Generate these with 'gcloud beta auth application-default login'
|
|
||||||
GoogleCredential credential =
|
|
||||||
GoogleCredential.getApplicationDefault().createScoped(MonitoringScopes.all());
|
|
||||||
|
|
||||||
// Create and return the CloudMonitoring service object
|
|
||||||
HttpTransport httpTransport = new NetHttpTransport();
|
|
||||||
JsonFactory jsonFactory = new JacksonFactory();
|
|
||||||
return new Monitoring.Builder(httpTransport, jsonFactory, credential)
|
|
||||||
.setApplicationName("Monitoring Sample")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
if (args.length < 1) {
|
|
||||||
System.err.println("Missing required project argument");
|
|
||||||
System.err.printf(
|
|
||||||
"Usage: java %s gcp-project-id [verbose]\n", SheepCounterExample.class.getName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String project = args[0];
|
|
||||||
|
|
||||||
// Turn up the logging verbosity
|
|
||||||
if (args.length > 1) {
|
|
||||||
Logger log = LogManager.getLogManager().getLogger("");
|
|
||||||
log.setLevel(Level.ALL);
|
|
||||||
for (Handler h : log.getHandlers()) {
|
|
||||||
h.setLevel(Level.ALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a sample resource. In this case, a GCE Instance.
|
|
||||||
// See https://cloud.google.com/monitoring/api/resources for a list of resource types.
|
|
||||||
MonitoredResource monitoredResource =
|
|
||||||
new MonitoredResource()
|
|
||||||
.setType("gce_instance")
|
|
||||||
.setLabels(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"instance_id", "test-instance",
|
|
||||||
"zone", "us-central1-f"));
|
|
||||||
|
|
||||||
// Set up the Metrics infrastructure.
|
|
||||||
MetricWriter stackdriverWriter =
|
|
||||||
new StackdriverWriter(
|
|
||||||
createAuthorizedMonitoringClient(),
|
|
||||||
project,
|
|
||||||
monitoredResource,
|
|
||||||
STACKDRIVER_MAX_QPS,
|
|
||||||
STACKDRIVER_MAX_POINTS_PER_REQUEST);
|
|
||||||
final MetricReporter reporter =
|
|
||||||
new MetricReporter(
|
|
||||||
stackdriverWriter, METRICS_REPORTING_INTERVAL, Executors.defaultThreadFactory());
|
|
||||||
reporter.startAsync().awaitRunning();
|
|
||||||
|
|
||||||
// Set up a handler to stop sleeping on SIGINT.
|
|
||||||
Runtime.getRuntime()
|
|
||||||
.addShutdownHook(
|
|
||||||
new Thread(
|
|
||||||
() -> {
|
|
||||||
reporter.stopAsync().awaitTerminated();
|
|
||||||
// Allow the LogManager to cleanup the loggers.
|
|
||||||
DelayedShutdownLogManager.resetFinally();
|
|
||||||
}));
|
|
||||||
|
|
||||||
System.err.println("Send SIGINT (Ctrl+C) to stop sleeping.");
|
|
||||||
while (true) {
|
|
||||||
// Count some Googley sheep.
|
|
||||||
int colorIndex = new Random().nextInt(SHEEP_COLORS.size());
|
|
||||||
int speciesIndex = new Random().nextInt(SHEEP_SPECIES.size());
|
|
||||||
sheepCounter.incrementBy(1, SHEEP_COLORS.get(colorIndex), SHEEP_SPECIES.get(speciesIndex));
|
|
||||||
sheepFluffiness.record(
|
|
||||||
new Random().nextDouble() * 200,
|
|
||||||
SHEEP_COLORS.get(colorIndex),
|
|
||||||
SHEEP_SPECIES.get(speciesIndex));
|
|
||||||
isSleeping.set(true);
|
|
||||||
|
|
||||||
logger.info("zzz...");
|
|
||||||
Thread.sleep(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special {@link LogManager} with a no-op {@link LogManager#reset()} so that logging can proceed
|
|
||||||
* as usual until stopped in in another runtime shutdown hook.
|
|
||||||
*
|
|
||||||
* <p>The class is marked public because it is loaded by the JVM classloader at runtime.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public static class DelayedShutdownLogManager extends LogManager {
|
|
||||||
|
|
||||||
private static DelayedShutdownLogManager instance;
|
|
||||||
|
|
||||||
public DelayedShutdownLogManager() {
|
|
||||||
instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A no-op implementation. */
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
/* don't reset yet. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void resetFinally() {
|
|
||||||
instance.delayedReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delayedReset() {
|
|
||||||
super.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package(
|
|
||||||
default_testonly = 1,
|
|
||||||
default_visibility = ["//java/google/registry:registry_project"],
|
|
||||||
)
|
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
|
|
||||||
|
|
||||||
java_library(
|
|
||||||
name = "stackdriver",
|
|
||||||
srcs = glob(["*.java"]),
|
|
||||||
deps = [
|
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/stackdriver",
|
|
||||||
"//third_party/junit",
|
|
||||||
"@com_google_api_client",
|
|
||||||
"@com_google_apis_google_api_services_monitoring",
|
|
||||||
"@com_google_guava",
|
|
||||||
"@com_google_http_client",
|
|
||||||
"@com_google_http_client_jackson2",
|
|
||||||
"@com_google_truth",
|
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
|
||||||
"@joda_time",
|
|
||||||
"@junit",
|
|
||||||
"@org_mockito_all",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
GenTestRules(
|
|
||||||
name = "GeneratedTestRules",
|
|
||||||
test_files = glob(["*Test.java"]),
|
|
||||||
deps = [
|
|
||||||
":stackdriver",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -1,171 +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 com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
|
||||||
import com.google.api.client.http.GenericUrl;
|
|
||||||
import com.google.api.client.http.HttpContent;
|
|
||||||
import com.google.api.client.http.HttpRequest;
|
|
||||||
import com.google.api.client.http.HttpRequestFactory;
|
|
||||||
import com.google.api.client.http.HttpResponse;
|
|
||||||
import com.google.api.client.http.HttpTransport;
|
|
||||||
import com.google.api.client.http.LowLevelHttpRequest;
|
|
||||||
import com.google.api.client.http.LowLevelHttpResponse;
|
|
||||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/** A helper to create instances of {@link GoogleJsonResponseException}. */
|
|
||||||
public class GoogleJsonResponseExceptionHelper {
|
|
||||||
/**
|
|
||||||
* @param statusCode the status code that should be in the returned {@link
|
|
||||||
* GoogleJsonResponseException}
|
|
||||||
* @return a {@link GoogleJsonResponseException} with the status code {@code statusCode}
|
|
||||||
* @throws IOException shouldn't occur
|
|
||||||
*/
|
|
||||||
public static GoogleJsonResponseException create(int statusCode) throws IOException {
|
|
||||||
HttpResponse response = createHttpResponse(statusCode, null);
|
|
||||||
return GoogleJsonResponseException.from(new JacksonFactory(), response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HttpResponse createHttpResponse(int statusCode, InputStream content)
|
|
||||||
throws IOException {
|
|
||||||
FakeHttpTransport transport = new FakeHttpTransport(statusCode, content);
|
|
||||||
HttpRequestFactory factory = transport.createRequestFactory();
|
|
||||||
HttpRequest request =
|
|
||||||
factory.buildRequest(
|
|
||||||
"foo", new GenericUrl("http://example.com/bar"), new EmptyHttpContent());
|
|
||||||
request.setThrowExceptionOnExecuteError(false);
|
|
||||||
return request.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FakeHttpTransport extends HttpTransport {
|
|
||||||
private final int statusCode;
|
|
||||||
private final InputStream content;
|
|
||||||
|
|
||||||
FakeHttpTransport(int statusCode, InputStream content) {
|
|
||||||
this.statusCode = statusCode;
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
|
|
||||||
return new FakeLowLevelHttpRequest(statusCode, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FakeLowLevelHttpRequest extends LowLevelHttpRequest {
|
|
||||||
private final int statusCode;
|
|
||||||
private final InputStream content;
|
|
||||||
|
|
||||||
FakeLowLevelHttpRequest(int statusCode, InputStream content) {
|
|
||||||
this.statusCode = statusCode;
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addHeader(String name, String value) throws IOException {
|
|
||||||
// Nothing!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LowLevelHttpResponse execute() throws IOException {
|
|
||||||
return new FakeLowLevelHttpResponse(statusCode, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FakeLowLevelHttpResponse extends LowLevelHttpResponse {
|
|
||||||
private final int statusCode;
|
|
||||||
private final InputStream content;
|
|
||||||
|
|
||||||
FakeLowLevelHttpResponse(int statusCode, InputStream content) {
|
|
||||||
this.statusCode = statusCode;
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getContent() throws IOException {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContentEncoding() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getContentLength() throws IOException {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContentType() throws IOException {
|
|
||||||
return "text/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getStatusLine() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStatusCode() throws IOException {
|
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReasonPhrase() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeaderCount() throws IOException {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeaderName(int index) throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeaderValue(int index) throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class EmptyHttpContent implements HttpContent {
|
|
||||||
@Override
|
|
||||||
public long getLength() throws IOException {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return "text/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean retrySupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(OutputStream out) throws IOException {
|
|
||||||
// Nothing!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,548 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Matchers.anyString;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
|
||||||
import com.google.api.client.http.HttpResponse;
|
|
||||||
import com.google.api.client.http.HttpResponseException;
|
|
||||||
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.Explicit;
|
|
||||||
import com.google.api.services.monitoring.v3.model.Exponential;
|
|
||||||
import com.google.api.services.monitoring.v3.model.Linear;
|
|
||||||
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.TimeSeries;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import google.registry.monitoring.metrics.CustomFitter;
|
|
||||||
import google.registry.monitoring.metrics.Distribution;
|
|
||||||
import google.registry.monitoring.metrics.ExponentialFitter;
|
|
||||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
|
||||||
import google.registry.monitoring.metrics.LinearFitter;
|
|
||||||
import google.registry.monitoring.metrics.Metric;
|
|
||||||
import google.registry.monitoring.metrics.MetricPoint;
|
|
||||||
import google.registry.monitoring.metrics.MetricSchema;
|
|
||||||
import google.registry.monitoring.metrics.MetricSchema.Kind;
|
|
||||||
import google.registry.monitoring.metrics.MutableDistribution;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
/** Unit tests for {@link StackdriverWriter}. */
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class StackdriverWriterTest {
|
|
||||||
|
|
||||||
@Mock private Monitoring client;
|
|
||||||
@Mock private Monitoring.Projects projects;
|
|
||||||
@Mock private Monitoring.Projects.MetricDescriptors metricDescriptors;
|
|
||||||
@Mock private Monitoring.Projects.MetricDescriptors.Get metricDescriptorGet;
|
|
||||||
@Mock private Monitoring.Projects.TimeSeries timeSeries;
|
|
||||||
@Mock private Monitoring.Projects.MetricDescriptors.Create metricDescriptorCreate;
|
|
||||||
@Mock private Monitoring.Projects.TimeSeries.Create timeSeriesCreate;
|
|
||||||
@Mock private Metric<Long> metric;
|
|
||||||
@Mock private Metric<Boolean> boolMetric;
|
|
||||||
@Mock private Metric<Distribution> distributionMetric;
|
|
||||||
private static final String PROJECT = "PROJECT";
|
|
||||||
private static final int MAX_QPS = 10;
|
|
||||||
private static final int MAX_POINTS_PER_REQUEST = 10;
|
|
||||||
private static final MonitoredResource MONITORED_RESOURCE = new MonitoredResource();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
when(metric.getValueClass()).thenReturn(Long.class);
|
|
||||||
when(metric.getCardinality()).thenReturn(1);
|
|
||||||
when(metric.getMetricSchema())
|
|
||||||
.thenReturn(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/name",
|
|
||||||
"desc",
|
|
||||||
"vdn",
|
|
||||||
Kind.CUMULATIVE,
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "desc1"))));
|
|
||||||
// Store in an intermediate value, because Mockito hates when mocks are evaluated inside of
|
|
||||||
// thenReturn() methods.
|
|
||||||
MetricPoint<Long> longPoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("value1"), new Instant(1337), new Instant(1338), 5L);
|
|
||||||
when(metric.getTimestampedValues()).thenReturn(ImmutableList.of(longPoint));
|
|
||||||
|
|
||||||
when(boolMetric.getValueClass()).thenReturn(Boolean.class);
|
|
||||||
when(boolMetric.getMetricSchema())
|
|
||||||
.thenReturn(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/name",
|
|
||||||
"desc",
|
|
||||||
"vdn",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "desc1"))));
|
|
||||||
// Store in an intermediate value, because Mockito hates when mocks are evaluated inside of
|
|
||||||
// thenReturn() methods.
|
|
||||||
MetricPoint<Boolean> boolPoint =
|
|
||||||
MetricPoint.create(boolMetric, ImmutableList.of("foo"), new Instant(1337), true);
|
|
||||||
when(boolMetric.getTimestampedValues()).thenReturn(ImmutableList.of(boolPoint));
|
|
||||||
|
|
||||||
when(distributionMetric.getMetricSchema())
|
|
||||||
.thenReturn(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/name",
|
|
||||||
"desc",
|
|
||||||
"vdn",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "desc1"))));
|
|
||||||
when(distributionMetric.getValueClass()).thenReturn(Distribution.class);
|
|
||||||
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
|
||||||
when(client.projects()).thenReturn(projects);
|
|
||||||
when(projects.metricDescriptors()).thenReturn(metricDescriptors);
|
|
||||||
when(projects.timeSeries()).thenReturn(timeSeries);
|
|
||||||
when(metricDescriptors.create(anyString(), any(MetricDescriptor.class)))
|
|
||||||
.thenReturn(metricDescriptorCreate);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(descriptor);
|
|
||||||
when(metricDescriptors.get(anyString())).thenReturn(metricDescriptorGet);
|
|
||||||
when(metricDescriptorGet.execute()).thenReturn(descriptor);
|
|
||||||
when(timeSeries.create(anyString(), any(CreateTimeSeriesRequest.class)))
|
|
||||||
.thenReturn(timeSeriesCreate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrite_maxPoints_flushes() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
spy(
|
|
||||||
new StackdriverWriter(
|
|
||||||
client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST));
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_POINTS_PER_REQUEST; i++) {
|
|
||||||
for (MetricPoint<?> point : metric.getTimestampedValues()) {
|
|
||||||
writer.write(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(writer).flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrite_lessThanMaxPoints_doesNotFlush() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
spy(
|
|
||||||
new StackdriverWriter(
|
|
||||||
client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST));
|
|
||||||
for (int i = 0; i < MAX_POINTS_PER_REQUEST - 1; i++) {
|
|
||||||
for (MetricPoint<?> point : metric.getTimestampedValues()) {
|
|
||||||
writer.write(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(writer, never()).flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrite_invalidMetricType_throwsException() throws Exception {
|
|
||||||
when(metric.getValueClass()).thenAnswer((Answer<Class<?>>) invocation -> Object.class);
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
for (MetricPoint<?> point : metric.getTimestampedValues()) {
|
|
||||||
assertThrows(IOException.class, () -> writer.write(point));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWrite_ManyPoints_flushesTwice() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
spy(
|
|
||||||
new StackdriverWriter(
|
|
||||||
client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST));
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_POINTS_PER_REQUEST * 2; i++) {
|
|
||||||
for (MetricPoint<?> point : metric.getTimestampedValues()) {
|
|
||||||
writer.write(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(writer, times(2)).flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegisterMetric_registersWithStackdriver() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
writer.registerMetric(metric);
|
|
||||||
|
|
||||||
verify(
|
|
||||||
client
|
|
||||||
.projects()
|
|
||||||
.metricDescriptors()
|
|
||||||
.create(PROJECT, StackdriverWriter.encodeMetricDescriptor(metric)))
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registerMetric_doesNotReregisterDupe() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
writer.registerMetric(metric);
|
|
||||||
writer.registerMetric(metric);
|
|
||||||
|
|
||||||
verify(
|
|
||||||
client
|
|
||||||
.projects()
|
|
||||||
.metricDescriptors()
|
|
||||||
.create(PROJECT, StackdriverWriter.encodeMetricDescriptor(metric)))
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registerMetric_fetchesStackdriverDefinition() throws Exception {
|
|
||||||
// Stackdriver throws an Exception with the status message "ALREADY_EXISTS" when you try to
|
|
||||||
// register a metric that's already been registered, so we fake one here.
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
|
|
||||||
HttpResponse response = GoogleJsonResponseExceptionHelper.createHttpResponse(400, inputStream);
|
|
||||||
HttpResponseException.Builder httpResponseExceptionBuilder =
|
|
||||||
new HttpResponseException.Builder(response);
|
|
||||||
httpResponseExceptionBuilder.setStatusCode(400);
|
|
||||||
httpResponseExceptionBuilder.setStatusMessage("ALREADY_EXISTS");
|
|
||||||
GoogleJsonResponseException exception =
|
|
||||||
new GoogleJsonResponseException(httpResponseExceptionBuilder, null);
|
|
||||||
when(metricDescriptorCreate.execute()).thenThrow(exception);
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
writer.registerMetric(metric);
|
|
||||||
|
|
||||||
verify(client.projects().metricDescriptors().get("metric")).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registerMetric_rethrowsException() throws Exception {
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
|
|
||||||
HttpResponse response = GoogleJsonResponseExceptionHelper.createHttpResponse(400, inputStream);
|
|
||||||
HttpResponseException.Builder httpResponseExceptionBuilder =
|
|
||||||
new HttpResponseException.Builder(response);
|
|
||||||
httpResponseExceptionBuilder.setStatusCode(404);
|
|
||||||
GoogleJsonResponseException exception =
|
|
||||||
new GoogleJsonResponseException(httpResponseExceptionBuilder, null);
|
|
||||||
when(metricDescriptorCreate.execute()).thenThrow(exception);
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
assertThrows(GoogleJsonResponseException.class, () -> writer.registerMetric(metric));
|
|
||||||
assertThat(exception.getStatusCode()).isEqualTo(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_nullLabels_encodes() throws Exception {
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
|
|
||||||
HttpResponse response = GoogleJsonResponseExceptionHelper.createHttpResponse(400, inputStream);
|
|
||||||
HttpResponseException.Builder httpResponseExceptionBuilder =
|
|
||||||
new HttpResponseException.Builder(response);
|
|
||||||
httpResponseExceptionBuilder.setStatusCode(400);
|
|
||||||
httpResponseExceptionBuilder.setStatusMessage("ALREADY_EXISTS");
|
|
||||||
GoogleJsonResponseException exception =
|
|
||||||
new GoogleJsonResponseException(httpResponseExceptionBuilder, null);
|
|
||||||
when(metricDescriptorCreate.execute()).thenThrow(exception);
|
|
||||||
when(metricDescriptorGet.execute())
|
|
||||||
.thenReturn(new MetricDescriptor().setName("foo").setLabels(null));
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
writer.registerMetric(metric);
|
|
||||||
|
|
||||||
TimeSeries timeSeries =
|
|
||||||
writer.getEncodedTimeSeries(
|
|
||||||
MetricPoint.create(metric, ImmutableList.of("foo"), new Instant(1337), 10L));
|
|
||||||
|
|
||||||
assertThat(timeSeries.getMetric().getLabels()).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void encodeMetricDescriptor_simpleMetric_encodes() {
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
|
||||||
|
|
||||||
assertThat(descriptor.getType()).isEqualTo("custom.googleapis.com/name");
|
|
||||||
assertThat(descriptor.getValueType()).isEqualTo("INT64");
|
|
||||||
assertThat(descriptor.getDescription()).isEqualTo("desc");
|
|
||||||
assertThat(descriptor.getDisplayName()).isEqualTo("vdn");
|
|
||||||
assertThat(descriptor.getLabels())
|
|
||||||
.containsExactly(
|
|
||||||
new com.google.api.services.monitoring.v3.model.LabelDescriptor()
|
|
||||||
.setValueType("STRING")
|
|
||||||
.setKey("label1")
|
|
||||||
.setDescription("desc1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void encodeLabelDescriptors_simpleLabels_encodes() {
|
|
||||||
ImmutableSet<LabelDescriptor> descriptors =
|
|
||||||
ImmutableSet.of(
|
|
||||||
LabelDescriptor.create("label1", "description1"),
|
|
||||||
LabelDescriptor.create("label2", "description2"));
|
|
||||||
|
|
||||||
ImmutableList<com.google.api.services.monitoring.v3.model.LabelDescriptor> encodedDescritors =
|
|
||||||
StackdriverWriter.encodeLabelDescriptors(descriptors);
|
|
||||||
|
|
||||||
assertThat(encodedDescritors)
|
|
||||||
.containsExactly(
|
|
||||||
new com.google.api.services.monitoring.v3.model.LabelDescriptor()
|
|
||||||
.setValueType("STRING")
|
|
||||||
.setKey("label1")
|
|
||||||
.setDescription("description1"),
|
|
||||||
new com.google.api.services.monitoring.v3.model.LabelDescriptor()
|
|
||||||
.setValueType("STRING")
|
|
||||||
.setKey("label2")
|
|
||||||
.setDescription("description2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_cumulativeMetricPoint_ZeroInterval_encodesGreaterEndTime()
|
|
||||||
throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
MetricPoint<Long> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("foo"), new Instant(1337), new Instant(1337), 10L);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("INT64");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("CUMULATIVE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getInt64Value()).isEqualTo(10L);
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.338Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_cumulativeMetricPoint_nonZeroInterval_encodesSameInterval()
|
|
||||||
throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
MetricPoint<Long> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("foo"), new Instant(1337), new Instant(1339), 10L);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("INT64");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("CUMULATIVE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getInt64Value()).isEqualTo(10L);
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.339Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_gaugeMetricPoint_zeroInterval_encodesSameInterval()
|
|
||||||
throws Exception {
|
|
||||||
when(metric.getMetricSchema())
|
|
||||||
.thenReturn(
|
|
||||||
MetricSchema.create(
|
|
||||||
"/name",
|
|
||||||
"desc",
|
|
||||||
"vdn",
|
|
||||||
Kind.GAUGE,
|
|
||||||
ImmutableSet.of(LabelDescriptor.create("label1", "desc1"))));
|
|
||||||
// Store in an intermediate value, because Mockito hates when mocks are evaluated inside of
|
|
||||||
// thenReturn() methods.
|
|
||||||
MetricPoint<Long> testPoint =
|
|
||||||
MetricPoint.create(metric, ImmutableList.of("foo"), new Instant(1337), 10L);
|
|
||||||
when(metric.getTimestampedValues()).thenReturn(ImmutableList.of(testPoint));
|
|
||||||
// Store in an intermediate value, because Mockito hates when mocks are evaluated inside of
|
|
||||||
// thenReturn() methods.
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(descriptor);
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
MetricPoint<Long> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
metric, ImmutableList.of("foo"), new Instant(1337), new Instant(1337), 10L);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("INT64");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("GAUGE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getInt64Value()).isEqualTo(10L);
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_booleanMetric_encodes() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
MetricDescriptor boolDescriptor = StackdriverWriter.encodeMetricDescriptor(boolMetric);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(boolDescriptor);
|
|
||||||
MetricPoint<Boolean> nativePoint =
|
|
||||||
MetricPoint.create(boolMetric, ImmutableList.of("foo"), new Instant(1337), true);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("BOOL");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("GAUGE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getBoolValue()).isEqualTo(true);
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_distributionMetricCustomFitter_encodes() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(distributionMetric);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(descriptor);
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(CustomFitter.create(ImmutableSet.of(5.0)));
|
|
||||||
distribution.add(10.0, 5L);
|
|
||||||
distribution.add(0.0, 5L);
|
|
||||||
MetricPoint<Distribution> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
distributionMetric, ImmutableList.of("foo"), new Instant(1337), distribution);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("DISTRIBUTION");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("GAUGE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getDistributionValue())
|
|
||||||
.isEqualTo(
|
|
||||||
new com.google.api.services.monitoring.v3.model.Distribution()
|
|
||||||
.setMean(5.0)
|
|
||||||
.setSumOfSquaredDeviation(250.0)
|
|
||||||
.setCount(10L)
|
|
||||||
.setBucketCounts(ImmutableList.of(5L, 5L))
|
|
||||||
.setBucketOptions(
|
|
||||||
new BucketOptions()
|
|
||||||
.setExplicitBuckets(new Explicit().setBounds(ImmutableList.of(5.0)))));
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_distributionMetricLinearFitter_encodes() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(distributionMetric);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(descriptor);
|
|
||||||
MutableDistribution distribution = new MutableDistribution(LinearFitter.create(2, 5.0, 3.0));
|
|
||||||
distribution.add(0.0, 1L);
|
|
||||||
distribution.add(3.0, 2L);
|
|
||||||
distribution.add(10.0, 5L);
|
|
||||||
distribution.add(20.0, 5L);
|
|
||||||
MetricPoint<Distribution> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
distributionMetric, ImmutableList.of("foo"), new Instant(1337), distribution);
|
|
||||||
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("DISTRIBUTION");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("GAUGE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getDistributionValue())
|
|
||||||
.isEqualTo(
|
|
||||||
new com.google.api.services.monitoring.v3.model.Distribution()
|
|
||||||
.setMean(12.0)
|
|
||||||
.setSumOfSquaredDeviation(646.0)
|
|
||||||
.setCount(13L)
|
|
||||||
.setBucketCounts(ImmutableList.of(1L, 2L, 5L, 5L))
|
|
||||||
.setBucketOptions(
|
|
||||||
new BucketOptions()
|
|
||||||
.setLinearBuckets(
|
|
||||||
new Linear().setNumFiniteBuckets(2).setWidth(5.0).setOffset(3.0))));
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getEncodedTimeSeries_distributionMetricExponentialFitter_encodes() throws Exception {
|
|
||||||
StackdriverWriter writer =
|
|
||||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
|
||||||
|
|
||||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(distributionMetric);
|
|
||||||
when(metricDescriptorCreate.execute()).thenReturn(descriptor);
|
|
||||||
MutableDistribution distribution =
|
|
||||||
new MutableDistribution(ExponentialFitter.create(2, 3.0, 0.5));
|
|
||||||
distribution.add(0.0, 1L);
|
|
||||||
distribution.add(3.0, 2L);
|
|
||||||
distribution.add(10.0, 5L);
|
|
||||||
distribution.add(20.0, 5L);
|
|
||||||
MetricPoint<Distribution> nativePoint =
|
|
||||||
MetricPoint.create(
|
|
||||||
distributionMetric, ImmutableList.of("foo"), new Instant(1337), distribution);
|
|
||||||
|
|
||||||
TimeSeries timeSeries = writer.getEncodedTimeSeries(nativePoint);
|
|
||||||
|
|
||||||
assertThat(timeSeries.getValueType()).isEqualTo("DISTRIBUTION");
|
|
||||||
assertThat(timeSeries.getMetricKind()).isEqualTo("GAUGE");
|
|
||||||
List<Point> points = timeSeries.getPoints();
|
|
||||||
assertThat(points).hasSize(1);
|
|
||||||
Point point = points.get(0);
|
|
||||||
assertThat(point.getValue().getDistributionValue())
|
|
||||||
.isEqualTo(
|
|
||||||
new com.google.api.services.monitoring.v3.model.Distribution()
|
|
||||||
.setMean(12.0)
|
|
||||||
.setSumOfSquaredDeviation(646.0)
|
|
||||||
.setCount(13L)
|
|
||||||
.setBucketCounts(ImmutableList.of(1L, 0L, 2L, 10L))
|
|
||||||
.setBucketOptions(
|
|
||||||
new BucketOptions()
|
|
||||||
.setExponentialBuckets(
|
|
||||||
new Exponential()
|
|
||||||
.setNumFiniteBuckets(2)
|
|
||||||
.setGrowthFactor(3.0)
|
|
||||||
.setScale(0.5))));
|
|
||||||
assertThat(point.getInterval().getEndTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
assertThat(point.getInterval().getStartTime()).isEqualTo("1970-01-01T00:00:01.337Z");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,14 +15,14 @@ java_library(
|
||||||
"quota/testdata/*.yaml",
|
"quota/testdata/*.yaml",
|
||||||
]),
|
]),
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/monitoring/metrics",
|
|
||||||
"//java/google/registry/monitoring/metrics/contrib",
|
|
||||||
"//java/google/registry/proxy",
|
"//java/google/registry/proxy",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"//javatests/google/registry/testing",
|
"//javatests/google/registry/testing",
|
||||||
"@com_beust_jcommander",
|
"@com_beust_jcommander",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_contrib",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
"@com_google_truth",
|
"@com_google_truth",
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
"@com_google_truth_extensions_truth_java8_extension",
|
||||||
"@io_netty_buffer",
|
"@io_netty_buffer",
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
package google.registry.proxy.metric;
|
package google.registry.proxy.metric;
|
||||||
|
|
||||||
import static google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
import static com.google.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||||
import static google.registry.proxy.TestUtils.makeHttpPostRequest;
|
import static google.registry.proxy.TestUtils.makeHttpPostRequest;
|
||||||
import static google.registry.proxy.TestUtils.makeHttpResponse;
|
import static google.registry.proxy.TestUtils.makeHttpResponse;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
package google.registry.proxy.metric;
|
package google.registry.proxy.metric;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.DefaultChannelId;
|
import io.netty.channel.DefaultChannelId;
|
||||||
|
|
|
@ -14,7 +14,6 @@ java_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
"//java/google/registry/monitoring/metrics/contrib",
|
|
||||||
"//java/google/registry/rdap",
|
"//java/google/registry/rdap",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request/auth",
|
"//java/google/registry/request/auth",
|
||||||
|
@ -26,6 +25,7 @@ java_library(
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
|
"@com_google_monitoring_client_contrib",
|
||||||
"@com_google_truth",
|
"@com_google_truth",
|
||||||
"@com_google_truth_extensions_truth_java8_extension",
|
"@com_google_truth_extensions_truth_java8_extension",
|
||||||
"@com_googlecode_json_simple",
|
"@com_googlecode_json_simple",
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
package google.registry.rdap;
|
package google.registry.rdap;
|
||||||
|
|
||||||
import static google.registry.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
import static com.google.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
||||||
import static google.registry.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue