jmeter-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pmoua...@apache.org
Subject svn commit: r1778947 - in /jmeter/trunk: src/components/org/apache/jmeter/visualizers/backend/influxdb/ xdocs/ xdocs/usermanual/
Date Sun, 15 Jan 2017 21:19:52 GMT
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- &amp; 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>



Mime
View raw message