Hi Philippe,
a few comments inline.
Am 30.11.2014 um 21:29 schrieb [email protected]:
Author: pmouawad
Date: Sun Nov 30 20:29:49 2014
New Revision: 1642603
URL: http://svn.apache.org/r1642603
Log:
Bug 57246 - BackendListener : Create a Graphite implementation
Make reported percentiles configurable
Compute min and max within sliding window
Move threads (Users) computing to UserMetric
Bugzilla Id: 57246
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java
(with props)
Modified:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
jmeter/trunk/xdocs/usermanual/component_reference.xml
Modified:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java?rev=1642603&r1=1642602&r2=1642603&view=diff
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
(original)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
Sun Nov 30 20:29:49 2014
@@ -53,6 +53,7 @@ import org.apache.log.Logger;
public abstract class AbstractBackendListenerClient implements
BackendListenerClient {
private static final Logger LOGGER = LoggingManager.getLoggerForClass();
+ private UserMetric userMetrics = new UserMetric();
private ConcurrentHashMap<String, SamplerMetric> metricsPerSampler = new ConcurrentHashMap<String, SamplerMetric>();
@@ -117,4 +118,10 @@ public abstract class AbstractBackendLis
return metricsPerSampler;
}
+ /**
+ * @return UserMetric
+ */
+ protected UserMetric getUserMetrics() {
+ return userMetrics;
+ }
}
Modified:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java?rev=1642603&r1=1642602&r2=1642603&view=diff
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
(original)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
Sun Nov 30 20:29:49 2014
@@ -20,20 +20,19 @@ package org.apache.jmeter.visualizers.ba
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.util.JMeterUtils;
/**
* Sampler metric
* @since 2.13
*/
public class SamplerMetric {
- // Limit to sliding window of 100 values
- private DescriptiveStatistics stats = new DescriptiveStatistics(100);
- private int success;
- private int failure;
- private long maxTime=0L;
- private long minTime=Long.MAX_VALUE;
- private int maxActiveThreads = 0;
- private int minActiveThreads = Integer.MAX_VALUE;
+ private static final int SLIDING_WINDOW_SIZE =
JMeterUtils.getPropDefault("backend_metrics_window", 100); //$NON-NLS-1$
+
+ // Limit to sliding window of SLIDING_WINDOW_SIZE values
+ private DescriptiveStatistics responsesStats = new
DescriptiveStatistics(SLIDING_WINDOW_SIZE);
+ private int successes;
I would have named it successCount and failureCount and shouldn't they
also be windowed, if all other values are?
+ private int failures;
/**
*
*/
@@ -46,20 +45,15 @@ public class SamplerMetric {
*/
public synchronized void add(SampleResult result) {
if(result.isSuccessful()) {
- success++;
+ successes++;
} else {
- failure++;
+ failures++;
}
long time = result.getTime();
- int activeThreads = result.getAllThreads();
- maxTime = Math.max(time, maxTime);
- minTime = Math.min(time, minTime);
- maxActiveThreads = Math.max(maxActiveThreads, activeThreads);
- minActiveThreads = Math.min(minActiveThreads, activeThreads);
if(result.isSuccessful()) {
// Should we also compute KO , all response time ?
// only take successful requests for time computing
- stats.addValue(time);
+ responsesStats.addValue(time);
}
}
@@ -67,14 +61,10 @@ public class SamplerMetric {
* Reset metric except for percentile related data
*/
public synchronized void resetForTimeInterval() {
- // We don't clear stats as it will slide as per my understanding of
+ // We don't clear responsesStats nor usersStats as it will slide as
per my understanding of
// http://commons.apache.org/proper/commons-math/userguide/stat.html
- success = 0;
- failure = 0;
- maxTime=0L;
- minTime=Long.MAX_VALUE;
- maxActiveThreads = 0;
- minActiveThreads = Integer.MAX_VALUE;
+ successes = 0;
+ failures = 0;
}
/**
@@ -83,7 +73,7 @@ public class SamplerMetric {
* @return The arithmetic mean of the stored values
*/
public double getMean() {
- return stats.getMean();
+ return responsesStats.getMean();
}
/**
@@ -95,7 +85,7 @@ public class SamplerMetric {
* values.
*/
public double getPercentile(double percentile) {
- return stats.getPercentile(percentile);
+ return responsesStats.getPercentile(percentile);
}
/**
@@ -104,7 +94,7 @@ public class SamplerMetric {
* @return number of total requests
*/
public int getTotal() {
- return success+failure;
+ return successes+failures;
}
/**
@@ -112,8 +102,8 @@ public class SamplerMetric {
*
* @return number of successful requests
*/
- public int getSuccess() {
- return success;
+ public int getSuccesses() {
+ return successes;
}
/**
@@ -121,43 +111,27 @@ public class SamplerMetric {
*
* @return number of failed requests
*/
- public int getFailure() {
- return failure;
+ public int getFailures() {
+ return failures;
}
/**
- * Get the maximal elapsed time for all requests
+ * Get the maximal elapsed time for requests within sliding window
*
* @return the maximal elapsed time, or <code>0</code> if no requests have
* been added yet
*/
- public long getMaxTime() {
- return maxTime;
+ public double getMaxTime() {
+ return responsesStats.getMax();
}
/**
- * Get the minimal elapsed time for all requests
+ * Get the minimal elapsed time for requests within sliding window
*
* @return the minTime, or {@link Long#MAX_VALUE} if no requests have been
* added yet
*/
- public long getMinTime() {
- return minTime;
- }
-
- /**
- * @return the max number of active threads for this test run, or
- * <code>0</code> if no samples have been added yet
- */
- public int getMaxActiveThreads() {
- return maxActiveThreads;
- }
-
- /**
- * @return the min number of active threads for this test run, or
- * {@link Integer#MAX_VALUE} if no samples have been added yet
- */
- public int getMinActiveThreads() {
- return minActiveThreads;
+ public double getMinTime() {
+ return responsesStats.getMin();
}
}
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java?rev=1642603&view=auto
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java
Sun Nov 30 20:29:49 2014
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jmeter.visualizers.backend;
+
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.util.JMeterUtils;
+
+/**
+ * User metric
+ * @since 2.13
+ */
+public class UserMetric {
+ private static final int SLIDING_WINDOW_SIZE =
JMeterUtils.getPropDefault("backend_metrics_window", 100); //$NON-NLS-1$
+
+ // Limit to sliding window of SLIDING_WINDOW_SIZE values
+ private DescriptiveStatistics usersStats = new
DescriptiveStatistics(SLIDING_WINDOW_SIZE);
+ /**
+ *
+ */
+ public UserMetric() {
+ }
+
+ /**
+ * Add a {@link SampleResult} to be used in the statistics
+ * @param result {@link SampleResult} to be used
+ */
+ public synchronized void add(SampleResult result) {
+ usersStats.addValue(result.getAllThreads());
+ }
+
+ /**
+ * Reset metric except for percentile related data
+ */
+ public synchronized void resetForTimeInterval() {
+ // NOOP
+ }
+
+ /**
+ * @return the max number of active threads for this test run
+ * using a sliding window of SLIDING_WINDOW_SIZE
+ */
+ public int getMaxActiveThreads() {
+ return (int) usersStats.getMin();
+ }
+
+ /**
+ * @return the mean number of active threads for this test run
+ * using a sliding window of SLIDING_WINDOW_SIZE
+ */
+ public int getMeanActiveThreads() {
+ return (int) usersStats.getMean();
+ }
+
+ /**
+ * @return the min number of active threads for this test run
+ * using a sliding window of SLIDING_WINDOW_SIZE
+ */
+ public int getMinActiveThreads() {
+ return (int) usersStats.getMax();
+ }
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/UserMetric.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java?rev=1642603&r1=1642602&r2=1642603&view=diff
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
(original)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
Sun Nov 30 20:29:49 2014
@@ -18,6 +18,8 @@
package org.apache.jmeter.visualizers.backend.graphite;
+import java.text.DecimalFormat;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -26,6 +28,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.threads.JMeterContextService;
@@ -48,7 +51,9 @@ public class GraphiteBackendListenerClie
private static final Logger LOGGER = LoggingManager.getLoggerForClass();
private static final String DEFAULT_METRICS_PREFIX = "jmeter.";
//$NON-NLS-1$
private static final String CUMULATED_METRICS = "__cumulated__";
//$NON-NLS-1$
- private static final String METRIC_ACTIVE_THREADS = "activeThreads";
//$NON-NLS-1$
+ private static final String METRIC_MAX_ACTIVE_THREADS =
"maxActiveThreads"; //$NON-NLS-1$
+ private static final String METRIC_MIN_ACTIVE_THREADS =
"minActiveThreads"; //$NON-NLS-1$
+ private static final String METRIC_MEAN_ACTIVE_THREADS =
"meanActiveThreads"; //$NON-NLS-1$
private static final String METRIC_STARTED_THREADS = "startedThreads";
//$NON-NLS-1$
private static final String METRIC_STOPPED_THREADS = "stoppedThreads";
//$NON-NLS-1$
private static final String METRIC_FAILED_REQUESTS = "failure";
//$NON-NLS-1$
@@ -56,10 +61,10 @@ public class GraphiteBackendListenerClie
private static final String METRIC_TOTAL_REQUESTS = "total"; //$NON-NLS-1$
private static final String METRIC_MIN_RESPONSE_TIME = "min";
//$NON-NLS-1$
private static final String METRIC_MAX_RESPONSE_TIME = "max";
//$NON-NLS-1$
- private static final String METRIC_PERCENTILE90_RESPONSE_TIME =
"percentile90"; //$NON-NLS-1$
- private static final String METRIC_PERCENTILE95_RESPONSE_TIME =
"percentile95"; //$NON-NLS-1$
+ private static final String METRIC_PERCENTILE_PREFIX = "percentile";
//$NON-NLS-1$
private static final long ONE_SECOND = 1L;
private static final int MAX_POOL_SIZE = 1;
+ private static final String DEFAULT_PERCENTILES = "90;95;99";
In the documentation below you state, that this property will be split
upon comma. I think the semicolon here is a better choice.
private String graphiteHost;
private int graphitePort;
@@ -67,6 +72,7 @@ public class GraphiteBackendListenerClie
private String rootMetricsPrefix;
private String samplersList = ""; //$NON-NLS-1$
private Set<String> samplersToFilter;
+ private HashMap<String, Float> percentiles;
I would just declare Map<String, Float> instead of the specific HashMap.
private GraphiteMetricsSender pickleMetricsManager;
@@ -93,7 +99,9 @@ public class GraphiteBackendListenerClie
}
ThreadCounts tc = JMeterContextService.getThreadCounts();
- pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_ACTIVE_THREADS, Integer.toString(tc.activeThreads));
+ pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_MIN_ACTIVE_THREADS,
Integer.toString(getUserMetrics().getMaxActiveThreads()));
+ pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_MAX_ACTIVE_THREADS,
Integer.toString(getUserMetrics().getMinActiveThreads()));
+ pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_MEAN_ACTIVE_THREADS,
Integer.toString(getUserMetrics().getMeanActiveThreads()));
pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_STARTED_THREADS, Integer.toString(tc.startedThreads));
pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME,
METRIC_STOPPED_THREADS, Integer.toString(tc.finishedThreads));
@@ -107,14 +115,16 @@ public class GraphiteBackendListenerClie
* @param metric
*/
private void addMetrics(long timestamp, String contextName, SamplerMetric
metric) {
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_FAILED_REQUESTS, Integer.toString(metric.getFailure()));
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_SUCCESSFUL_REQUESTS, Integer.toString(metric.getSuccess()));
+ pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_FAILED_REQUESTS, Integer.toString(metric.getFailures()));
+ pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_SUCCESSFUL_REQUESTS, Integer.toString(metric.getSuccesses()));
pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_TOTAL_REQUESTS, Integer.toString(metric.getTotal()));
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_MIN_RESPONSE_TIME, Long.toString(metric.getMinTime()));
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_MAX_RESPONSE_TIME, Long.toString(metric.getMaxTime()));
- // TODO Make this customizable
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_PERCENTILE90_RESPONSE_TIME, Double.toString(metric.getPercentile(90)));
- pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_PERCENTILE95_RESPONSE_TIME, Double.toString(metric.getPercentile(95)));
+ pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_MIN_RESPONSE_TIME, Double.toString(metric.getMinTime()));
+ pickleMetricsManager.addMetric(timestamp, contextName,
METRIC_MAX_RESPONSE_TIME, Double.toString(metric.getMaxTime()));
+ for (Map.Entry<String, Float> entry : percentiles.entrySet()) {
+ pickleMetricsManager.addMetric(timestamp, contextName,
+ entry.getKey(),
+
Double.toString(metric.getPercentile(entry.getValue().floatValue())));
+ }
}
/**
@@ -135,6 +145,7 @@ public class GraphiteBackendListenerClie
public void handleSampleResults(List<SampleResult> sampleResults,
BackendListenerContext context) {
for (SampleResult sampleResult : sampleResults) {
+ getUserMetrics().add(sampleResult);
if(!summaryOnly &&
samplersToFilter.contains(sampleResult.getSampleLabel())) {
SamplerMetric samplerMetric =
getSamplerMetric(sampleResult.getSampleLabel());
samplerMetric.add(sampleResult);
@@ -153,6 +164,22 @@ public class GraphiteBackendListenerClie
summaryOnly = context.getBooleanParameter("summaryOnly", true);
samplersList = context.getParameter("samplersList", "");
rootMetricsPrefix = context.getParameter("rootMetricsPrefix",
DEFAULT_METRICS_PREFIX);
+ String percentilesAsString = context.getParameter("percentiles",
DEFAULT_METRICS_PREFIX);
+ String[] percentilesStringArray = percentilesAsString.split(";");
+ percentiles = new HashMap<String,
Float>(percentilesStringArray.length);
+ DecimalFormat format = new DecimalFormat("0.##");
+ for (int i = 0; i < percentilesStringArray.length; i++) {
Any reason for not using for(String percentile:
percentilesAsString.split(";")) { instead?
Below in the documentation you state, that the property will be split
upon a comma (",").
+ if(!StringUtils.isEmpty(percentilesStringArray[i].trim())) {
+ try {
+ Float percentileValue =
Float.parseFloat(percentilesStringArray[i].trim());
+ percentiles.put(
+
METRIC_PERCENTILE_PREFIX+AbstractGraphiteMetricsSender.sanitizeString(format.format(percentileValue)),
+ percentileValue);
+ } catch(Exception e) {
+ LOGGER.error("Error parsing
percentile:'"+percentilesStringArray[i]+"'");
You could include the exception in the log message.
Do we want to catch any exception here, or just those that get thrown by
parseFloat?
+ }
+ }
+ }
Class<?> clazz = Class.forName(graphiteMetricsSenderClass);
this.pickleMetricsManager = (GraphiteMetricsSender)
clazz.newInstance();
pickleMetricsManager.setup(graphiteHost, graphitePort,
rootMetricsPrefix);
@@ -189,6 +216,7 @@ public class GraphiteBackendListenerClie
arguments.addArgument("rootMetricsPrefix", DEFAULT_METRICS_PREFIX);
arguments.addArgument("summaryOnly", "true");
arguments.addArgument("samplersList", "");
+ arguments.addArgument("percentiles", DEFAULT_PERCENTILES);
return arguments;
}
}
Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL:
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1642603&r1=1642602&r2=1642603&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Sun Nov 30 20:29:49
2014
@@ -3402,6 +3402,19 @@ By default, a Graphite implementation is
<property name="Async Queue size" required="Yes">Size of the queue that holds the
SampleResults while they are processed asynchronously.</property>
<property name="Parameters" required="Yes">Parameters of the BackendListenerClient
implementation.</property>
</properties>
+
+
+ <p>The following parameters apply to the <b>GraphiteBackendListenerClient</b>
implementation:</p>
+
+ <properties>
+ <property name="graphiteMetricsSender"
required="Yes">org.apache.jmeter.visualizers.backend.graphite.TextGraphiteMetricsSender or
org.apache.jmeter.visualizers.backend.graphite.PickleGraphiteMetricsSender</property>
+ <property name="graphiteHost" required="Yes">Graphite or InfluxDB or
CollectD server port</property>
+ <property name="graphitePort" required="Yes">Graphite or InfluxDB or
CollectD server port, defaults to 2003. Note PickleGraphiteMetricsSender can only talk to Graphite
server.</property>
+ <property name="rootMetricsPrefix" required="Yes">Prefix of metrics sent to backend.
Defaults to ""jmeter."</property>
+ <property name="summaryOnly" required="Yes">Only send a summary with no
detail. Defaults to true.</property>
+ <property name="samplersList" required="Yes">Comma separated list of
samplers for which you want to report metrics to backend.</property>
You are using a semicolon to separate the property above.
Regards
Felix
+ <property name="percentiles" required="Yes">The percentiles you want to send
to backend. List must be semicolon separated.</property>
+ </properties>
</component>
<a href="#">^</a>