org.attribyte.metrics.cloudwatch.CloudwatchReporter.java Source code

Java tutorial

Introduction

Here is the source code for org.attribyte.metrics.cloudwatch.CloudwatchReporter.java

Source

/*
 * Copyright 2015 Attribyte, LLC
 *
 * 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 org.attribyte.metrics.cloudwatch;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsyncClient;
import com.blacklocus.metrics.CloudWatchReporter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.ImmutableMap;
import org.attribyte.api.InitializationException;
import org.attribyte.metrics.RegistryTranslation;
import org.attribyte.metrics.Reporter;
import org.attribyte.metrics.ReporterBase;
import org.attribyte.util.InitUtil;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Reports metrics to CloudWatch.
 * <p>
 *    You'll want to be very careful about the names and format of metrics
 *    you report to CloudWatch. It can be expensive. As such, this reporter
 *    expects to extract and rename particular metrics from the full registry
 *    that, for a normal application, is likely to contain far more metrics than you'll want to report.
 *    They are defined as properties like this:
 *      <code>metrics-reporting.cloudwatch.translate.org.attribyte.pubsub.callbacks=NumberOfMessagesSent</code>
 *    See: https://github.com/blacklocus/metrics-cloudwatch
 *      "In case you forgot, AWS costs money. Metrics and monitoring can easily become the most expensive part of your stack.
 *      So be wary of metrics explosions."
 *    If you really want to use the full registry set: <code>metrics-reporting.cloudwatch.disableTranslate=true</code>
 * </p>
 */
public class CloudwatchReporter extends ReporterBase implements Reporter {

    /**
     * The AWS access key id ('accessKeyId').
     */
    public static final String AWS_ACCESS_KEY_ID_PROPERTY = "accessKeyId";

    /**
     * The AWS access key secret ('accessKeySecret').
     */
    public static final String AWS_ACCESS_KEY_SECRET_PROPERTY = "accessKeySecret";

    /**
     * The namespace to which metrics will be reported ('metricNamespace').
     */
    public static final String METRIC_NAMESPACE_PROPERTY = "metricNamespace";

    /**
     * Disable translation and use the metrics as they are in the original registry.
     */
    public static final String DISABLE_TRANSLATE_PROPERTY = "disableTranslate";

    /**
     * Should an instance profile be used?
     */
    public static final String USE_INSTANCE_CREDENTIALS_PROPERTY = "useInstanceCredentials";

    /**
     * Should the default provider chain be used?
     */
    public static final String USE_DEFAULT_PROVIDER_CHAIN_PROPERTY = "useDefaultProviderChain";

    @Override
    public void init(final String name, final Properties _props, final MetricRegistry registry,
            final MetricFilter filter) throws Exception {
        if (isInit.compareAndSet(false, true)) {
            init(name, _props);

            boolean useInstanceCredentials = init.getProperty(USE_INSTANCE_CREDENTIALS_PROPERTY, "false")
                    .equalsIgnoreCase("true");
            boolean useDefaultProviderChain = init.getProperty(USE_DEFAULT_PROVIDER_CHAIN_PROPERTY, "false")
                    .equalsIgnoreCase("true");

            if (useDefaultProviderChain) {
                client = new AmazonCloudWatchAsyncClient(new DefaultAWSCredentialsProviderChain());
            } else if (useInstanceCredentials) {
                client = new AmazonCloudWatchAsyncClient(new InstanceProfileCredentialsProvider());
            } else {
                String awsKeyId = init.getProperty(AWS_ACCESS_KEY_ID_PROPERTY, "");
                if (awsKeyId.isEmpty()) {
                    init.throwRequiredException(AWS_ACCESS_KEY_ID_PROPERTY);
                }

                String awsKeySecret = init.getProperty(AWS_ACCESS_KEY_SECRET_PROPERTY, "");
                if (awsKeySecret.isEmpty()) {
                    init.throwRequiredException(AWS_ACCESS_KEY_SECRET_PROPERTY);
                }

                client = new AmazonCloudWatchAsyncClient(new BasicAWSCredentials(awsKeyId, awsKeySecret));
            }

            String cloudwatchNamespace = init.getProperty(METRIC_NAMESPACE_PROPERTY, null);

            frequencyMillis = InitUtil.millisFromTime(init.getProperty(FREQUENCY_PROPERTY, "1m"));

            boolean disableTranslate = init.getProperty(DISABLE_TRANSLATE_PROPERTY, "false")
                    .equalsIgnoreCase("true");

            if (disableTranslate) {
                reporter = new CloudWatchReporter(registry, cloudwatchNamespace, client);
            } else {
                MetricRegistry filteredRegistry = RegistryTranslation.translate(init.getProperties(), registry);
                reporter = new CloudWatchReporter(filteredRegistry, cloudwatchNamespace, client);
            }
        }
    }

    @Override
    public void start() throws Exception {
        if (!isInit.get()) {
            throw new InitializationException("The reporter must be initialized before start!");
        }
        if (isRunning.compareAndSet(false, true)) {
            reporter.start(frequencyMillis, TimeUnit.MILLISECONDS);
        }
    }

    @Override
    public void stop() {
        if (isRunning.compareAndSet(true, false)) {
            reporter.stop();
            client.shutdown();
        }
    }

    @Override
    public Map<String, Metric> getMetrics() {
        return ImmutableMap.of();
    }

    private CloudWatchReporter reporter;
    private long frequencyMillis;

    private AmazonCloudWatchAsyncClient client;
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
}