All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.metrics.internal.cloudwatch.PredefinedMetricTransformer Maven / Gradle / Ivy

Go to download

This package holds the classes for uploading the clientside metrics collected from AWS Java SDK to Amazon CloudWatch

There is a newer version: 1.12.778
Show newest version
/*
 * Copyright 2010-2024 Amazon.com, Inc. or its affiliates. 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.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 com.amazonaws.metrics.internal.cloudwatch;

import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.Request;
import com.amazonaws.Response;
import com.amazonaws.metrics.MetricType;
import com.amazonaws.metrics.RequestMetricCollector;
import com.amazonaws.metrics.internal.cloudwatch.spi.AWSMetricTransformerFactory;
import com.amazonaws.metrics.internal.cloudwatch.spi.Dimensions;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.StandardUnit;
import com.amazonaws.util.AWSRequestMetrics;
import com.amazonaws.util.AWSRequestMetrics.Field;
import com.amazonaws.util.TimingInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.annotation.ThreadSafe;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.amazonaws.metrics.internal.cloudwatch.spi.MetricData.newMetricDatum;
import static com.amazonaws.metrics.internal.cloudwatch.spi.RequestMetricTransformer.Utils.endTimestamp;
/**
 * Used to transform the predefined metrics of the AWS SDK into instances of
 * {@link MetricDatum}.
 *
 * See http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/
 * publishingMetrics.html
 *
 * @see AWSRequestMetrics
 * @see RequestMetricCollector
 */
@ThreadSafe
public class PredefinedMetricTransformer {
    private static final Log log = LogFactory.getLog(PredefinedMetricTransformer.class);
    static final boolean INCLUDE_REQUEST_TYPE = true;
    static final boolean EXCLUDE_REQUEST_TYPE = !INCLUDE_REQUEST_TYPE;

    /**
     * Returns a non-null list of metric datum for the metrics collected for the
     * given request/response.
     *
     * @param metricType the request metric type
     */
    public List toMetricData(MetricType metricType, Request request, Response response) {
        if (metricType instanceof Field) {
            // Predefined metrics across all AWS http clients
            Field predefined = (Field) metricType;
            switch(predefined) {
                case HttpClientRetryCount:
                case HttpClientPoolAvailableCount:
                case HttpClientPoolLeasedCount:
                case HttpClientPoolPendingCount:
                    return metricOfCount(predefined, request, response);
                case RequestCount:  // intentionally fall through to reuse the same routine as RetryCount
                case RetryCount:
                    return metricOfRequestOrRetryCount(predefined, request, response);
                case ThrottledRetryCount: // drop through
                case RetryCapacityConsumed:
                    return counterMetricOf(predefined, request, response, EXCLUDE_REQUEST_TYPE);
                case ResponseProcessingTime: // drop through
                case RequestSigningTime: // drop through
                    return latencyMetricOf(predefined, request, response, EXCLUDE_REQUEST_TYPE);
                case ClientExecuteTime:
                    return latencyOfClientExecuteTime(request, response);
                case HttpClientSendRequestTime:
                case HttpClientReceiveResponseTime:
                case HttpRequestTime:
                case HttpSocketReadTime:
                    return latencyMetricOf(predefined, request, response, INCLUDE_REQUEST_TYPE);
                case Exception:
                case ThrottleException:
                    return counterMetricOf(predefined, request, response, INCLUDE_REQUEST_TYPE);
                default:
                    break;
            }
        }
        // Predefined metrics for specific service clients
        for (AWSMetricTransformerFactory aws: AWSMetricTransformerFactory.values()) {
            if (metricType.name().startsWith(aws.name())) {
                List metricData = aws.getRequestMetricTransformer()
                        .toMetricData(metricType, request, response);
                if (metricData != null)
                    return metricData;
                break;
            }
        }
        if (log.isDebugEnabled()) {
            AmazonWebServiceRequest origReq = request == null ? null : request
                    .getOriginalRequest();
            String reqClassName = origReq == null ? null : origReq.getClass().getName();
            log.debug("No request metric transformer can be found for metric type "
                    + metricType.name() + " for " + reqClassName);
        }
        return Collections.emptyList();
    }

    /**
     * Returns a list with a single metric datum for the specified retry or
     * request count predefined metric; or an empty list if there is none.
     *
     * @param metricType
     *            must be either {@link Field#RequestCount} or
     *            {@link Field#RetryCount}; or else GIGO.
     */
    protected List metricOfRequestOrRetryCount(
            Field metricType, Request req, Object resp) {
        AWSRequestMetrics m = req.getAWSRequestMetrics();
        TimingInfo ti = m.getTimingInfo();
        // Always retrieve the request count even for retry which is equivalent
        // to the number of requests minus one.
        Number counter = ti.getCounter(Field.RequestCount.name());
        if (counter == null) {
            // this is possible if one of the request handlers screwed up
            return Collections.emptyList();
        }
        int requestCount = counter.intValue();
        if (requestCount < 1) {
            if (log.isDebugEnabled()) {
                log.debug("request count must be at least one");
            }
            return Collections.emptyList();
        }
        final double count = metricType == Field.RequestCount
                           ? requestCount
                           : requestCount-1 // retryCount = requestCount - 1
                           ;
        if (count < 1) {
            return Collections.emptyList();
        } else {
            return Collections.singletonList(new MetricDatum()
                .withMetricName(req.getServiceName())
                .withDimensions(new Dimension()
                    .withName(Dimensions.MetricType.name())
                    .withValue(metricType.name()))
                .withUnit(StandardUnit.Count)
                .withValue(Double.valueOf(count))
                .withTimestamp(endTimestamp(ti)))
                ;
        }
    }

    protected List metricOfCount(
            Field metricType, Request req, Object resp) {
        AWSRequestMetrics m = req.getAWSRequestMetrics();
        TimingInfo ti = m.getTimingInfo();
        Number counter = ti.getCounter(metricType.name());
        if (counter == null) {
            return Collections.emptyList();
        }
        final double count = counter.doubleValue();
        if (count < 1) {
            return Collections.emptyList();
        } else {
            return Collections.singletonList(new MetricDatum()
                .withMetricName(req.getServiceName())
                .withDimensions(new Dimension()
                    .withName(Dimensions.MetricType.name())
                    .withValue(metricType.name()))
                .withUnit(StandardUnit.Count)
                .withValue(Double.valueOf(count))
                .withTimestamp(endTimestamp(ti)))
                ;
        }
    }

    /**
     * Returns all the latency metric data recorded for the specified metric
     * event type; or an empty list if there is none. The number of metric datum
     * in the returned list should be exactly one when there is no retries, or
     * more than one when there are retries.
     *
     * @param includesRequestType
     *            true iff the "request" dimension is to be included;
     */
    protected List latencyMetricOf(MetricType metricType,
            Request req, Object response, boolean includesRequestType) {
        AWSRequestMetrics m = req.getAWSRequestMetrics();
        TimingInfo root = m.getTimingInfo();
        final String metricName = metricType.name();
        List subMeasures =
            root.getAllSubMeasurements(metricName);
        if (subMeasures != null) {
            List result =
                new ArrayList(subMeasures.size());
            for (TimingInfo sub : subMeasures) {
                if (sub.isEndTimeKnown()) { // being defensive
                    List dims = new ArrayList();
                    dims.add(new Dimension()
                            .withName(Dimensions.MetricType.name())
                            .withValue(metricName));
                    // Either a non request type specific datum is created per
                    // sub-measurement, or a request type specific one is
                    // created but not both
                    if (includesRequestType) {
                        dims.add(new Dimension()
                                .withName(Dimensions.RequestType.name())
                                .withValue(requestType(req)));
                    }
                    MetricDatum datum = new MetricDatum()
                        .withMetricName(req.getServiceName())
                        .withDimensions(dims)
                        .withUnit(StandardUnit.Milliseconds)
                        .withValue(sub.getTimeTakenMillisIfKnown());
                    result.add(datum);
                }
            }
            return result;
        }
        return Collections.emptyList();
    }

    /**
     * Returns a request type specific metrics for
     * {@link Field#ClientExecuteTime} which is special in the sense that it
     * makes a more accurate measurement by taking the {@link TimingInfo} at the
     * root into account.
     */
    protected List latencyOfClientExecuteTime(Request req, Object response) {
        AWSRequestMetrics m = req.getAWSRequestMetrics();
        TimingInfo root = m.getTimingInfo();
        final String metricName = Field.ClientExecuteTime.name();
        if (root.isEndTimeKnown()) { // being defensive
            List dims = new ArrayList();
            dims.add(new Dimension()
                    .withName(Dimensions.MetricType.name())
                    .withValue(metricName));
            // request type specific
            dims.add(new Dimension()
                    .withName(Dimensions.RequestType.name())
                    .withValue(requestType(req)));
            MetricDatum datum = new MetricDatum()
                .withMetricName(req.getServiceName())
                .withDimensions(dims)
                .withUnit(StandardUnit.Milliseconds)
                .withValue(root.getTimeTakenMillisIfKnown());
            return Collections.singletonList(datum);
        }
        return Collections.emptyList();
    }

    /**
     * Returns the name of the type of request.
     */
    private String requestType(Request req) {
        return req.getOriginalRequest().getClass().getSimpleName();
    }

    /**
     * Returns a list of metric datum recorded for the specified counter metric
     * type; or an empty list if there is none.
     *
     * @param includesRequestType
     *            true iff an additional metric datum is to be created that
     *            includes the "request" dimension
     */
    protected List counterMetricOf(MetricType type,
            Request req, Object resp, boolean includesRequestType) {
        AWSRequestMetrics m = req.getAWSRequestMetrics();
        TimingInfo ti = m.getTimingInfo();
        final String metricName = type.name();
        Number counter = ti.getCounter(metricName);
        if (counter == null) {
            return Collections.emptyList();
        }
        int count = counter.intValue();
        if (count < 1) {
            if (log.isDebugEnabled()) {
                log.debug("Count must be at least one");
            }
            return Collections.emptyList();
        }
        final List result = new ArrayList();
        final Dimension metricDimension = new Dimension()
            .withName(Dimensions.MetricType.name())
            .withValue(metricName);
        // non-request type specific metric datum
        final MetricDatum first = new MetricDatum()
            .withMetricName(req.getServiceName())
            .withDimensions(metricDimension)
            .withUnit(StandardUnit.Count)
            .withValue(Double.valueOf(count))
            .withTimestamp(endTimestamp(ti));
        result.add(first);
        if (includesRequestType) {
            // additional request type specific metric datum
            Dimension requestDimension = new Dimension()
                .withName(Dimensions.RequestType.name())
                .withValue(requestType(req));
            final MetricDatum second =
                newMetricDatum(first, metricDimension, requestDimension);
            result.add(second);
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy