// 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.proxy.metric; import com.google.common.collect.ImmutableSet; import google.registry.monitoring.metrics.CustomFitter; import google.registry.monitoring.metrics.EventMetric; import google.registry.monitoring.metrics.ExponentialFitter; import google.registry.monitoring.metrics.FibonacciFitter; import google.registry.monitoring.metrics.IncrementableMetric; import google.registry.monitoring.metrics.LabelDescriptor; import google.registry.monitoring.metrics.MetricRegistryImpl; import google.registry.util.NonFinalForTesting; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import javax.inject.Inject; /** Backend metrics instrumentation. */ public class BackendMetrics { // Maximum request size is defined in the config file, this is not realistic and we'd be out of // memory when the size approach 1 GB. private static final CustomFitter DEFAULT_SIZE_FITTER = FibonacciFitter.create(1073741824); // Maximum 1 hour latency, this is not specified by the spec, but given we have a one hour idle // timeout, it seems reasonable that maximum latency is set to 1 hour as well. If we are // approaching anywhere near 1 hour latency, we'd be way out of SLO anyway. private static final ExponentialFitter DEFAULT_LATENCY_FITTER = ExponentialFitter.create(22, 2, 1.0); private static final ImmutableSet LABELS = ImmutableSet.of( LabelDescriptor.create("protocol", "Name of the protocol."), LabelDescriptor.create( "client_cert_hash", "SHA256 hash of the client certificate, if available.")); static final IncrementableMetric requestsCounter = MetricRegistryImpl.getDefault() .newIncrementableMetric( "/proxy/backend/requests", "Total number of requests send to the backend.", "Requests", LABELS); static final IncrementableMetric responsesCounter = MetricRegistryImpl.getDefault() .newIncrementableMetric( "/proxy/backend/responses", "Total number of responses received by the backend.", "Responses", ImmutableSet.builder() .addAll(LABELS) .add(LabelDescriptor.create("status", "HTTP status code.")) .build()); static final EventMetric requestBytes = MetricRegistryImpl.getDefault() .newEventMetric( "/proxy/backend/request_bytes", "Size of the backend requests sent.", "Bytes", LABELS, DEFAULT_SIZE_FITTER); static final EventMetric responseBytes = MetricRegistryImpl.getDefault() .newEventMetric( "/proxy/backend/response_bytes", "Size of the backend responses received.", "Bytes", LABELS, DEFAULT_SIZE_FITTER); static final EventMetric latencyMs = MetricRegistryImpl.getDefault() .newEventMetric( "/proxy/backend/latency_ms", "Round-trip time between a request sent and its corresponding response received.", "Milliseconds", LABELS, DEFAULT_LATENCY_FITTER); @Inject BackendMetrics() {} /** * Resets all backend metrics. * *

This should only used in tests to clear out states. No production code should call this * function. */ void resetMetric() { requestBytes.reset(); requestsCounter.reset(); responseBytes.reset(); responsesCounter.reset(); latencyMs.reset(); } @NonFinalForTesting public void requestSent(String protocol, String certHash, FullHttpRequest request) { requestsCounter.increment(protocol, certHash); requestBytes.record(request.content().readableBytes(), protocol, certHash); } @NonFinalForTesting public void responseReceived( String protocol, String certHash, FullHttpResponse response, long latency) { latencyMs.record(latency, protocol, certHash); responseBytes.record(response.content().readableBytes(), protocol, certHash); responsesCounter.increment(protocol, certHash, response.status().toString()); } }