diff --git a/.gitignore b/.gitignore index 1940f2a..9006e87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .classpath .project .settings +.idea target -coverage.ec \ No newline at end of file +coverage.ec +*.iml diff --git a/README.md b/README.md index 87ecd37..9427e53 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # stackify-metrics +[![Maven Central](https://img.shields.io/maven-central/v/com.stackify/stackify-metrics.svg)](http://mvnrepository.com/artifact/com.stackify/stackify-metrics) [![Build Status](https://travis-ci.org/stackify/stackify-metrics.png)](https://travis-ci.org/stackify/stackify-metrics) [![Coverage Status](https://coveralls.io/repos/stackify/stackify-metrics/badge.png?branch=master)](https://coveralls.io/r/stackify/stackify-metrics?branch=master) @@ -7,12 +8,23 @@ API for sending custom metrics to Stackify. Custom Metrics Overview: -http://docs.stackify.com/m/7787/l/232533 +http://support.stackify.com/custom-metrics-overview/ Sign Up for a Trial: http://www.stackify.com/sign-up/ +## Installation + +Add it as a maven dependency: +```xml + + com.stackify + stackify-metrics + 2.1.1 + +``` + ## Usage There are four different types of metrics: @@ -44,6 +56,23 @@ stackify.environment=YOUR_ENVIRONMENT Note: *If you are logging from a device that has the stackify-agent installed, the environment setting is optional. We will use the environment associated to your device in Stackify.* +#### Programmatic Configuration (Optional) + +Instead of providing a properties file in your classpath, you can configure the Metrics API programmatically: +``` +ApiConfiguration.Builder builder = ApiConfiguration.newBuilder(); +builder.apiKey("YOUR_API_KEY"); +builder.application("YOUR_APPLICATION_NAME"); +builder.environment("YOUR_ENVIRONMENT"); +ApiConfiguration config = builder.build(); + +MetricManager.configure(config); +``` + +This needs to be done at application startup before any other interactions with the Metrics API. + +Note: *If you are logging from a device that has the stackify-agent installed, the environment setting is optional. We will use the environment associated to your device in Stackify.* + #### Gauge Metric ```java @@ -179,22 +208,9 @@ counterAndTimer.autoReportZeroValue(); ... ``` -## Installation - -Add it as a maven dependency: -```xml - - com.stackify - stackify-metrics - 1.0.4 - -``` - -Note: *We are dependent on the Guava project from Google. We require version 14.0.1 (or beyond) for the background thread that sends data back to Stackify.* - ## License -Copyright 2014 Stackify, LLC. +Copyright 2020 Stackify, LLC. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pom.xml b/pom.xml index bcb3c69..c754e9a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,17 +3,17 @@ com.stackify stackify-metrics - 1.0.4-SNAPSHOT - + 2.1.2-SNAPSHOT + Stackify Metrics Stackify Metrics https://github.com/stackify/stackify-metrics org.sonatype.oss oss-parent - 7 + 9 - + The Apache Software License, Version 2.0 @@ -31,62 +31,60 @@ Stackify, LLC http://www.stackify.com - + eric-martin Eric Martin + + darinhoward + Darin Howard + - + 1.6 - + - + - com.google.guava - guava - 14.0.1 + com.stackify + stackify-api-java + 3.1.0 - + org.slf4j slf4j-api 1.7.5 - + com.fasterxml.jackson.core jackson-core - 2.1.3 - - - - com.fasterxml.jackson.core - jackson-annotations - 2.1.2 + 2.9.10 - + - com.fasterxml.jackson.core - jackson-databind - 2.1.3 + com.fasterxml.jackson.core + jackson-annotations + 2.9.10 - + - com.stackify - stackify-api-java - 2.0.2-SNAPSHOT + com.fasterxml.jackson.core + jackson-databind + 2.9.10.5 - + - + org.slf4j slf4j-log4j12 @@ -100,53 +98,53 @@ 4.11 test - + org.mockito mockito-core 1.9.5 test - + org.powermock powermock-core 1.5.6 test - + org.powermock powermock-module-junit4 1.5.6 test - + org.powermock powermock-api-mockito 1.5.6 test - + nl.jqno.equalsverifier equalsverifier 1.4.1 test - + - + - + src/main/resources true - + org.apache.maven.wagon @@ -154,9 +152,9 @@ 2.2 - + - + org.apache.maven.plugins maven-compiler-plugin @@ -166,7 +164,7 @@ ${java.version} - + org.apache.maven.plugins maven-jar-plugin @@ -177,20 +175,25 @@ - + org.apache.maven.plugins maven-javadoc-plugin + 2.9.1 attach-javadocs jar + + false + -Xdoclint:none + - + org.apache.maven.plugins maven-source-plugin @@ -203,7 +206,7 @@ - + org.apache.felix maven-bundle-plugin @@ -218,7 +221,7 @@ - + org.codehaus.mojo cobertura-maven-plugin @@ -229,31 +232,31 @@ - + org.eluder.coveralls coveralls-maven-plugin 2.2.0 - + - + - + - + org.apache.maven.plugins maven-javadoc-plugin 2.9.1 - + - + - + release-sign-artifacts @@ -283,5 +286,5 @@ - - \ No newline at end of file + + diff --git a/src/main/java/com/stackify/metric/MetricManager.java b/src/main/java/com/stackify/metric/MetricManager.java index 31e3d4f..0b0c36d 100644 --- a/src/main/java/com/stackify/metric/MetricManager.java +++ b/src/main/java/com/stackify/metric/MetricManager.java @@ -15,17 +15,16 @@ */ package com.stackify.metric; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Throwables; import com.stackify.api.common.ApiConfiguration; import com.stackify.api.common.ApiConfigurations; import com.stackify.api.common.AppIdentityService; +import com.stackify.api.common.EnvironmentDetails; import com.stackify.metric.impl.MetricBackgroundService; import com.stackify.metric.impl.MetricCollector; import com.stackify.metric.impl.MetricMonitorService; @@ -72,25 +71,52 @@ public static MetricCollector getCollector() { return COLLECTOR; } + + /** + * Manually configure the metrics api + * @param config API configuration + */ + public static synchronized void configure(final ApiConfiguration config) { + + ApiConfiguration.Builder builder = ApiConfiguration.newBuilder(); + builder.apiUrl(config.getApiUrl()); + builder.apiKey(config.getApiKey()); + builder.application(config.getApplication()); + builder.environment(config.getEnvironment()); + + if (config.getEnvDetail() == null) + { + builder.envDetail(EnvironmentDetails.getEnvironmentDetail(config.getApplication(), config.getEnvironment())); + } + else + { + builder.envDetail(config.getEnvDetail()); + } + + CONFIG = builder.build(); + } + /** * Start up the background thread that is processing metrics */ private static synchronized void startup() { try { - CONFIG = ApiConfigurations.fromProperties(); - + if (CONFIG == null) { + CONFIG = ApiConfigurations.fromProperties(); + } + ObjectMapper objectMapper = new ObjectMapper(); - AppIdentityService appIdentityService = new AppIdentityService(CONFIG, objectMapper); + AppIdentityService appIdentityService = new AppIdentityService(CONFIG, objectMapper, true); MetricMonitorService monitorService = new MetricMonitorService(CONFIG, objectMapper, appIdentityService); MetricSender sender = new MetricSender(CONFIG, objectMapper, monitorService); BACKGROUND_SERVICE = new MetricBackgroundService(COLLECTOR, sender); - BACKGROUND_SERVICE.start().get(5, TimeUnit.SECONDS); + BACKGROUND_SERVICE.start(); } catch (Throwable t) { LOGGER.error("Exception starting Stackify Metrics API service", t); @@ -103,10 +129,12 @@ private static synchronized void startup() { public static synchronized void shutdown() { if (BACKGROUND_SERVICE != null) { try { - BACKGROUND_SERVICE.stop().get(5, TimeUnit.SECONDS); - } catch (Exception e) { - Throwables.propagate(e); + BACKGROUND_SERVICE.stop(); + } catch (Throwable t) { + LOGGER.error("Exception stopping Stackify Metrics API service", t); } + + INITIALIZED.compareAndSet(true, false); } } diff --git a/src/main/java/com/stackify/metric/impl/JsonMetric.java b/src/main/java/com/stackify/metric/impl/JsonMetric.java index 96d4f3f..7b93083 100644 --- a/src/main/java/com/stackify/metric/impl/JsonMetric.java +++ b/src/main/java/com/stackify/metric/impl/JsonMetric.java @@ -62,6 +62,12 @@ public class JsonMetric { @JsonProperty("MonitorTypeID") private final Integer monitorTypeId; + /** + * Client device id + */ + @JsonProperty("ClientDeviceID") + private final Integer clientDeviceId; + /** * @return the monitorId */ @@ -97,6 +103,13 @@ public Integer getMonitorTypeId() { return monitorTypeId; } + /** + * @return the clientDeviceId + */ + public Integer getClientDeviceId() { + return clientDeviceId; + } + /** * @param builder The Builder object that contains all of the values for initialization */ @@ -106,6 +119,7 @@ private JsonMetric(final Builder builder) { this.count = builder.count; this.occurredUtc = builder.occurredUtc; this.monitorTypeId = builder.monitorTypeId; + this.clientDeviceId = builder.clientDeviceId; } /** @@ -152,6 +166,12 @@ public static class Builder { @JsonProperty("MonitorTypeID") private Integer monitorTypeId; + /** + * The builder's clientDeviceId + */ + @JsonProperty("ClientDeviceID") + private Integer clientDeviceId; + /** * Sets the builder's monitorId * @param monitorId The monitorId to be set @@ -202,6 +222,16 @@ public Builder monitorTypeId(final Integer monitorTypeId) { return this; } + /** + * Sets the builder's clientDeviceId + * @param clientDeviceId The clientDeviceId to be set + * @return Reference to the current object + */ + public Builder clientDeviceId(final Integer clientDeviceId) { + this.clientDeviceId = clientDeviceId; + return this; + } + /** * @return A new object constructed from this builder */ diff --git a/src/main/java/com/stackify/metric/impl/Metric.java b/src/main/java/com/stackify/metric/impl/Metric.java index eb065db..1a92d0c 100644 --- a/src/main/java/com/stackify/metric/impl/Metric.java +++ b/src/main/java/com/stackify/metric/impl/Metric.java @@ -15,7 +15,6 @@ */ package com.stackify.metric.impl; -import com.google.common.base.Objects; /** * Metric @@ -147,16 +146,11 @@ public Metric build() { /** * @see java.lang.Object#toString() - * @return A string representation of the object */ @Override public String toString() { - return Objects.toStringHelper(this) - .omitNullValues() - .add("identity", identity) - .add("occurredMillis", occurredMillis) - .add("value", value) - .add("isIncrement", isIncrement) - .toString(); + return "Metric [identity=" + identity + ", occurredMillis=" + + occurredMillis + ", value=" + value + ", isIncrement=" + + isIncrement + "]"; } } diff --git a/src/main/java/com/stackify/metric/impl/MetricAggregate.java b/src/main/java/com/stackify/metric/impl/MetricAggregate.java index 92cdfea..dd25641 100644 --- a/src/main/java/com/stackify/metric/impl/MetricAggregate.java +++ b/src/main/java/com/stackify/metric/impl/MetricAggregate.java @@ -15,8 +15,7 @@ */ package com.stackify.metric.impl; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; +import com.stackify.api.common.util.Preconditions; /** * MetricAggregate @@ -111,16 +110,11 @@ public long getOccurredMillis() { /** * @see java.lang.Object#toString() - * @return A string representation of the object */ @Override public String toString() { - return Objects.toStringHelper(this) - .omitNullValues() - .add("identity", identity) - .add("occurredMillis", occurredMillis) - .add("value", value) - .add("count", count) - .toString(); + return "MetricAggregate [identity=" + identity + ", occurredMillis=" + + occurredMillis + ", value=" + value + ", count=" + count + + "]"; } } diff --git a/src/main/java/com/stackify/metric/impl/MetricAggregator.java b/src/main/java/com/stackify/metric/impl/MetricAggregator.java index c916005..fa72b66 100644 --- a/src/main/java/com/stackify/metric/impl/MetricAggregator.java +++ b/src/main/java/com/stackify/metric/impl/MetricAggregator.java @@ -15,6 +15,8 @@ */ package com.stackify.metric.impl; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -22,9 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import com.stackify.api.common.util.Preconditions; /** * MetricAggregator @@ -40,7 +40,7 @@ public class MetricAggregator { /** * Map from metric key to utc minute to metric aggregate) */ - private final Map> aggregates = Maps.newHashMap(); + private final Map> aggregates = new HashMap>(); /** * Current minute @@ -69,7 +69,7 @@ public MetricAggregator(final long currentMinute, final Map getAggregates() { - List flatAggregates = Lists.newArrayList(); + List flatAggregates = new ArrayList(); for (Map metricAggregates : aggregates.values()) { for (MetricAggregate aggregate : metricAggregates.values()) { @@ -180,7 +180,7 @@ private MetricAggregate getAggregate(final MetricIdentity identity) { // get the map from utc minute to aggregate for this metric if (!aggregates.containsKey(identity)) { - aggregates.put(identity, Maps.newHashMap()); + aggregates.put(identity, new HashMap()); } Map metricAggregates = aggregates.get(identity); diff --git a/src/main/java/com/stackify/metric/impl/MetricBackgroundService.java b/src/main/java/com/stackify/metric/impl/MetricBackgroundService.java index a893f3a..333dedf 100644 --- a/src/main/java/com/stackify/metric/impl/MetricBackgroundService.java +++ b/src/main/java/com/stackify/metric/impl/MetricBackgroundService.java @@ -18,14 +18,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.AbstractScheduledService; +import com.stackify.api.common.concurrent.BackgroundService; +import com.stackify.api.common.util.Preconditions; /** * MetricBackgroundService * @author Eric Martin */ -public class MetricBackgroundService extends AbstractScheduledService { +public class MetricBackgroundService extends BackgroundService { /** * Logger @@ -60,19 +60,10 @@ public MetricBackgroundService(final MetricCollector collector, final MetricSend } /** - * @see com.google.common.util.concurrent.AbstractScheduledService#serviceName() + * @see com.stackify.api.common.concurrent.BackgroundService#startUp() */ @Override - protected String serviceName() { - return "Stackify_MetricBackgroundService"; - } - - /** - * @see com.google.common.util.concurrent.AbstractScheduledService#scheduler() - */ - @Override - protected Scheduler scheduler() { - return scheduler; + protected void startUp() { } /** @@ -89,6 +80,14 @@ protected void runOneIteration() { } } + /** + * @see com.stackify.api.common.concurrent.BackgroundService#getNextScheduleDelayMilliseconds() + */ + @Override + protected long getNextScheduleDelayMilliseconds() { + return scheduler.getScheduleDelay(); + } + /** * @see com.google.common.util.concurrent.AbstractScheduledService#shutDown() */ @@ -99,7 +98,5 @@ protected void shutDown() throws Exception { } catch (Throwable t) { LOGGER.info("Exception flushing metrics collector during shut down", t); } - - super.shutDown(); } } diff --git a/src/main/java/com/stackify/metric/impl/MetricBackgroundServiceScheduler.java b/src/main/java/com/stackify/metric/impl/MetricBackgroundServiceScheduler.java index 10c6787..356d87e 100644 --- a/src/main/java/com/stackify/metric/impl/MetricBackgroundServiceScheduler.java +++ b/src/main/java/com/stackify/metric/impl/MetricBackgroundServiceScheduler.java @@ -16,9 +16,7 @@ package com.stackify.metric.impl; import java.net.HttpURLConnection; -import java.util.concurrent.TimeUnit; -import com.google.common.util.concurrent.AbstractScheduledService.CustomScheduler; import com.stackify.api.common.http.HttpException; /** @@ -26,7 +24,7 @@ * * @author Eric Martin */ -public class MetricBackgroundServiceScheduler extends CustomScheduler { +public class MetricBackgroundServiceScheduler { /** * Five seconds (milliseconds) @@ -99,12 +97,4 @@ public void update(final Throwable t) { public long getScheduleDelay() { return scheduleDelay; } - - /** - * @see com.google.common.util.concurrent.AbstractScheduledService.CustomScheduler#getNextSchedule() - */ - @Override - protected Schedule getNextSchedule() { - return new Schedule(scheduleDelay, TimeUnit.MILLISECONDS); - } } diff --git a/src/main/java/com/stackify/metric/impl/MetricCollector.java b/src/main/java/com/stackify/metric/impl/MetricCollector.java index 79a31af..eaf5fa3 100644 --- a/src/main/java/com/stackify/metric/impl/MetricCollector.java +++ b/src/main/java/com/stackify/metric/impl/MetricCollector.java @@ -16,6 +16,8 @@ package com.stackify.metric.impl; import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Queue; @@ -24,12 +26,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; -import com.google.common.collect.Queues; -import com.google.common.collect.Sets; +import com.stackify.api.common.collect.SynchronizedEvictingQueue; import com.stackify.api.common.http.HttpException; -import com.stackify.api.common.lang.EvictingQueue; +import com.stackify.api.common.util.Preconditions; /** * MetricCollector @@ -50,22 +49,22 @@ public class MetricCollector { /** * The queue of objects to be transmitted */ - private final Queue queue = Queues.synchronizedQueue(new EvictingQueue(1000)); + private final Queue queue = new SynchronizedEvictingQueue(100000); /** * Initial values for the next iteration */ - private final Map lastValues = Maps.newHashMap(); + private final Map lastValues = new HashMap(); /** * Metrics that should auto report zero if there isn't a current value */ - private final Set autoReportZeroMetrics = Sets.newHashSet(); + private final Set autoReportZeroMetrics = new HashSet(); /** * Metrics that should auto report the last value if there isn't a current value */ - private final Set autoReportLastMetrics = Sets.newHashSet(); + private final Set autoReportLastMetrics = new HashSet(); /** * The last time the collector was flushed @@ -116,6 +115,7 @@ public int flush(final MetricSender sender) throws IOException, HttpException { long currentMinute = (System.currentTimeMillis() / MS_IN_MIN) * MS_IN_MIN; LOGGER.debug("Flushing metrics < {}", currentMinute); + LOGGER.debug("Metrics queue size {}", queue.size()); MetricAggregator aggregator = new MetricAggregator(currentMinute, lastValues); diff --git a/src/main/java/com/stackify/metric/impl/MetricIdentity.java b/src/main/java/com/stackify/metric/impl/MetricIdentity.java index 08576e9..c29611c 100644 --- a/src/main/java/com/stackify/metric/impl/MetricIdentity.java +++ b/src/main/java/com/stackify/metric/impl/MetricIdentity.java @@ -15,8 +15,7 @@ */ package com.stackify.metric.impl; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; +import com.stackify.api.common.util.Preconditions; /** * MetricIdentity @@ -80,46 +79,51 @@ public MetricMonitorType getType() { /** * @see java.lang.Object#hashCode() - * @return A hash code of this object */ @Override public int hashCode() { - return Objects.hashCode(category, name, type); + final int prime = 31; + int result = 1; + result = prime * result + + ((category == null) ? 0 : category.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; } /** * @see java.lang.Object#equals(java.lang.Object) - * @param other The reference object with which to compare - * @return True if this object is the same as the other object, false otherwise */ @Override - public boolean equals(final Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof MetricIdentity)) { - return false; - } - - final MetricIdentity otherMetricIdentity = (MetricIdentity) other; - - return Objects.equal(category, otherMetricIdentity.category) - && Objects.equal(name, otherMetricIdentity.name) - && Objects.equal(type, otherMetricIdentity.type); + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof MetricIdentity)) + return false; + MetricIdentity other = (MetricIdentity) obj; + if (category == null) { + if (other.category != null) + return false; + } else if (!category.equals(other.category)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (type != other.type) + return false; + return true; } /** * @see java.lang.Object#toString() - * @return A string representation of the object */ @Override public String toString() { - return Objects.toStringHelper(this) - .omitNullValues() - .add("category", category) - .add("name", name) - .add("type", type) - .toString(); + return "MetricIdentity [category=" + category + ", name=" + name + + ", type=" + type + "]"; } } diff --git a/src/main/java/com/stackify/metric/impl/MetricMonitorService.java b/src/main/java/com/stackify/metric/impl/MetricMonitorService.java index bf44490..d863172 100644 --- a/src/main/java/com/stackify/metric/impl/MetricMonitorService.java +++ b/src/main/java/com/stackify/metric/impl/MetricMonitorService.java @@ -16,6 +16,7 @@ package com.stackify.metric.impl; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; @@ -24,14 +25,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; import com.stackify.api.AppIdentity; import com.stackify.api.common.ApiConfiguration; import com.stackify.api.common.AppIdentityService; import com.stackify.api.common.http.HttpClient; import com.stackify.api.common.http.HttpException; +import com.stackify.api.common.util.Preconditions; /** * MetricMonitorService @@ -52,12 +51,12 @@ public class MetricMonitorService { /** * Timestamp of the last queries */ - private final Map lastQueries = Maps.newHashMap(); + private final Map lastQueries = new HashMap(); /** * The cached monitor ids */ - private Map monitorIds = Maps.newHashMap(); + private Map monitorIds = new HashMap(); /** * API configuration @@ -89,6 +88,19 @@ public MetricMonitorService(final ApiConfiguration apiConfig, final ObjectMapper this.appIdentityService = appIdentityService; } + /** + * @return Client device id + */ + public Integer getDeviceId() { + AppIdentity appIdentity = appIdentityService.getAppIdentity(); + + if (appIdentity != null) { + return appIdentity.getDeviceId(); + } + + return null; + } + /** * Gets the monitor id for this metric * @param identity The metric identity @@ -96,11 +108,11 @@ public MetricMonitorService(final ApiConfiguration apiConfig, final ObjectMapper * @throws IOException * @throws HttpException */ - public Optional getMonitorId(final MetricIdentity identity) throws IOException, HttpException { + public Integer getMonitorId(final MetricIdentity identity) throws IOException, HttpException { Preconditions.checkNotNull(identity); if (monitorIds.containsKey(identity)) { - return Optional.of(monitorIds.get(identity)); + return monitorIds.get(identity); } long lastQuery = 0; @@ -115,11 +127,11 @@ public Optional getMonitorId(final MetricIdentity identity) throws IOEx try { lastQueries.put(identity, lastQuery); - Optional appIdentity = appIdentityService.getAppIdentity(); + AppIdentity appIdentity = appIdentityService.getAppIdentity(); - if (appIdentity.isPresent()) { + if (appIdentity != null) { - int monitorId = getMetricInfo(identity, appIdentity.get()); + int monitorId = getMetricInfo(identity, appIdentity); LOGGER.debug("Metric {} monitor id: {}", identity, monitorId); @@ -133,10 +145,10 @@ public Optional getMonitorId(final MetricIdentity identity) throws IOEx } if (monitorIds.containsKey(identity)) { - return Optional.of(monitorIds.get(identity)); + return monitorIds.get(identity); } - return Optional.absent(); + return null; } /** @@ -166,11 +178,18 @@ private int getMetricInfo(final MetricIdentity identity, final AppIdentity appId byte[] jsonBytes = objectMapper.writer().writeValueAsBytes(request); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("GetMetricInfo Request: {}", new String(jsonBytes, "UTF-8")); + } + // post to stackify HttpClient httpClient = new HttpClient(apiConfig); String responseString = httpClient.post("/Metrics/GetMetricInfo", jsonBytes); + LOGGER.debug("GetMetricInfo Response: {}", responseString); + // deserialize the response and return the monitor id ObjectReader jsonReader = objectMapper.reader(new TypeReference(){}); diff --git a/src/main/java/com/stackify/metric/impl/MetricSender.java b/src/main/java/com/stackify/metric/impl/MetricSender.java index d94b275..2f92500 100644 --- a/src/main/java/com/stackify/metric/impl/MetricSender.java +++ b/src/main/java/com/stackify/metric/impl/MetricSender.java @@ -16,6 +16,7 @@ package com.stackify.metric.impl; import java.io.IOException; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -23,12 +24,11 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; import com.stackify.api.common.ApiConfiguration; import com.stackify.api.common.http.HttpClient; import com.stackify.api.common.http.HttpException; +import com.stackify.api.common.http.HttpResendQueue; +import com.stackify.api.common.util.Preconditions; /** * MetricSender @@ -56,6 +56,11 @@ public class MetricSender { */ private final MetricMonitorService monitorService; + /** + * The queue of requests to be retransmitted (60 minutes of metric aggregates) + */ + private final HttpResendQueue resendQueue = new HttpResendQueue(60); + /** * Constructor * @param apiConfig API configuration @@ -79,21 +84,28 @@ public MetricSender(final ApiConfiguration apiConfig, final ObjectMapper objectM */ public void send(final List aggregates) throws IOException, HttpException { + HttpClient httpClient = new HttpClient(apiConfig); + + // retransmit any metrics on the resend queue + + resendQueue.drain(httpClient, "/Metrics/SubmitMetricsByID"); + // build the json objects - List metrics = Lists.newArrayListWithCapacity(aggregates.size()); + List metrics = new ArrayList(aggregates.size()); for (MetricAggregate aggregate : aggregates) { - Optional monitorId = monitorService.getMonitorId(aggregate.getIdentity()); + Integer monitorId = monitorService.getMonitorId(aggregate.getIdentity()); - if (monitorId.isPresent()) { + if (monitorId != null) { JsonMetric.Builder builder = JsonMetric.newBuilder(); - builder.monitorId(monitorId.get()); + builder.monitorId(monitorId); builder.value(Double.valueOf(aggregate.getValue())); builder.count(Integer.valueOf(aggregate.getCount())); builder.occurredUtc(new Date(aggregate.getOccurredMillis())); builder.monitorTypeId(Integer.valueOf(aggregate.getIdentity().getType().getId())); + builder.clientDeviceId(monitorService.getDeviceId()); metrics.add(builder.build()); } else { @@ -105,13 +117,20 @@ public void send(final List aggregates) throws IOException, Htt return; } - // convert to json bytes - - byte[] jsonBytes = objectMapper.writer().writeValueAsBytes(metrics); + // post metrics to stackify - // post to stackify + byte[] jsonBytes = objectMapper.writer().writeValueAsBytes(metrics); - HttpClient httpClient = new HttpClient(apiConfig); - httpClient.post("/Metrics/SubmitMetricsByID", jsonBytes); + try { + httpClient.post("/Metrics/SubmitMetricsByID", jsonBytes); + } catch (IOException t) { + LOGGER.info("Queueing metrics for retransmission due to IOException"); + resendQueue.offer(jsonBytes, t); + throw t; + } catch (HttpException t) { + LOGGER.info("Queueing metrics for retransmission due to HttpException"); + resendQueue.offer(jsonBytes, t); + throw t; + } } } diff --git a/src/test/java/com/stackify/metric/MetricManagerTest.java b/src/test/java/com/stackify/metric/MetricManagerTest.java index 7cbd74c..7c1cfed 100644 --- a/src/test/java/com/stackify/metric/MetricManagerTest.java +++ b/src/test/java/com/stackify/metric/MetricManagerTest.java @@ -15,6 +15,7 @@ */ package com.stackify.metric; +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,7 +24,8 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import com.google.common.util.concurrent.ListenableFuture; +import com.stackify.api.common.ApiConfiguration; +import com.stackify.api.common.ApiConfigurations; import com.stackify.api.common.AppIdentityService; import com.stackify.metric.impl.MetricBackgroundService; import com.stackify.metric.impl.MetricCollector; @@ -35,9 +37,17 @@ * @author Eric Martin */ @RunWith(PowerMockRunner.class) -@PrepareForTest({MetricManager.class, AppIdentityService.class, MetricMonitorService.class, MetricSender.class, MetricBackgroundService.class}) +@PrepareForTest({MetricManager.class, AppIdentityService.class, MetricMonitorService.class, MetricSender.class, MetricBackgroundService.class, ApiConfigurations.class}) public class MetricManagerTest { + /** + * tearDown + */ + @After + public void tearDown() { + MetricManager.shutdown(); + } + /** * testGetCollectorAndShutdown * @throws Exception @@ -54,8 +64,6 @@ public void testGetCollectorAndShutdown() throws Exception { PowerMockito.whenNew(MetricSender.class).withAnyArguments().thenReturn(sender); MetricBackgroundService background = PowerMockito.mock(MetricBackgroundService.class); - Mockito.when(background.start()).thenReturn(Mockito.mock(ListenableFuture.class)); - Mockito.when(background.stop()).thenReturn(Mockito.mock(ListenableFuture.class)); PowerMockito.whenNew(MetricBackgroundService.class).withAnyArguments().thenReturn(background); MetricManager.shutdown(); @@ -76,4 +84,40 @@ public void testGetCollectorAndShutdown() throws Exception { Mockito.verify(background).stop(); } + + /** + * testManualConfig + * @throws Exception + */ + @Test + public void testManualConfig() throws Exception { + + PowerMockito.mockStatic(ApiConfigurations.class); + PowerMockito.when(ApiConfigurations.fromProperties()).thenThrow(new RuntimeException()); + + ApiConfiguration config = Mockito.mock(ApiConfiguration.class); + + MetricManager.configure(config); + + AppIdentityService ais = Mockito.mock(AppIdentityService.class); + PowerMockito.whenNew(AppIdentityService.class).withAnyArguments().thenReturn(ais); + + MetricMonitorService mms = Mockito.mock(MetricMonitorService.class); + PowerMockito.whenNew(MetricMonitorService.class).withAnyArguments().thenReturn(mms); + + MetricSender sender = Mockito.mock(MetricSender.class); + PowerMockito.whenNew(MetricSender.class).withAnyArguments().thenReturn(sender); + + MetricBackgroundService background = PowerMockito.mock(MetricBackgroundService.class); + PowerMockito.whenNew(MetricBackgroundService.class).withAnyArguments().thenReturn(background); + + MetricCollector collector = MetricManager.getCollector(); + Assert.assertNotNull(collector); + + Mockito.verify(background).start(); + + MetricManager.shutdown(); + + Mockito.verify(background).stop(); + } } diff --git a/src/test/java/com/stackify/metric/impl/MetricAggregatorTest.java b/src/test/java/com/stackify/metric/impl/MetricAggregatorTest.java index 79151c9..10c1089 100644 --- a/src/test/java/com/stackify/metric/impl/MetricAggregatorTest.java +++ b/src/test/java/com/stackify/metric/impl/MetricAggregatorTest.java @@ -16,15 +16,14 @@ package com.stackify.metric.impl; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.Assert; import org.junit.Test; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - /** * MetricAggregatorTest * @author Eric Martin @@ -105,12 +104,14 @@ public void testAddGauge() { public void testAutoReportZero() { MetricIdentity identity = new MetricIdentity("category", "name", MetricMonitorType.COUNTER); - Map lastValues = Maps.newHashMap(); + Map lastValues = new HashMap(); lastValues.put(identity, Double.valueOf(14.0)); MetricAggregator aggregator = new MetricAggregator(System.currentTimeMillis(), lastValues); - aggregator.autoReportZero(Sets.newHashSet(identity)); + Set arz = new HashSet(); + arz.add(identity); + aggregator.autoReportZero(arz); List aggregates = aggregator.getAggregates(); @@ -129,12 +130,14 @@ public void testAutoReportZero() { public void testAutoReportLast() { MetricIdentity identity = new MetricIdentity("category", "name", MetricMonitorType.COUNTER); - Map lastValues = Maps.newHashMap(); + Map lastValues = new HashMap(); lastValues.put(identity, Double.valueOf(14.0)); MetricAggregator aggregator = new MetricAggregator(System.currentTimeMillis(), lastValues); - aggregator.autoReportLast(Sets.newHashSet(identity)); + Set arl = new HashSet(); + arl.add(identity); + aggregator.autoReportLast(arl); List aggregates = aggregator.getAggregates(); @@ -153,11 +156,13 @@ public void testAutoReportLast() { public void testAutoReportLastWithoutLast() { MetricIdentity identity = new MetricIdentity("category", "name", MetricMonitorType.COUNTER); - Map lastValues = Maps.newHashMap(); + Map lastValues = new HashMap(); MetricAggregator aggregator = new MetricAggregator(System.currentTimeMillis(), lastValues); - aggregator.autoReportLast(Sets.newHashSet(identity)); + Set arl = new HashSet(); + arl.add(identity); + aggregator.autoReportLast(arl); List aggregates = aggregator.getAggregates(); diff --git a/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceSchedulerTest.java b/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceSchedulerTest.java index c7d6df8..3cbfc9f 100644 --- a/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceSchedulerTest.java +++ b/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceSchedulerTest.java @@ -27,8 +27,6 @@ * * @author Eric Martin */ -//@RunWith(PowerMockRunner.class) -//@PrepareForTest({MetricBackgroundServiceScheduler.class, System.class}) public class MetricBackgroundServiceSchedulerTest { /** @@ -39,7 +37,6 @@ public void testNoUpdate() { MetricBackgroundServiceScheduler scheduler = new MetricBackgroundServiceScheduler(); Assert.assertEquals(5000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); } /** @@ -52,7 +49,6 @@ public void testUpdateOk() { scheduler.update(50); Assert.assertEquals(5000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); } /** @@ -65,7 +61,6 @@ public void testUpdateUnauthorized() { scheduler.update(new HttpException(HttpURLConnection.HTTP_UNAUTHORIZED)); Assert.assertEquals(300000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); } /** @@ -78,7 +73,6 @@ public void testUpdateError() { scheduler.update(new HttpException(HttpURLConnection.HTTP_INTERNAL_ERROR)); Assert.assertEquals(15000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); } /** @@ -91,11 +85,9 @@ public void testUpdateErrorAndClear() { scheduler.update(new HttpException(HttpURLConnection.HTTP_INTERNAL_ERROR)); Assert.assertEquals(15000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); scheduler.update(50); Assert.assertEquals(5000, scheduler.getScheduleDelay()); - Assert.assertNotNull(scheduler.getNextSchedule()); } } diff --git a/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceTest.java b/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceTest.java index 081960e..cc841ce 100644 --- a/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceTest.java +++ b/src/test/java/com/stackify/metric/impl/MetricBackgroundServiceTest.java @@ -38,8 +38,7 @@ public void testConstructor() { MetricSender sender = Mockito.mock(MetricSender.class); MetricBackgroundService service = new MetricBackgroundService(collector, sender); - Assert.assertEquals("Stackify_MetricBackgroundService", service.serviceName()); - Assert.assertNotNull(service.scheduler()); + Assert.assertFalse(service.isRunning()); } /** diff --git a/src/test/java/com/stackify/metric/impl/MetricMonitorServiceTest.java b/src/test/java/com/stackify/metric/impl/MetricMonitorServiceTest.java index 2c86f75..d1cfcc6 100644 --- a/src/test/java/com/stackify/metric/impl/MetricMonitorServiceTest.java +++ b/src/test/java/com/stackify/metric/impl/MetricMonitorServiceTest.java @@ -24,7 +24,6 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; import com.stackify.api.AppIdentity; import com.stackify.api.common.ApiConfiguration; import com.stackify.api.common.AppIdentityService; @@ -47,7 +46,7 @@ public void testNoAppIdentity() throws Exception { ApiConfiguration apiConfig = Mockito.mock(ApiConfiguration.class); ObjectMapper objectMapper = new ObjectMapper(); AppIdentityService appIdentityService = Mockito.mock(AppIdentityService.class); - Mockito.when(appIdentityService.getAppIdentity()).thenReturn(Optional.absent()); + Mockito.when(appIdentityService.getAppIdentity()).thenReturn(null); HttpClient httpClient = PowerMockito.mock(HttpClient.class); PowerMockito.whenNew(HttpClient.class).withAnyArguments().thenReturn(httpClient); @@ -57,10 +56,9 @@ public void testNoAppIdentity() throws Exception { MetricIdentity identity = new MetricIdentity("category", "name", MetricMonitorType.COUNTER); - Optional id = service.getMonitorId(identity); + Integer id = service.getMonitorId(identity); - Assert.assertNotNull(id); - Assert.assertFalse(id.isPresent()); + Assert.assertNull(id); Mockito.verifyZeroInteractions(httpClient); } @@ -74,7 +72,7 @@ public void testGetMonitorId() throws Exception { ApiConfiguration apiConfig = Mockito.mock(ApiConfiguration.class); ObjectMapper objectMapper = new ObjectMapper(); AppIdentityService appIdentityService = Mockito.mock(AppIdentityService.class); - Mockito.when(appIdentityService.getAppIdentity()).thenReturn(Optional.of(Mockito.mock(AppIdentity.class))); + Mockito.when(appIdentityService.getAppIdentity()).thenReturn(Mockito.mock(AppIdentity.class)); HttpClient httpClient = PowerMockito.mock(HttpClient.class); PowerMockito.whenNew(HttpClient.class).withAnyArguments().thenReturn(httpClient); @@ -84,21 +82,18 @@ public void testGetMonitorId() throws Exception { MetricIdentity identity = new MetricIdentity("category", "name", MetricMonitorType.COUNTER); - Optional id = service.getMonitorId(identity); + Integer id = service.getMonitorId(identity); Assert.assertNotNull(id); - Assert.assertTrue(id.isPresent()); - Assert.assertEquals(14, id.get().intValue()); + Assert.assertEquals(14, id.intValue()); - Optional cachedId = service.getMonitorId(identity); + Integer cachedId = service.getMonitorId(identity); Assert.assertNotNull(cachedId); - Assert.assertTrue(cachedId.isPresent()); - Assert.assertEquals(14, cachedId.get().intValue()); + Assert.assertEquals(14, cachedId.intValue()); - Optional absentId = service.getMonitorId(new MetricIdentity("does-not", "exist", MetricMonitorType.COUNTER)); + Integer absentId = service.getMonitorId(new MetricIdentity("does-not", "exist", MetricMonitorType.COUNTER)); - Assert.assertNotNull(absentId); - Assert.assertFalse(absentId.isPresent()); + Assert.assertNull(absentId); } } diff --git a/src/test/java/com/stackify/metric/impl/MetricSenderTest.java b/src/test/java/com/stackify/metric/impl/MetricSenderTest.java index cffbdad..faa6fad 100644 --- a/src/test/java/com/stackify/metric/impl/MetricSenderTest.java +++ b/src/test/java/com/stackify/metric/impl/MetricSenderTest.java @@ -25,7 +25,6 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; import com.stackify.api.common.ApiConfiguration; import com.stackify.api.common.http.HttpClient; @@ -48,7 +47,7 @@ public void testSend() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); MetricMonitorService monitorService = Mockito.mock(MetricMonitorService.class); - Mockito.when(monitorService.getMonitorId(Mockito.any(MetricIdentity.class))).thenReturn(Optional.of(14)); + Mockito.when(monitorService.getMonitorId(Mockito.any(MetricIdentity.class))).thenReturn(14); HttpClient httpClient = PowerMockito.mock(HttpClient.class); PowerMockito.whenNew(HttpClient.class).withAnyArguments().thenReturn(httpClient); @@ -75,7 +74,7 @@ public void testSendWithoutMonitorId() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); MetricMonitorService monitorService = Mockito.mock(MetricMonitorService.class); - Mockito.when(monitorService.getMonitorId(Mockito.any(MetricIdentity.class))).thenReturn(Optional.absent()); + Mockito.when(monitorService.getMonitorId(Mockito.any(MetricIdentity.class))).thenReturn(null); HttpClient httpClient = PowerMockito.mock(HttpClient.class); PowerMockito.whenNew(HttpClient.class).withAnyArguments().thenReturn(httpClient);