From 3d62f0f09fa359bcb254ebc78a8da5daed5d44cc Mon Sep 17 00:00:00 2001 From: Theodore Cowan Date: Thu, 23 Apr 2020 11:22:42 -0400 Subject: [PATCH] Add CloudWatchOutputWriter using the v2 AWS SDK and async cloudwatch client --- README.md | 10 +++ pom.xml | 37 +++++++-- .../agent/CloudWatchOutputWriter.java | 81 +++++++++++++++++++ 3 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/jmxtrans/agent/CloudWatchOutputWriter.java diff --git a/README.md b/README.md index 2e249d9b..c2db7ba2 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,9 @@ Out of the box output writers: * `retentionPolicy`: retention policy to use - optional * `connectTimeoutMillis`: connect timeout for the HTTP connection to influx - optional, defaults to 3000 * `readTimeoutMillis`: read timeout for the HTTP connection to influx - optional, defaults to 5000 +* [CloudWatchOutputWriter](https://github.com/jmxtrans/jmxtrans-agent/blob/master/src/main/java/org/jmxtrans/agent/CloudWatchOutputWriter.java): Output to AWS CloudWatch Metrics. Credentials and region are loaded using the default region and credential providers (https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/CloudWatchClient.html#create--). Configuration parameters: + * `namespace`: Cloudwatch namespace. Default is "JMX". e.g. `JMX/Production`. - optional + * `dimensions`: additional dimensions to use for all metrics. Use `n1:v1,n2:v2` format, e.g. `host:#hostname#` - optional Output writers configuration support an [expression language](https://github.com/jmxtrans/jmxtrans-agent/wiki/Expression-Language) based on property placeholders with the `{prop-name[:default-value]}` syntax (e.g. "`${graphite.port:2003}`"). @@ -365,6 +368,13 @@ tomcat.bytesReceived 0 application.activeSessions 0 ``` +# Building + +``` +mvn compile +mvn package +``` + # Release Notes diff --git a/pom.xml b/pom.xml index dd44d194..4874b8be 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,17 @@ https://github.com/jmxtrans/jmxtrans-agent HEAD + + + + software.amazon.awssdk + bom + 2.13.0 + pom + import + + + com.sun @@ -90,14 +101,16 @@ 2.5.1 test - + + software.amazon.awssdk + cloudwatch + org.apache.maven.plugins - maven-jar-plugin - 3.0.2 + maven-assembly-plugin @@ -111,14 +124,28 @@ + + + create-my-bundle + package + + single + + + + jar-with-dependencies + + + + org.apache.maven.plugins maven-compiler-plugin 3.6.1 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/src/main/java/org/jmxtrans/agent/CloudWatchOutputWriter.java b/src/main/java/org/jmxtrans/agent/CloudWatchOutputWriter.java new file mode 100644 index 00000000..1651cbfa --- /dev/null +++ b/src/main/java/org/jmxtrans/agent/CloudWatchOutputWriter.java @@ -0,0 +1,81 @@ +package org.jmxtrans.agent; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.jmxtrans.agent.util.ConfigurationUtils; +import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient; +import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException; +import software.amazon.awssdk.services.cloudwatch.model.Dimension; +import software.amazon.awssdk.services.cloudwatch.model.MetricDatum; +import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest; + +public class CloudWatchOutputWriter extends AbstractOutputWriter { + + private CloudWatchAsyncClient client; + private String configNamespace; + private String configDimensions; + private List listOfDimensions; + private Collection dimensions; + + @Override + public void postConstruct(Map settings) { + + client = CloudWatchAsyncClient.create(); + configNamespace = ConfigurationUtils.getString(settings, "namespace", "JMX"); + configDimensions = ConfigurationUtils.getString(settings, "dimensions", ""); + listOfDimensions = Tag.tagsFromCommaSeparatedString(configDimensions); + + dimensions = new ArrayList(); + + for (Tag thisDimension : listOfDimensions) { + dimensions.add( + Dimension.builder() + .name(thisDimension.getName()) + .value(thisDimension.getValue()) + .build()); + } + } + + @Override + public void writeQueryResult(String name, String type, Object value) { + + Double doubleValue; + + if (value instanceof Number) { + Number numberValue = (Number) value; + doubleValue = numberValue.doubleValue(); + } else { + logger.log(Level.WARNING, "Cannot write result " + name + ". " + value + " is not a Number."); + return; + } + + try { + + MetricDatum datum = + MetricDatum.builder().metricName(name).value(doubleValue).dimensions(dimensions).build(); + + PutMetricDataRequest request = + PutMetricDataRequest.builder().namespace(configNamespace).metricData(datum).build(); + + client.putMetricData(request); + + } catch (CloudWatchException e) { + logger.log(Level.SEVERE, e.awsErrorDetails().errorMessage()); + } + } + + @Override + public void writeInvocationResult(String invocationName, Object value) throws IOException { + writeQueryResult(invocationName, null, value); + } + + @Override + public void preDestroy() { + super.preDestroy(); + client.close(); + } +}