Author: pmouawad
Date: Sun Jan 15 21:19:52 2017
New Revision: 1778947
URL: http://svn.apache.org/viewvc?rev=1778947&view=rev
Log:
Bug 60590 - BackendListener : Add Influxdb BackendListenerClient implementation
to JMeter
Partly Based on PR 246 from by Logan Mauzaize (logan.mauzaize at gmail.com) and
Maxime Chassagneux (maxime.chassagneux at gmail.com).
Fixed following issues in PR:
- Reinit httpRequest
- Fix issue with broken NaN comparison which led to missing
- Improve InfluxDB Annotations
- Use StringBuilder
- Init StringBuilder capacity
- Add documentation
This closes #246
Bugzilla Id: 60590
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java
(with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java
(with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
(with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java
(with props)
Modified:
jmeter/trunk/xdocs/changes.xml
jmeter/trunk/xdocs/usermanual/component_reference.xml
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java?rev=1778947&view=auto
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java
Sun Jan 15 21:19:52 2017
@@ -0,0 +1,35 @@
+/*
+ * 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.influxdb;
+
+/**
+ * Base class for {@link InfluxdbMetricsSender}
+ * @since 3.2
+ */
+abstract class AbstractInfluxdbMetricsSender implements InfluxdbMetricsSender {
+
+ /**
+ * For tag keys, tag values, and field keys always use a backslash
character
+ * \ to escape List of special characters : commas , equal sign = spaces
+ */
+ static final String toStringValue(String s) {
+ return s.trim().replaceAll(" ", "\\\\ ").replaceAll(",",
"\\\\,").replaceAll("=", "\\\\=");
+ }
+
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/AbstractInfluxdbMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java?rev=1778947&view=auto
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java
Sun Jan 15 21:19:52 2017
@@ -0,0 +1,175 @@
+/*
+ * 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.influxdb;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * Influxdb sender base on The Line Protocol. The Line Protocol is a text based
+ * format for writing points to InfluxDB. Syntax : <measurement>[,<tag_key>=
+ * <tag_value>[,<tag_key>=<tag_value>]] <field_key>=<field_value>[,<field_key>=
+ * <field_value>] [<timestamp>] Each line, separated by the newline character,
+ * represents a single point in InfluxDB. Line Protocol is whitespace
sensitive.
+ *
+ * @since 3.2
+ */
+class HttpMetricsSender extends AbstractInfluxdbMetricsSender {
+ private static final Logger LOG = LoggingManager.getLoggerForClass();
+
+ private List<MetricTuple> metrics = new ArrayList<>();
+
+ private HttpPost httpRequest;
+
+ private CloseableHttpClient httpClient;
+
+ private URL url;
+
+ HttpMetricsSender() {
+ super();
+ }
+
+ /**
+ * The HTTP API is the primary means of writing data into InfluxDB, by
+ * sending POST requests to the /write endpoint. Initiate the HttpClient
+ * client with a HttpPost request from influxdb url
+ *
+ * @param influxdbUrl
+ * example : http://localhost:8086/write?db=myd&rp=one_week
+ * @see
org.apache.jmeter.visualizers.backend.influxdb.InfluxdbMetricsSender#setup(java.lang.String)
+ */
+ @Override
+ public void setup(String influxdbUrl) throws Exception {
+ httpClient = HttpClients.createDefault();
+ url = new URL(influxdbUrl);
+ httpRequest = createRequest(url);
+ }
+
+ /**
+ * @param influxdbUrl
+ * @return
+ * @throws URISyntaxException
+ */
+ private HttpPost createRequest(URL url) throws URISyntaxException {
+ RequestConfig defaultRequestConfig = RequestConfig.custom()
+ .setConnectTimeout(1000)
+ .setSocketTimeout(3000)
+ .setConnectionRequestTimeout(100)
+ .build();
+
+ HttpPost httpRequest = new HttpPost(url.toURI());
+ httpRequest.setConfig(defaultRequestConfig);
+ httpRequest.setHeader("User-Agent", "JMeter/1.0");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Created InfluxDBMetricsSender with url:" + url);
+ }
+ return httpRequest;
+ }
+
+ @Override
+ public void addMetric(String mesurement, String tag, String field) {
+ metrics.add(new MetricTuple(mesurement, tag, field));
+ }
+
+ /**
+ * @see
org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#
+ * writeAndSendMetrics()
+ */
+ @Override
+ public void writeAndSendMetrics() {
+ if (!metrics.isEmpty()) {
+ try {
+ if(httpRequest == null) {
+ httpRequest = createRequest(url);
+ }
+ StringBuilder sb = new StringBuilder(metrics.size()*20);
+ for (MetricTuple metric : metrics) {
+ // We let the Influxdb server fill the timestamp so we
don't
+ // add epoch time on each point
+ sb.append(metric.measurement + metric.tag + " " +
metric.field + "\n");
+ }
+
+ StringEntity entity = new StringEntity(sb.toString(),
StandardCharsets.UTF_8);
+
+ httpRequest.setEntity(entity);
+ HttpResponse response = httpClient.execute(httpRequest);
+ if (LOG.isDebugEnabled()) {
+ int code = response.getStatusLine().getStatusCode();
+ /*
+ * HTTP response summary 2xx: If your write request
received
+ * HTTP 204 No Content, it was a success! 4xx: InfluxDB
+ * could not understand the request. 5xx: The system is
+ * overloaded or significantly impaired.
+ */
+ switch (code) {
+ case 204:
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Success, number of metrics written : "
+ metrics.size());
+ }
+ break;
+ default:
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Error writing metrics to influxDB Url:
"+ url+", responseCode: " + code);
+ }
+ }
+
+ }
+ EntityUtils.consumeQuietly(response.getEntity());
+
+ } catch (Exception e) {
+ // A Failure occured we abort request
+ if(httpRequest != null) {
+ httpRequest.abort();
+ httpRequest = null;
+ }
+ LOG.error("Error writing to InfluxDB : " + e.getMessage());
+ }
+ }
+
+ // We drop metrics in all cases
+ metrics.clear();
+ }
+
+ /**
+ * @see
org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#
+ * destroy()
+ */
+ @Override
+ public void destroy() {
+ if(httpRequest != null) {
+ httpRequest.abort();
+ }
+ IOUtils.closeQuietly(httpClient);
+ }
+
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/HttpMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java?rev=1778947&view=auto
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
Sun Jan 15 21:19:52 2017
@@ -0,0 +1,358 @@
+/*
+ * 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.influxdb;
+
+import java.text.DecimalFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
+import org.apache.jmeter.visualizers.backend.BackendListenerContext;
+import org.apache.jmeter.visualizers.backend.SamplerMetric;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * Implementation of {@link AbstractBackendListenerClient} to write in an
InfluxDB using
+ * custom schema
+ * @since 3.2
+ */
+public class InfluxdbBackendListenerClient extends
AbstractBackendListenerClient implements Runnable {
+
+ private static final Logger LOGGER = LoggingManager.getLoggerForClass();
+ private ConcurrentHashMap<String, SamplerMetric> metricsPerSampler = new
ConcurrentHashMap<>();
+ // Name of the measurement
+ private static final String EVENTS_FOR_ANNOTATION = "events";
+
+ // Name of the measurement
+ private static final String DEFAULT_MEASUREMENT = "jmeter";
+ // Name of the application tested
+ private static String application = "";
+
+ private static final String TAG_TRANSACTION = ",transaction=";
+
+ private static final String TAG_STATUT = ",statut=";
+ private static final String TAG_APPLICATION = ",application=";
+
+ private static final String METRIC_COUNT = "count=";
+ private static final String METRIC_COUNT_ERREUR = "countError=";
+ private static final String METRIC_MIN = "min=";
+ private static final String METRIC_MAX = "max=";
+ private static final String METRIC_AVG = "avg=";
+
+ private static final String METRIC_HIT = "hit=";
+ private static final String METRIC_PCT = "pct";
+
+ private static final String METRIC_MAXAT = "maxAT=";
+ private static final String METRIC_MINAT = "minAT=";
+ private static final String METRIC_MEANAT = "meanAT=";
+ private static final String METRIC_STARTEDT = "startedT=";
+ private static final String METRIC_ENDEDT = "endedT=";
+
+ private static final String TAG_OK = "ok";
+ private static final String TAG_KO = "ko";
+ private static final String TAG_ALL = "all";
+
+ private static final String CUMULATED_METRICS = "all";
+ private static final long FIVE_SECOND = 5L;
+ private static final int MAX_POOL_SIZE = 1;
+ private static final String SEPARATOR = ";"; //$NON-NLS-1$
+ private static final Object LOCK = new Object();
+
+ private boolean summaryOnly;
+ private String measurement = "DEFAULT_MEASUREMENT";
+ private String influxdbUrl = "";
+ private String samplersRegex = "";
+ private Pattern samplersToFilter;
+ private Map<String, Float> okPercentiles;
+ private Map<String, Float> koPercentiles;
+ private Map<String, Float> allPercentiles;
+ private String testTitle;
+
+ private InfluxdbMetricsSender influxdbMetricsManager;
+
+ private ScheduledExecutorService scheduler;
+ private ScheduledFuture<?> timerHandle;
+
+ public InfluxdbBackendListenerClient() {
+ super();
+ }
+
+ @Override
+ public void run() {
+ sendMetrics();
+ }
+
+ /**
+ * Send metrics
+ */
+ protected void sendMetrics() {
+
+ synchronized (LOCK) {
+ for (Map.Entry<String, SamplerMetric> entry :
getMetricsInfluxdbPerSampler().entrySet()) {
+ SamplerMetric metric = entry.getValue();
+ if (entry.getKey().equals(CUMULATED_METRICS)) {
+ addCumulatedMetrics(metric);
+ } else {
+
addMetrics(AbstractInfluxdbMetricsSender.toStringValue(entry.getKey()), metric);
+ }
+ // We are computing on interval basis so cleanup
+ metric.resetForTimeInterval();
+ }
+ }
+
+
+ // For JMETER context
+ StringBuilder tag = new StringBuilder(60);
+ tag.append(TAG_APPLICATION).append(application);
+ tag.append(TAG_TRANSACTION).append("internal");
+ StringBuilder field = new StringBuilder(80);
+
field.append(METRIC_MINAT).append(getUserMetrics().getMinActiveThreads()).append(",");
+
field.append(METRIC_MAXAT).append(getUserMetrics().getMaxActiveThreads()).append(",");
+
field.append(METRIC_MEANAT).append(getUserMetrics().getMeanActiveThreads()).append(",");
+
field.append(METRIC_STARTEDT).append(getUserMetrics().getStartedThreads()).append(",");
+
field.append(METRIC_ENDEDT).append(getUserMetrics().getFinishedThreads());
+
+ influxdbMetricsManager.addMetric(measurement, tag.toString(),
field.toString());
+
+ influxdbMetricsManager.writeAndSendMetrics();
+ }
+
+ /**
+ * Add request metrics to metrics manager.
+ *
+ * @param metric
+ * {@link SamplerMetric}
+ */
+ private void addMetrics(String transaction, SamplerMetric metric) {
+ // FOR ALL STATUS
+ addMetric(transaction, metric, metric.getTotal(), false, TAG_ALL,
metric.getAllMean(), metric.getAllMinTime(),
+ metric.getAllMaxTime(), allPercentiles.values());
+ // FOR OK STATUS
+ addMetric(transaction, metric, metric.getSuccesses(), false, TAG_OK,
metric.getOkMean(), metric.getOkMinTime(),
+ metric.getOkMaxTime(), Collections.<Float> emptySet());
+ // FOR KO STATUS
+ addMetric(transaction, metric, metric.getFailures(), true, TAG_KO,
metric.getKoMean(), metric.getKoMinTime(),
+ metric.getKoMaxTime(), Collections.<Float> emptySet());
+ }
+
+ private void addMetric(String transaction, SamplerMetric metric, int
count, boolean includeResponseCode,
+ String statut, double mean, double minTime, double maxTime,
Collection<Float> pcts) {
+ if (count > 0) {
+ StringBuilder tag = new StringBuilder(70);
+ tag.append(TAG_APPLICATION).append(application);
+ tag.append(TAG_STATUT).append(statut);
+ tag.append(TAG_TRANSACTION).append(transaction);
+ StringBuilder field = new StringBuilder(80);
+ field.append(METRIC_COUNT).append(count);
+ if (!Double.isNaN(mean)) {
+ field.append(",").append(METRIC_AVG).append(mean);
+ }
+ if (!Double.isNaN(minTime)) {
+ field.append(",").append(METRIC_MIN).append(minTime);
+ }
+ if (!Double.isNaN(maxTime)) {
+ field.append(",").append(METRIC_MAX).append(maxTime);
+ }
+ for (Float pct : pcts) {
+
field.append(",").append(METRIC_PCT).append(pct).append("=").append(metric.getAllPercentile(pct));
+ }
+ influxdbMetricsManager.addMetric(measurement, tag.toString(),
field.toString());
+ }
+ }
+
+ private void addCumulatedMetrics(SamplerMetric metric) {
+ int total = metric.getTotal();
+ if (total > 0) {
+ StringBuilder tag = new StringBuilder(70);
+ StringBuilder field = new StringBuilder(100);
+ Collection<Float> pcts = allPercentiles.values();
+ tag.append(TAG_APPLICATION).append(application);
+ tag.append(TAG_TRANSACTION).append(CUMULATED_METRICS);
+ tag.append(TAG_STATUT).append(CUMULATED_METRICS);
+
+ field.append(METRIC_COUNT).append(total);
+
field.append(",").append(METRIC_COUNT_ERREUR).append(metric.getFailures());
+
+ if (!Double.isNaN(metric.getOkMean())) {
+
field.append(",").append(METRIC_AVG).append(Double.toString(metric.getOkMean()));
+ }
+ if (!Double.isNaN(metric.getOkMinTime())) {
+
field.append(",").append(METRIC_MIN).append(Double.toString(metric.getOkMinTime()));
+ }
+ if (!Double.isNaN(metric.getOkMaxTime())) {
+
field.append(",").append(METRIC_MAX).append(Double.toString(metric.getOkMaxTime()));
+ }
+
+ field.append(",").append(METRIC_HIT).append(metric.getHits());
+ for (Float pct : pcts) {
+
field.append(",").append(METRIC_PCT).append(pct).append("=").append(Double.toString(metric.getAllPercentile(pct)));
+ }
+ field.append(",").append(METRIC_HIT).append(metric.getHits());
+ influxdbMetricsManager.addMetric(measurement, tag.toString(),
field.toString());
+ }
+ }
+
+ /**
+ * @return the samplersList
+ */
+ public String getSamplersRegex() {
+ return samplersRegex;
+ }
+
+ /**
+ * @param samplersList
+ * the samplersList to set
+ */
+ public void setSamplersList(String samplersList) {
+ this.samplersRegex = samplersList;
+ }
+
+ @Override
+ public void handleSampleResults(List<SampleResult> sampleResults,
BackendListenerContext context) {
+ synchronized (LOCK) {
+ for (SampleResult sampleResult : sampleResults) {
+ getUserMetrics().add(sampleResult);
+ Matcher matcher =
samplersToFilter.matcher(sampleResult.getSampleLabel());
+ if (!summaryOnly && (matcher.find())) {
+ SamplerMetric samplerMetric =
getSamplerMetricInfluxdb(sampleResult.getSampleLabel());
+ samplerMetric.add(sampleResult);
+ }
+ SamplerMetric cumulatedMetrics =
getSamplerMetricInfluxdb(CUMULATED_METRICS);
+ cumulatedMetrics.add(sampleResult);
+ }
+ }
+ }
+
+ @Override
+ public void setupTest(BackendListenerContext context) throws Exception {
+ String influxdbMetricsSender =
context.getParameter("influxdbMetricsSender");
+ influxdbUrl = context.getParameter("influxdbUrl");
+ summaryOnly = context.getBooleanParameter("summaryOnly", false);
+ samplersRegex = context.getParameter("samplersRegex", "");
+ application =
AbstractInfluxdbMetricsSender.toStringValue(context.getParameter("application",
""));
+ measurement = AbstractInfluxdbMetricsSender
+ .toStringValue(context.getParameter("measurement",
DEFAULT_MEASUREMENT));
+ testTitle =
AbstractInfluxdbMetricsSender.toStringValue(context.getParameter("testTitle",
"Test"));
+ String percentilesAsString = context.getParameter("percentiles", "");
+ String[] percentilesStringArray = percentilesAsString.split(SEPARATOR);
+ okPercentiles = new HashMap<>(percentilesStringArray.length);
+ koPercentiles = new HashMap<>(percentilesStringArray.length);
+ allPercentiles = new HashMap<>(percentilesStringArray.length);
+ DecimalFormat format = new DecimalFormat("0.##");
+ for (int i = 0; i < percentilesStringArray.length; i++) {
+ if (!StringUtils.isEmpty(percentilesStringArray[i].trim())) {
+ try {
+ Float percentileValue =
Float.valueOf(percentilesStringArray[i].trim());
+
okPercentiles.put(AbstractInfluxdbMetricsSender.toStringValue(format.format(percentileValue)),
+ percentileValue);
+
koPercentiles.put(AbstractInfluxdbMetricsSender.toStringValue(format.format(percentileValue)),
+ percentileValue);
+
allPercentiles.put(AbstractInfluxdbMetricsSender.toStringValue(format.format(percentileValue)),
+ percentileValue);
+
+ } catch (Exception e) {
+ LOGGER.error("Error parsing percentile:'" +
percentilesStringArray[i] + "'", e);
+ }
+ }
+ }
+ Class<?> clazz = Class.forName(influxdbMetricsSender);
+ this.influxdbMetricsManager = (InfluxdbMetricsSender)
clazz.newInstance();
+ influxdbMetricsManager.setup(influxdbUrl);
+ samplersToFilter = Pattern.compile(samplersRegex);
+
+ // Annotation of the start of the run
+ influxdbMetricsManager.addMetric(EVENTS_FOR_ANNOTATION,
TAG_APPLICATION + application,
+ "title=\"JMETER\""
+ +",text=\"" + testTitle + " started\""
+ + ",tags=\"" + application + "\"");
+
+ scheduler = Executors.newScheduledThreadPool(MAX_POOL_SIZE);
+ // Start scheduler and put the pooling to 5 seconds
+ this.timerHandle = scheduler.scheduleAtFixedRate(this, FIVE_SECOND,
FIVE_SECOND, TimeUnit.SECONDS);
+
+ }
+
+ protected SamplerMetric getSamplerMetricInfluxdb(String sampleLabel) {
+ SamplerMetric samplerMetric = metricsPerSampler.get(sampleLabel);
+ if (samplerMetric == null) {
+ samplerMetric = new SamplerMetric();
+ SamplerMetric oldValue =
metricsPerSampler.putIfAbsent(sampleLabel, samplerMetric);
+ if (oldValue != null) {
+ samplerMetric = oldValue;
+ }
+ }
+ return samplerMetric;
+ }
+
+ private Map<String, SamplerMetric> getMetricsInfluxdbPerSampler() {
+ return metricsPerSampler;
+ }
+
+ @Override
+ public void teardownTest(BackendListenerContext context) throws Exception {
+ boolean cancelState = timerHandle.cancel(false);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Canceled state:" + cancelState);
+ }
+ scheduler.shutdown();
+ try {
+ scheduler.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Error waiting for end of scheduler");
+ }
+ // Annotation of the end of the run ( usefull with Grafana )
+ influxdbMetricsManager.addMetric(EVENTS_FOR_ANNOTATION,
TAG_APPLICATION + application,
+ "title=\"JMETER\""
+ +",text=\"" + testTitle + " ended\""
+ + ",tags=\"" + application + "\"");
+ // Send last set of data before ending
+ sendMetrics();
+
+ influxdbMetricsManager.destroy();
+ super.teardownTest(context);
+ }
+
+ @Override
+ public Arguments getDefaultParameters() {
+ Arguments arguments = new Arguments();
+ arguments.addArgument("influxdbMetricsSender",
HttpMetricsSender.class.getName());
+ arguments.addArgument("influxdbUrl", "");
+ arguments.addArgument("application", "application name");
+ arguments.addArgument("measurement", DEFAULT_MEASUREMENT);
+ arguments.addArgument("summaryOnly", "false");
+ arguments.addArgument("samplersRegex", ".*");
+ arguments.addArgument("percentiles", "99,95,90");
+ arguments.addArgument("testTitle", "Test name");
+ return arguments;
+ }
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java
URL:
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java?rev=1778947&view=auto
==============================================================================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java
Sun Jan 15 21:19:52 2017
@@ -0,0 +1,69 @@
+/*
+ * 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.influxdb;
+
+/**
+ * InfluxDB Sender interface
+ * @since 3.2
+ *
+ */
+interface InfluxdbMetricsSender {
+
+ /**
+ * One data point in InfluxDB is represented by a measurement name, a tag
+ * set and a field set ( optionally a timestamp )
+ */
+ final class MetricTuple {
+ String measurement;
+ String tag;
+ String field;
+
+ MetricTuple(String measurment, String tag, String field) {
+ this.measurement = measurment;
+ this.tag = tag;
+ this.field = field;
+ }
+ }
+
+ /**
+ *
+ * @param measurement
+ * @param tag
+ * @param field
+ */
+ public void addMetric(String measurement, String tag, String field);
+
+ /**
+ * Write metrics to Influxdb with HTTP API with InfluxDBâs Line Protocol
+ */
+ public void writeAndSendMetrics();
+
+ /**
+ * Setup sender using influxDBUrl
+ * @param influxdbUrl
+ * @throws Exception
+ */
+ public void setup(String influxDBUrl) throws Exception;
+
+ /**
+ * Destroy sender
+ */
+ public void destroy();
+
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: jmeter/trunk/xdocs/changes.xml
URL:
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1778947&r1=1778946&r2=1778947&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Sun Jan 15 21:19:52 2017
@@ -125,6 +125,7 @@ JMeter now requires Java 8. Ensure you u
<li><bug>60144</bug>View Results Tree : Add a more up to date Browser
Renderer to replace old Render</li>
<li><bug>60542</bug>View Results Tree : Allow Upper Panel to be collapsed.
Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
<li><bug>52962</bug>Allow sorting by columns for View Results in Table,
Summary Report, Aggregate Report and Aggregate Graph. Based on a <pr>245</pr>
by Logan Mauzaize (logan.mauzaize at gmail.com) and Maxime Chassagneux
(maxime.chassagneux at gmail.com).</li>
+ <li><bug>60590</bug>BackendListener : Add Influxdb BackendListenerClient
implementation to JMeter. Partly based on <pr>246</pr> by Logan Mauzaize
(logan.mauzaize at gmail.com) and Maxime Chassagneux (maxime.chassagneux at
gmail.com).</li>
</ul>
<h3>Timers, Assertions, Config, Pre- & Post-Processors</h3>
Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL:
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1778947&r1=1778946&r2=1778947&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Sun Jan 15 21:19:52
2017
@@ -3471,7 +3471,7 @@ By default, a Graphite implementation is
</properties>
- <p>The following parameters apply to the
<code>GraphiteBackendListenerClient</code> implementation:</p>
+ <p>The following parameters apply to the <a
href="../api/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.html">GraphiteBackendListenerClient</a>
implementation:</p>
<properties>
<property name="graphiteMetricsSender"
required="Yes"><code>org.apache.jmeter.visualizers.backend.graphite.TextGraphiteMetricsSender</code>
or
<code>org.apache.jmeter.visualizers.backend.graphite.PickleGraphiteMetricsSender</code></property>
@@ -3491,6 +3491,25 @@ By default, a Graphite implementation is
</properties>
<p>See also <a href="realtime-results.html" >Real-time results</a> for
more details.</p>
<figure width="1265" height="581" image="grafana_dashboard.png">Grafana
dashboard</figure>
+
+
+ <p>Since JMeter 3.2, a new implementation (in Alpha state) has been added
that allows writing directly in InfluxDB with a custom schema, it is called
<code>InfluxdbBackendListenerClient</code>
+ The following parameters apply to the <a
href="../api/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.html">InfluxdbBackendListenerClient</a>
implementation:</p>
+
+ <properties>
+ <property name="influxdbMetricsSender"
required="Yes"><code>org.apache.jmeter.visualizers.backend.influxdb.HttpMetricsSender</code></property>
+ <property name="influxdbUrl" required="Yes">Influx URL (example :
http://influxHost:8086/write?db=jmeter)</property>
+ <property name="application" required="Yes">Name of tested
application</property>
+ <property name="measurement" required="Yes">Measurement as per <a
href="https://docs.influxdata.com/influxdb/v1.1/write_protocols/line_protocol_reference/">Influx
Line Protocol Reference</a>. Defaults to "<code>jmeter</code>."</property>
+ <property name="summaryOnly" required="Yes">Only send a summary with
no detail. Defaults to <code>true</code>.</property>
+ <property name="samplersRegex" required="Yes">Regular expression which
will be matched against the names of samples and sent to the back
end.</property>
+ <property name="testTitle" required="Yes">Test name. Defaults to
<code>Test name</code>.</property>
+ <property name="percentiles" required="Yes">The percentiles you want
to send to the backend.
+ A percentile may contain a fractional part, for example
<code>12.5</code>.
+ (The separator is always ".")
+ List must be semicolon separated. Generally 3 or 4 values should be
sufficient.</property>
+ </properties>
+ <p>See also <a href="realtime-results.html" >Real-time results</a> for
more details.</p>
</component>
<a href="#">^</a>