mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 07:57:13 +02:00
Refactor Stackdriver-over-REST specific code to a different package
Downstream users who use gRPC rather than REST don't want to pull down rest-related dependencies. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=145834701
This commit is contained in:
parent
bfc4841761
commit
3f9c53b850
10 changed files with 12 additions and 1127 deletions
|
@ -867,7 +867,7 @@ public final class RegistryConfig {
|
|||
* Maximum QPS for the Google Cloud Monitoring V3 (aka Stackdriver) API. The QPS limit can be
|
||||
* adjusted by contacting Cloud Support.
|
||||
*
|
||||
* @see google.registry.monitoring.metrics.StackdriverWriter
|
||||
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
|
||||
*/
|
||||
@Provides
|
||||
@Config("stackdriverMaxQps")
|
||||
|
@ -879,7 +879,7 @@ public final class RegistryConfig {
|
|||
* Maximum number of points that can be sent to Stackdriver in a single TimeSeries.Create API
|
||||
* call.
|
||||
*
|
||||
* @see google.registry.monitoring.metrics.StackdriverWriter
|
||||
* @see google.registry.monitoring.metrics.stackdriver.StackdriverWriter
|
||||
*/
|
||||
@Provides
|
||||
@Config("stackdriverMaxPointsPerRequest")
|
||||
|
|
|
@ -8,15 +8,11 @@ java_library(
|
|||
name = "metrics",
|
||||
srcs = glob(["*.java"]),
|
||||
deps = [
|
||||
"@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",
|
||||
],
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
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;
|
||||
|
@ -32,7 +33,8 @@ public abstract class MetricPoint<V> {
|
|||
* <p>Callers should insure that the count of {@code labelValues} matches the count of labels for
|
||||
* the given metric.
|
||||
*/
|
||||
static <V> MetricPoint<V> create(
|
||||
@VisibleForTesting
|
||||
public static <V> MetricPoint<V> create(
|
||||
Metric<V> metric, ImmutableList<String> labelValues, Instant timestamp, V value) {
|
||||
MetricsUtils.checkLabelValuesLength(metric, labelValues);
|
||||
|
||||
|
@ -47,7 +49,8 @@ public abstract class MetricPoint<V> {
|
|||
* <p>Callers should insure that the count of {@code labelValues} matches the count of labels for
|
||||
* the given metric.
|
||||
*/
|
||||
static <V> MetricPoint<V> create(
|
||||
@VisibleForTesting
|
||||
public static <V> MetricPoint<V> create(
|
||||
Metric<V> metric,
|
||||
ImmutableList<String> labelValues,
|
||||
Instant startTime,
|
||||
|
|
|
@ -17,6 +17,7 @@ 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. */
|
||||
|
@ -36,7 +37,8 @@ public abstract class MetricSchema {
|
|||
* 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.
|
||||
*/
|
||||
static MetricSchema create(
|
||||
@VisibleForTesting
|
||||
public static MetricSchema create(
|
||||
String name,
|
||||
String description,
|
||||
String valueDisplayName,
|
||||
|
|
|
@ -1,400 +0,0 @@
|
|||
// Copyright 2016 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 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.MetricSchema.Kind;
|
||||
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 javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
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 MetricSchema.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.
|
||||
*/
|
||||
@Inject
|
||||
public StackdriverWriter(
|
||||
Monitoring monitoringClient,
|
||||
@Named("stackdriverGcpProject") String project,
|
||||
MonitoredResource monitoredResource,
|
||||
@Named("stackdriverMaxQps") int maxQps,
|
||||
@Named("stackdriverMaxPointsPerRequest") 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 <V> MetricDescriptor encodeMetricDescriptor(
|
||||
google.registry.monitoring.metrics.Metric<V> 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.<LabelDescriptor>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());
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ java_library(
|
|||
"//java/google/registry/config",
|
||||
"//java/google/registry/model",
|
||||
"//java/google/registry/monitoring/metrics",
|
||||
"//java/google/registry/monitoring/metrics/stackdriver",
|
||||
"//java/google/registry/request",
|
||||
"//java/google/registry/util",
|
||||
"//third_party/java/objectify:objectify-v4_1",
|
||||
|
|
|
@ -28,7 +28,7 @@ import dagger.Module;
|
|||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.monitoring.metrics.MetricWriter;
|
||||
import google.registry.monitoring.metrics.StackdriverWriter;
|
||||
import google.registry.monitoring.metrics.stackdriver.StackdriverWriter;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import javax.inject.Named;
|
||||
|
|
|
@ -12,11 +12,7 @@ java_library(
|
|||
srcs = glob(["*.java"]),
|
||||
deps = [
|
||||
"//java/google/registry/monitoring/metrics",
|
||||
"@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",
|
||||
"@joda_time",
|
||||
"@junit",
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2016 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.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,542 +0,0 @@
|
|||
// Copyright 2016 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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.fail;
|
||||
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.MetricSchema.Kind;
|
||||
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.invocation.InvocationOnMock;
|
||||
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> mockMetric;
|
||||
@Mock private MetricSchema schema;
|
||||
@Mock MetricPoint<Long> metricPoint;
|
||||
private Counter metric;
|
||||
private MetricDescriptor descriptor;
|
||||
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 {
|
||||
metric =
|
||||
new Counter(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")));
|
||||
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 {
|
||||
// The counter must be set once in order for there to be values to send.
|
||||
metric.set(0L, new Instant(1337), ImmutableList.of("some_value"));
|
||||
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(new Instant(1337))) {
|
||||
writer.write(point);
|
||||
}
|
||||
}
|
||||
|
||||
verify(writer).flush();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_lessThanMaxPoints_doesNotFlush() throws Exception {
|
||||
// The counter must be set once in order for there to be values to send.
|
||||
metric.set(0L, new Instant(1337), ImmutableList.of("some_value"));
|
||||
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(new Instant(1337))) {
|
||||
writer.write(point);
|
||||
}
|
||||
}
|
||||
|
||||
verify(writer, never()).flush();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_invalidMetricType_throwsException() throws Exception {
|
||||
when(mockMetric.getValueClass())
|
||||
.thenAnswer(
|
||||
new Answer<Class<?>>() {
|
||||
@Override
|
||||
public Class<?> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return Object.class;
|
||||
}
|
||||
});
|
||||
when(mockMetric.getMetricSchema()).thenReturn(schema);
|
||||
when(mockMetric.getTimestampedValues()).thenReturn(ImmutableList.of(metricPoint));
|
||||
when(schema.kind()).thenReturn(Kind.CUMULATIVE);
|
||||
when(metricPoint.metric()).thenReturn(mockMetric);
|
||||
StackdriverWriter writer =
|
||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
||||
|
||||
for (MetricPoint<?> point : mockMetric.getTimestampedValues()) {
|
||||
try {
|
||||
writer.write(point);
|
||||
fail("expected IllegalArgumentException");
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_ManyPoints_flushesTwice() throws Exception {
|
||||
// The counter must be set once in order for there to be values to send.
|
||||
metric.set(0L, new Instant(1337), ImmutableList.of("some_value"));
|
||||
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(new Instant(1337))) {
|
||||
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);
|
||||
|
||||
try {
|
||||
writer.registerMetric(metric);
|
||||
fail("Expected GoogleJsonResponseException");
|
||||
} catch (GoogleJsonResponseException expected) {
|
||||
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("label")
|
||||
.setDescription("description"));
|
||||
}
|
||||
|
||||
@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 {
|
||||
StackdriverWriter writer =
|
||||
new StackdriverWriter(client, PROJECT, MONITORED_RESOURCE, MAX_QPS, MAX_POINTS_PER_REQUEST);
|
||||
Metric<Long> metric =
|
||||
new StoredMetric<>(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")),
|
||||
Long.class);
|
||||
when(metricDescriptorCreate.execute())
|
||||
.thenReturn(StackdriverWriter.encodeMetricDescriptor(metric));
|
||||
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);
|
||||
Metric<Boolean> boolMetric =
|
||||
new StoredMetric<>(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")),
|
||||
Boolean.class);
|
||||
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);
|
||||
Metric<Distribution> metric =
|
||||
new StoredMetric<>(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")),
|
||||
Distribution.class);
|
||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
||||
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(metric, 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);
|
||||
Metric<Distribution> metric =
|
||||
new StoredMetric<>(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")),
|
||||
Distribution.class);
|
||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
||||
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(metric, 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);
|
||||
Metric<Distribution> metric =
|
||||
new StoredMetric<>(
|
||||
"/name",
|
||||
"desc",
|
||||
"vdn",
|
||||
ImmutableSet.of(LabelDescriptor.create("label", "description")),
|
||||
Distribution.class);
|
||||
MetricDescriptor descriptor = StackdriverWriter.encodeMetricDescriptor(metric);
|
||||
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(metric, 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");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue