com.appdynamics.extensions.cloudwatch.AmazonCloudWatchMonitor.java Source code

Java tutorial

Introduction

Here is the source code for com.appdynamics.extensions.cloudwatch.AmazonCloudWatchMonitor.java

Source

/**
 * Copyright 2013 AppDynamics 
 *
 * 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 com.appdynamics.extensions.cloudwatch;

import static com.appdynamics.extensions.cloudwatch.metricsmanager.MetricsManagerFactory.AWS_EC2_NAMESPACE;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
import com.amazonaws.services.cloudwatch.model.Datapoint;
import com.appdynamics.TaskInputArgs;
import com.appdynamics.extensions.ArgumentsValidator;
import com.appdynamics.extensions.cloudwatch.configuration.Configuration;
import com.appdynamics.extensions.cloudwatch.configuration.ConfigurationUtil;
import com.appdynamics.extensions.cloudwatch.ec2.EC2InstanceNameManager;
import com.appdynamics.extensions.cloudwatch.metricsmanager.MetricType;
import com.appdynamics.extensions.cloudwatch.metricsmanager.MetricsManager;
import com.appdynamics.extensions.cloudwatch.metricsmanager.MetricsManagerFactory;
import com.appdynamics.extensions.cloudwatch.metricsmanager.metricsmanagerimpl.CustomNamespaceMetricsManager;
import com.singularity.ee.agent.systemagent.api.AManagedMonitor;
import com.singularity.ee.agent.systemagent.api.MetricWriter;
import com.singularity.ee.agent.systemagent.api.TaskExecutionContext;
import com.singularity.ee.agent.systemagent.api.TaskOutput;

import org.apache.log4j.Logger;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;

public class AmazonCloudWatchMonitor extends AManagedMonitor {

    private Logger logger = Logger.getLogger(AmazonCloudWatchMonitor.class);

    private boolean isInitialized = false;
    private ExecutorService awsWorkerPool;
    private ExecutorService awsMetricWorkerPool;

    private MetricsManagerFactory metricsManagerFactory;
    private Map<String, Set<String>> disabledMetrics;
    private Map<String, Map<String, MetricType>> metricTypes;
    private Set<String> availableNamespaces;
    private Set<String> availableRegions;
    private AWSCredentials credentials;
    private ClientConfiguration clientConfiguration;
    private Configuration configuration;
    private String metric_prefix;
    private EC2InstanceNameManager ec2InstanceNameManager;
    private boolean useEc2InstanceNameInMetrics;

    private static final Map<String, String> regionVsURLs;

    static {
        Map<String, String> tmpRegionVsURLs = new HashMap<String, String>();

        tmpRegionVsURLs.put("ap-southeast-1", "monitoring.ap-southeast-1.amazonaws.com");
        tmpRegionVsURLs.put("eu-west-1", "monitoring.eu-west-1.amazonaws.com");
        tmpRegionVsURLs.put("eu-central-1", "monitoring.eu-central-1.amazonaws.com");
        tmpRegionVsURLs.put("us-east-1", "monitoring.us-east-1.amazonaws.com");
        tmpRegionVsURLs.put("us-west-1", "monitoring.us-west-1.amazonaws.com");
        tmpRegionVsURLs.put("us-west-2", "monitoring.us-west-2.amazonaws.com");
        tmpRegionVsURLs.put("ap-southeast-2", "monitoring.ap-southeast-2.amazonaws.com");
        tmpRegionVsURLs.put("ap-northeast-1", "monitoring.ap-northeast-1.amazonaws.com");
        tmpRegionVsURLs.put("sa-east-1", "monitoring.sa-east-1.amazonaws.com");

        regionVsURLs = Collections.unmodifiableMap(tmpRegionVsURLs);
    }

    private static final Map<String, String> DEFAULT_ARGS = new HashMap<String, String>() {
        {
            put("configurations", "monitors/CloudWatchMonitor/conf/AWSConfigurations.xml");
            put(TaskInputArgs.METRIC_PREFIX, "Custom Metrics|Amazon Cloud Watch|");
        }
    };

    public AmazonCloudWatchMonitor() {
        String msg = "Using Monitor Version [" + getImplementationVersion() + "]";
        logger.info(msg);
        System.out.println(msg);
    }

    /**
     * Initialize AWS credentials, disabled metrics, and supported namespaces
     *
     * @param taskArguments
     * @return
     */
    public void initialize(Map<String, String> taskArguments) throws Exception {
        if (!isInitialized) {
            taskArguments = ArgumentsValidator.validateArguments(taskArguments, DEFAULT_ARGS);
            metric_prefix = taskArguments.get(TaskInputArgs.METRIC_PREFIX);
            configuration = ConfigurationUtil.getConfigurations(taskArguments.get("configurations"));
            credentials = configuration.awsCredentials;
            clientConfiguration = configuration.clientConfiguration;
            setProxyParams(configuration);
            disabledMetrics = configuration.disabledMetrics;
            availableNamespaces = configuration.availableNamespaces;
            availableRegions = configuration.availableRegions;
            metricTypes = configuration.metricTypes;
            isInitialized = true;
            awsWorkerPool = Executors.newFixedThreadPool(5);
            awsMetricWorkerPool = Executors.newFixedThreadPool(20);
            metricsManagerFactory = new MetricsManagerFactory(this);
            useEc2InstanceNameInMetrics = configuration.useNameInMetrics;
            initializeEC2InstanceNameManager();
            logger.info("AmazonMonitor initialized");
        }
    }

    private void setProxyParams(Configuration configuration) {
        if (!configuration.proxyParams.isEmpty()) {
            clientConfiguration.setProxyHost(configuration.proxyParams.get("proxyHost"));
            clientConfiguration.setProxyPort(Integer.parseInt(configuration.proxyParams.get("proxyPort")));
            clientConfiguration.setProxyUsername(configuration.proxyParams.get("proxyUserName"));
            clientConfiguration.setProxyPassword(configuration.proxyParams.get("proxyPassword"));
        }
    }

    private void initializeEC2InstanceNameManager() {
        if (useEc2InstanceNameInMetrics && availableNamespaces.contains(AWS_EC2_NAMESPACE)) {
            ec2InstanceNameManager = new EC2InstanceNameManager(credentials, configuration.tagFilterName,
                    configuration.tagKey);
            ec2InstanceNameManager.initialise(availableRegions);
        }
    }

    /**
     * Main execution method that uploads the metrics to the AppDynamics
     * Controller
     *
     * @see com.singularity.ee.agent.systemagent.api.ITask#execute(java.util.Map,
     *      com.singularity.ee.agent.systemagent.api.TaskExecutionContext)
     */
    public TaskOutput execute(Map<String, String> taskArguments, TaskExecutionContext taskExecutionContext) {
        try {
            logger.info("Executing CloudWatchMonitor...");
            initialize(taskArguments);
            ExecutorCompletionService ecs = new ExecutorCompletionService(awsWorkerPool);
            int count = 0;
            for (final String region : availableRegions) {
                for (final String namespace : availableNamespaces) {
                    ecs.submit(new Callable() {
                        public Object call() throws Exception {
                            fetchAndPrintMetrics(namespace, region);
                            return null;
                        }
                    });
                    ++count;
                }
            }

            //Not sure if we need to wait.
            for (int i = 0; i < count; i++) {
                ecs.take().get();
            }
            logger.info("Finished Executing CloudWatchMonitor...");
            return new TaskOutput("AWS Cloud Watch Metric Upload Complete Successfully");
        } catch (Exception e) {
            logger.error("AWS Cloud Watch Metric Upload Failed ", e);
            return new TaskOutput("AWS Cloud Watch Metric Upload Failed");
        }
        // Not necessary to shutdown thread pools as they are initialized only once
    }

    private void fetchAndPrintMetrics(String namespace, String region) {
        AmazonCloudWatch awsCloudWatch;
        if (credentials == null) {
            awsCloudWatch = new AmazonCloudWatchClient(clientConfiguration);
        } else {
            awsCloudWatch = new AmazonCloudWatchClient(credentials, clientConfiguration);
        }
        awsCloudWatch.setEndpoint(regionVsURLs.get(region));
        MetricsManager metricsManager = metricsManagerFactory.createMetricsManager(namespace);
        metricsManager.setWorkerPool(awsMetricWorkerPool);
        Map<String, Map<String, List<Datapoint>>> metrics = metricsManager.gatherMetrics(awsCloudWatch, region);
        // Logging number of instances for which metrics
        // were collected

        if (metricsManager instanceof CustomNamespaceMetricsManager) {
            logger.info(String.format("Custom Namespace Metrics Count - %5s:%-5s %5s:%-5s %5s:%-5d", "Region",
                    region, "Namespace", namespace, "#Metric Size", metrics.size()));
        } else {
            logger.info(String.format("Running Instances Count in AWS - %5s:%-5s %5s:%-5s %5s:%-5d", "Region",
                    region, "Namespace", namespace, "#Instances", metrics.size()));
        }

        metricsManager.printMetrics(region, metrics);

    }

    /**
     * Get the Amazon Cloud Watch Client
     *
     * @return AmazonCloudWatch
     */

    /**
     * Get the hashmap of disabled metrics
     *
     * @return HashMap
     */
    public Map<String, Set<String>> getDisabledMetrics() {
        return this.disabledMetrics;
    }

    public static Map<String, String> getRegionvsurls() {
        return regionVsURLs;
    }

    /**
     * Set the Amazon Cloud Watch Client
     */

    /**
     * Set the hashmap of disabled metrics
     */
    public void setDisabledMetrics(Map<String, Set<String>> disabledMetrics) {
        this.disabledMetrics = disabledMetrics;
    }

    public Set<String> getAvailableRegions() {
        return availableRegions;
    }

    public void setAvailableRegions(Set<String> availableRegions) {
        this.availableRegions = availableRegions;
    }

    /**
     * Get the AWS Credentials
     *
     * @return AWSCredentials
     */
    public AWSCredentials getAWSCredentials() {
        return credentials;
    }

    /**
     * Get the Metric Types for specific 
     * 
     * @return Metric Types
     */
    public Map<String, Map<String, MetricType>> getMetricTypes() {
        return metricTypes;
    }

    /**
     * Check for disabled metrics in particular namespaces
     *
     * @return boolean
     */
    public boolean isMetricDisabled(String namespace, String metricName) {
        boolean result = false;
        if (disabledMetrics.get(namespace) != null) {
            if ((disabledMetrics.get(namespace)).contains(metricName)) {
                result = true;
            }
        }
        return result;
    }

    public EC2InstanceNameManager getEc2InstanceNameManager() {
        return ec2InstanceNameManager;
    }

    public boolean isUseEc2InstanceNameInMetrics() {
        return useEc2InstanceNameInMetrics;
    }

    /**
     * Returns the metric to the AppDynamics Controller.
     *
     * @param namespacePrefix
     *            Name of the Prefix
     * @param metricName
     *            Name of the Metric
     * @param metricValue
     *            Value of the Metric
     * @param aggregation
     *            Average OR Observation OR Sum
     * @param timeRollup
     *            Average OR Current OR Sum
     * @param cluster
     *            Collective OR Individual
     */
    public void printMetric(String region, String namespacePrefix, String metricName, double metricValue,
            String aggregation, String timeRollup, String cluster) {
        try {
            String value = new BigDecimal(metricValue).setScale(0, RoundingMode.HALF_UP).toString();
            MetricWriter metricWriter = getMetricWriter(getMetricPrefix() + region + namespacePrefix + metricName,
                    aggregation, timeRollup, cluster);
            if (logger.isDebugEnabled()) {
                logger.debug("Metric: " + getMetricPrefix() + region + namespacePrefix + metricName + " value: "
                        + metricValue + " -> " + value);
            }
            metricWriter.printMetric(value);
        } catch (Exception e) {
            logger.error(e);
        }
    }

    /**
     * Metric Prefix
     *
     * @return String
     */
    private String getMetricPrefix() {
        return metric_prefix + "|";
    }

    public static String getImplementationVersion() {
        return AmazonCloudWatchMonitor.class.getPackage().getImplementationTitle();
    }
}