You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2014/11/22 16:40:35 UTC
svn commit: r1641083 - in /jmeter/trunk:
src/components/org/apache/jmeter/visualizers/backend/graphite/ xdocs/
Author: pmouawad
Date: Sat Nov 22 15:40:35 2014
New Revision: 1641083
URL: http://svn.apache.org/r1641083
Log:
Bug 57246 - BackendListener : Create a Graphite implementation
Bugzilla Id: 57246
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java (with props)
Modified:
jmeter/trunk/xdocs/changes.xml
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,65 @@
+/*
+ * 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.graphite;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+
+/**
+ * Base class for {@link GraphiteMetricsSender}
+ * @since 2.13
+ */
+abstract class AbstractGraphiteMetricsSender implements GraphiteMetricsSender {
+
+ /**
+ * @return GenericKeyedObjectPool
+ *
+ */
+ protected GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream> createSocketOutputStreamPool() {
+ GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+ config.setMaxTotalPerKey(-1);
+ config.setMaxTotal(-1);
+ config.setMaxIdlePerKey(-1);
+ config.setMinEvictableIdleTimeMillis(TimeUnit.MINUTES.toMillis(3));
+ config.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES.toMillis(3));
+
+ return new GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream>(
+ new SocketOutputStreamPoolFactory(SOCKET_CONNECT_TIMEOUT_MS, SOCKET_TIMEOUT), config);
+ }
+
+ /**
+ * Replaces Graphite reserved chars:
+ * <ul>
+ * <li>' ' by '-'</li>
+ * <li>'\\' by '-'</li>
+ * <li>'.' by '_'</li>
+ * </ul>
+ * @param s
+ * @return
+ */
+ static final String sanitizeString(String s) {
+ // String#replace uses regexp
+ return StringUtils.replaceChars(s, "\\ .", "--_");
+ }
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/AbstractGraphiteMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: 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=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,194 @@
+/*
+ * 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.graphite;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterContextService.ThreadCounts;
+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;
+
+/**
+ * Graphite based Listener using Pickle Protocol
+ * @see http://graphite.readthedocs.org/en/latest/overview.html
+ * @since 2.13
+ */
+public class GraphiteBackendListenerClient extends AbstractBackendListenerClient implements Runnable {
+ private static final int DEFAULT_PICKLE_PORT = 2004;
+ private static final String CUMULATED_CONTEXT_NAME = "cumulated";
+
+ 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_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$
+ private static final String METRIC_SUCCESSFUL_REQUESTS = "success"; //$NON-NLS-1$
+ 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 long ONE_SECOND = 1L;
+ private static final int MAX_POOL_SIZE = 1;
+
+ private String graphiteHost;
+ private int graphitePort;
+ private boolean summaryOnly;
+ private String rootMetricsPrefix;
+ private String samplersList = ""; //$NON-NLS-1$
+ private transient Set<String> samplersToFilter;
+
+
+ private GraphiteMetricsSender pickleMetricsManager;
+
+ private ScheduledExecutorService scheduler;
+
+ public GraphiteBackendListenerClient() {
+ super();
+ }
+
+ @Override
+ public void run() {
+ // Need to convert millis to seconds for Graphite
+ long timestamp = TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ for (Map.Entry<String, SamplerMetric> entry : getMetricsPerSampler().entrySet()) {
+ SamplerMetric metric = entry.getValue();
+ if(entry.getKey().equals(CUMULATED_METRICS)) {
+ addMetrics(timestamp, CUMULATED_CONTEXT_NAME, metric);
+ } else {
+ addMetrics(timestamp, AbstractGraphiteMetricsSender.sanitizeString(entry.getKey()), metric);
+ }
+ // We are computing on interval basis so cleanup
+ metric.resetForTimeInterval();
+ }
+
+ ThreadCounts tc = JMeterContextService.getThreadCounts();
+ pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME, METRIC_ACTIVE_THREADS, Integer.toString(tc.activeThreads));
+ 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));
+
+ pickleMetricsManager.writeAndSendMetrics();
+ }
+
+
+ /**
+ * @param timestamp
+ * @param contextName
+ * @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_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)));
+ }
+
+ /**
+ * @return the samplersList
+ */
+ public String getSamplersList() {
+ return samplersList;
+ }
+
+ /**
+ * @param samplersList the samplersList to set
+ */
+ public void setSamplersList(String samplersList) {
+ this.samplersList = samplersList;
+ }
+
+ @Override
+ public void handleSampleResults(List<SampleResult> sampleResults,
+ BackendListenerContext context) {
+ for (SampleResult sampleResult : sampleResults) {
+ if(!summaryOnly && samplersToFilter.contains(sampleResult.getSampleLabel())) {
+ SamplerMetric samplerMetric = getSamplerMetric(sampleResult.getSampleLabel());
+ samplerMetric.add(sampleResult);
+ }
+ SamplerMetric cumulatedMetrics = getSamplerMetric(CUMULATED_METRICS);
+ cumulatedMetrics.add(sampleResult);
+ }
+ }
+
+ @Override
+ public void setupTest(BackendListenerContext context) throws Exception {
+ String graphiteMetricsSenderClass = context.getParameter("graphiteMetricsSender");
+
+ graphiteHost = context.getParameter("graphiteHost");
+ graphitePort = context.getIntParameter("graphitePort", DEFAULT_PICKLE_PORT);
+ summaryOnly = context.getBooleanParameter("summaryOnly", true);
+ samplersList = context.getParameter("samplersList", "");
+ rootMetricsPrefix = context.getParameter("rootMetricsPrefix", DEFAULT_METRICS_PREFIX);
+ Class clazz = Class.forName(graphiteMetricsSenderClass);
+ this.pickleMetricsManager = (GraphiteMetricsSender) clazz.newInstance();
+ pickleMetricsManager.setup(graphiteHost, graphitePort, rootMetricsPrefix);
+ String[] samplers = samplersList.split(",");
+ samplersToFilter = new HashSet<String>();
+ for (String samplerName : samplers) {
+ samplersToFilter.add(samplerName);
+ }
+ scheduler = Executors.newScheduledThreadPool(MAX_POOL_SIZE);
+ // Don't change this as metrics are per second
+ scheduler.scheduleAtFixedRate(this, ONE_SECOND, ONE_SECOND, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void teardownTest(BackendListenerContext context) throws Exception {
+ scheduler.shutdown();
+ try {
+ scheduler.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Error waiting for end of scheduler");
+ }
+
+ samplersToFilter.clear();
+ pickleMetricsManager.destroy();
+ super.teardownTest(context);
+ }
+
+ @Override
+ public Arguments getDefaultParameters() {
+ Arguments arguments = new Arguments();
+ arguments.addArgument("graphiteMetricsSender", TextGraphiteMetricsSender.class.getName());
+ arguments.addArgument("graphiteHost", "");
+ arguments.addArgument("graphitePort", Integer.toString(DEFAULT_PICKLE_PORT));
+ arguments.addArgument("rootMetricsPrefix", DEFAULT_METRICS_PREFIX);
+ arguments.addArgument("summaryOnly", "true");
+ arguments.addArgument("samplersList", "");
+ return arguments;
+ }
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,71 @@
+/*
+ * 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.graphite;
+
+/**
+ * @since 2.13
+ */
+interface GraphiteMetricsSender {
+ final int SOCKET_CONNECT_TIMEOUT_MS = 1000;
+ final int SOCKET_TIMEOUT = 1000;
+
+
+ String CHARSET_NAME = "UTF-8"; //$NON-NLS-1$
+
+ final class MetricTuple {
+ String name;
+ long timestamp;
+ String value;
+ MetricTuple(String name, long timestamp, String value) {
+ this.name = name;
+ this.timestamp = timestamp;
+ this.value = value;
+ }
+ }
+ /**
+ * Convert the metric to a python tuple of the form:
+ * (timestamp, (prefix.contextName.metricName, metricValue))
+ * And add it to the list of metrics.
+ * @param timestamp in Seconds from 1970
+ * @param contextName
+ * @param metricName
+ * @param metricValue
+ */
+ public abstract void addMetric(long timestamp, String contextName,
+ String metricName, String metricValue);
+
+ /**
+ *
+ * @param graphiteHost Host
+ * @param graphitePort Port
+ * @param prefix Root Data prefix
+ */
+ public void setup(String graphiteHost, int graphitePort, String prefix);
+
+ /**
+ * Write metrics to Graphite using custom format
+ */
+ public abstract void writeAndSendMetrics();
+
+ /**
+ * Destroy sender
+ */
+ public abstract void destroy();
+
+}
\ No newline at end of file
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/GraphiteMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,185 @@
+/*
+ * 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.graphite;
+
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * Pickle Graphite format
+ * Partly based on https://github.com/BrightcoveOS/metrics-graphite-pickle/blob/master/src/main/java/com/brightcove/metrics/reporting/GraphitePickleReporter.java
+ * as per license https://github.com/BrightcoveOS/metrics-graphite-pickle/blob/master/LICENSE.txt
+ * @since 2.13
+ */
+class PickleGraphiteMetricsSender extends AbstractGraphiteMetricsSender {
+ private static final Logger LOG = LoggingManager.getLoggerForClass();
+
+ /**
+ * Pickle opcodes needed for implementation
+ */
+ private static final char APPEND = 'a';
+ private static final char LIST = 'l';
+ private static final char LONG = 'L';
+ private static final char MARK = '(';
+ private static final char STOP = '.';
+ private static final char STRING = 'S';
+ private static final char TUPLE = 't';
+ private static final char QUOTE = '\'';
+ private static final char LF = '\n';
+
+ private String prefix;
+
+ // graphite expects a python-pickled list of nested tuples.
+ private List<MetricTuple> metrics = new LinkedList<MetricTuple>();
+
+ private GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream> socketOutputStreamPool;
+
+ private SocketConnectionInfos socketConnectionInfos;
+
+
+ PickleGraphiteMetricsSender() {
+ super();
+ }
+
+ /**
+ * @param graphiteHost Graphite Host
+ * @param graphitePort Graphite Port
+ * @param prefix Common Metrics prefix
+ */
+ @Override
+ public void setup(String graphiteHost, int graphitePort, String prefix) {
+ this.prefix = prefix;
+ this.socketConnectionInfos = new SocketConnectionInfos(graphiteHost, graphitePort);
+ this.socketOutputStreamPool = createSocketOutputStreamPool();
+
+ if(LOG.isInfoEnabled()) {
+ LOG.info("Created PickleGraphiteMetricsSender with host:"+graphiteHost+", port:"+graphitePort+", prefix:"+prefix);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#addMetric(long, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void addMetric(long timestamp, String contextName, String metricName, String metricValue) {
+ StringBuilder sb = new StringBuilder(50);
+ sb
+ .append(prefix)
+ .append(contextName)
+ .append(".")
+ .append(metricName);
+ metrics.add(new MetricTuple(sb.toString(), timestamp, metricValue));
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#writeAndSendMetrics()
+ */
+ @Override
+ public void writeAndSendMetrics() {
+ if (metrics.size()>0) {
+ SocketOutputStream out = null;
+ try {
+ String payload = convertMetricsToPickleFormat(metrics);
+
+ int length = payload.length();
+ byte[] header = ByteBuffer.allocate(4).putInt(length).array();
+
+ out = socketOutputStreamPool.borrowObject(socketConnectionInfos);
+ out.write(header);
+ Writer pickleWriter = new OutputStreamWriter(out, CHARSET_NAME);
+ pickleWriter.write(payload);
+ pickleWriter.flush();
+ socketOutputStreamPool.returnObject(socketConnectionInfos, out);
+ } catch (Exception e) {
+ if(out != null) {
+ try {
+ socketOutputStreamPool.invalidateObject(socketConnectionInfos, out);
+ } catch (Exception e1) {
+ LOG.warn("Exception invalidating socketOutputStream connected to graphite server '"+socketConnectionInfos.getHost()+"':"+socketConnectionInfos.getPort(), e1);
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Error writing to Graphite", e);
+ } else {
+ LOG.warn("Error writing to Graphite:"+e.getMessage());
+ }
+ }
+
+ // if there was an error, we might miss some data. for now, drop those on the floor and
+ // try to keep going.
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Wrote "+ metrics.size() +" metrics");
+ }
+ metrics.clear();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#destroy()
+ */
+ @Override
+ public void destroy() {
+ socketOutputStreamPool.close();
+ }
+
+ /**
+ * See: http://readthedocs.org/docs/graphite/en/1.0/feeding-carbon.html
+ */
+ private static final String convertMetricsToPickleFormat(List<MetricTuple> metrics) {
+ StringBuilder pickled = new StringBuilder(metrics.size()*75);
+ pickled.append(MARK).append(LIST);
+
+ for (MetricTuple tuple : metrics) {
+ // begin outer tuple
+ pickled.append(MARK);
+
+ // the metric name is a string.
+ pickled.append(STRING)
+ // the single quotes are to match python's repr("abcd")
+ .append(QUOTE).append(tuple.name).append(QUOTE).append(LF);
+
+ // begin the inner tuple
+ pickled.append(MARK);
+
+ // timestamp is a long
+ pickled.append(LONG).append(tuple.timestamp)
+ // the trailing L is to match python's repr(long(1234))
+ .append(LONG).append(LF);
+
+ // and the value is a string.
+ pickled.append(STRING).append(QUOTE).append(tuple.value).append(QUOTE).append(LF);
+
+ pickled.append(TUPLE) // end inner tuple
+ .append(TUPLE); // end outer tuple
+
+ pickled.append(APPEND);
+ }
+
+ // every pickle ends with STOP
+ pickled.append(STOP);
+ return pickled.toString();
+ }
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/PickleGraphiteMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,63 @@
+/*
+ * 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.graphite;
+
+/**
+ * Bean to embed host/port to Graphite
+ * @since 2.13
+ */
+public class SocketConnectionInfos {
+ private String host;
+ private int port;
+
+ /**
+ * @param host
+ * @param port
+ */
+ public SocketConnectionInfos(String host, int port) {
+ super();
+ this.host = host;
+ this.port = port;
+ }
+
+ /**
+ * @return the host
+ */
+ public String getHost() {
+ return host;
+ }
+ /**
+ * @param host the host to set
+ */
+ public void setHost(String host) {
+ this.host = host;
+ }
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+ /**
+ * @param port the port to set
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketConnectionInfos.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,57 @@
+/*
+ * 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.graphite;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Convenience class for writing bytes to a {@linkplain java.net.Socket}.
+ * @since 2.13
+ */
+public class SocketOutputStream extends FilterOutputStream {
+
+ private final Socket socket;
+
+ public SocketOutputStream(InetSocketAddress inetSocketAddress) throws IOException {
+ this(new Socket(inetSocketAddress.getAddress(), inetSocketAddress.getPort()));
+ }
+
+ public SocketOutputStream(Socket socket) throws IOException {
+ super(socket.getOutputStream());
+ this.socket = socket;
+ }
+
+ /**
+ * Return the underlying Socket
+ */
+ public Socket getSocket() {
+ return socket;
+ }
+
+ @Override
+ public String toString() {
+ return "SocketOutputStream{" +
+ "socket=" + socket +
+ '}';
+ }
+
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStream.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,85 @@
+/*
+ * 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.graphite;
+
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
+import org.apache.commons.pool2.KeyedPooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+/**
+ * Pool Factory of {@link SocketOutputStream}
+ * @since 2.13
+ */
+public class SocketOutputStreamPoolFactory
+ extends BaseKeyedPooledObjectFactory<SocketConnectionInfos, SocketOutputStream>
+ implements KeyedPooledObjectFactory<SocketConnectionInfos, SocketOutputStream> {
+
+ private final int socketTimeoutInMillis;
+ private final int socketConnectTimeoutInMillis;
+
+ public SocketOutputStreamPoolFactory(int socketConnectTimeoutInMillis, int socketTimeoutInMillis) {
+ this.socketConnectTimeoutInMillis = socketConnectTimeoutInMillis;
+ this.socketTimeoutInMillis = socketTimeoutInMillis;
+ }
+
+ @Override
+ public PooledObject<SocketOutputStream> makeObject(SocketConnectionInfos connectionInfos) throws Exception {
+ return wrap(create(connectionInfos));
+ }
+
+ @Override
+ public void destroyObject(SocketConnectionInfos socketConnectionInfos, PooledObject<SocketOutputStream> socketOutputStream) throws Exception {
+ super.destroyObject(socketConnectionInfos, socketOutputStream);
+ SocketOutputStream outputStream = socketOutputStream.getObject();
+ outputStream.close();
+ outputStream.getSocket().close();
+ }
+
+ /**
+ */
+ @Override
+ public boolean validateObject(SocketConnectionInfos HostAndPort, PooledObject<SocketOutputStream> socketOutputStream) {
+ Socket socket = socketOutputStream.getObject().getSocket();
+ return socket.isConnected()
+ && socket.isBound()
+ && !socket.isClosed()
+ && !socket.isInputShutdown()
+ && !socket.isOutputShutdown();
+ }
+
+ @Override
+ public SocketOutputStream create(SocketConnectionInfos connectionInfos)
+ throws Exception {
+ Socket socket = new Socket();
+ socket.setKeepAlive(true);
+ socket.setSoTimeout(socketTimeoutInMillis);
+ socket.connect(new InetSocketAddress(connectionInfos.getHost(), connectionInfos.getPort()), socketConnectTimeoutInMillis);
+
+ return new SocketOutputStream(socket);
+ }
+
+ @Override
+ public PooledObject<SocketOutputStream> wrap(SocketOutputStream outputStream) {
+ return new DefaultPooledObject<SocketOutputStream>(outputStream);
+ }
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/SocketOutputStreamPoolFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java?rev=1641083&view=auto
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java Sat Nov 22 15:40:35 2014
@@ -0,0 +1,127 @@
+/*
+ * 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.graphite;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * PlainText Graphite sender
+ * @since 2.13
+ */
+class TextGraphiteMetricsSender extends AbstractGraphiteMetricsSender {
+ private static final Logger LOG = LoggingManager.getLoggerForClass();
+
+ private String prefix;
+
+ private List<MetricTuple> metrics = new ArrayList<MetricTuple>();
+
+ private GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream> socketOutputStreamPool;
+
+ private SocketConnectionInfos socketConnectionInfos;
+
+ /**
+ * @param graphiteHost Graphite Host
+ * @param graphitePort Graphite Port
+ * @param prefix Common Metrics prefix
+ */
+ TextGraphiteMetricsSender() {
+ super();
+ }
+
+ @Override
+ public void setup(String graphiteHost, int graphitePort, String prefix) {
+ this.prefix = prefix;
+ this.socketConnectionInfos = new SocketConnectionInfos(graphiteHost, graphitePort);
+ this.socketOutputStreamPool = createSocketOutputStreamPool();
+
+ if(LOG.isInfoEnabled()) {
+ LOG.info("Created TextGraphiteMetricsSender with host:"+graphiteHost+", port:"+graphitePort+", prefix:"+prefix);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#addMetric(long, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void addMetric(long timestamp, String contextName, String metricName, String metricValue) {
+ StringBuilder sb = new StringBuilder(50);
+ sb
+ .append(prefix)
+ .append(contextName)
+ .append(".")
+ .append(metricName);
+ metrics.add(new MetricTuple(sb.toString(), timestamp, metricValue));
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#writeAndSendMetrics()
+ */
+ @Override
+ public void writeAndSendMetrics() {
+ if (metrics.size()>0) {
+ SocketOutputStream out = null;
+ try {
+ out = socketOutputStreamPool.borrowObject(socketConnectionInfos);
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, CHARSET_NAME), false);
+ for (MetricTuple metric: metrics) {
+ pw.printf("%s %s %d%n", metric.name, metric.value, Long.valueOf(metric.timestamp));
+ }
+ pw.flush();
+ // if there was an error, we might miss some data. for now, drop those on the floor and
+ // try to keep going.
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Wrote "+ metrics.size() +" metrics");
+ }
+ socketOutputStreamPool.returnObject(socketConnectionInfos, out);
+ } catch (Exception e) {
+ if(out != null) {
+ try {
+ socketOutputStreamPool.invalidateObject(socketConnectionInfos, out);
+ } catch (Exception e1) {
+ LOG.warn("Exception invalidating socketOutputStream connected to graphite server '"+
+ socketConnectionInfos.getHost()+"':"+socketConnectionInfos.getPort(), e1);
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Error writing to Graphite", e);
+ } else {
+ LOG.warn("Error writing to Graphite:"+e.getMessage());
+ }
+ }
+ // We drop metrics in all cases
+ metrics.clear();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.graphite.GraphiteMetricsSender#destroy()
+ */
+ @Override
+ public void destroy() {
+ socketOutputStreamPool.close();
+ }
+
+}
Propchange: jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/graphite/TextGraphiteMetricsSender.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1641083&r1=1641082&r2=1641083&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml (original)
+++ jmeter/trunk/xdocs/changes.xml Sat Nov 22 15:40:35 2014
@@ -189,6 +189,7 @@ See <bugzilla>56357</bugzilla> for deta
<h3>Listeners</h3>
<ul>
<li><bugzilla>55932</bugzilla> - Create a Async BackendListener to allow easy plug of new listener (Graphite, JDBC, Console,...)</li>
+<li><bugzilla>57246</bugzilla> - BackendListener : Create a Graphite implementation</li>
</ul>
<h3>Timers, Assertions, Config, Pre- & Post-Processors</h3>