All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kg.apc.jmeter.perfmon.PerfMonCollector Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package kg.apc.jmeter.perfmon;

import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.reporters.LoadosophiaUploadingNotifier;
import kg.apc.jmeter.vizualizers.CorrectedResultCollector;
import kg.apc.perfmon.PerfMonMetricGetter;
import kg.apc.perfmon.client.Transport;
import kg.apc.perfmon.client.TransportFactory;
import kg.apc.perfmon.metrics.MetricParams;
import org.apache.jmeter.samplers.SampleEvent;
import org.apache.jmeter.samplers.SampleSaveConfiguration;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class PerfMonCollector
        extends CorrectedResultCollector
        implements Runnable, PerfMonSampleGenerator {

    private static boolean autoGenerateFiles = false;
    public static final long MEGABYTE = 1024L * 1024L;
    private static final String PERFMON = "PerfMon";
    private static final Logger log = LoggingManager.getLoggerForClass();
    public static final String DATA_PROPERTY = "metricConnections";
    private int interval;
    private Thread workerThread = null;
    private Map connectors = new ConcurrentHashMap();
    private HashMap oldValues = new HashMap();
    private static String autoFileBaseName = null;
    private static int counter = 0;
    private LoadosophiaUploadingNotifier perfMonNotifier = LoadosophiaUploadingNotifier.getInstance();
    private static String workerHost = null;

    static {
        autoGenerateFiles = (JMeterUtils.getPropDefault("forcePerfmonFile", "false")).trim().equalsIgnoreCase("true");
    }

    private static synchronized String getAutoFileName() {
        String ret = "";
        counter++;
        if (autoFileBaseName == null) {
            Calendar now = Calendar.getInstance();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HHmmss");
            autoFileBaseName = "perfMon_" + formatter.format(now.getTime());
        }
        ret = ret + autoFileBaseName;
        if (counter > 1) {
            ret = ret + "_" + counter;
        }
        ret = ret + ".csv";

        return ret;
    }

    public PerfMonCollector() {
        // TODO: document it
        interval = JMeterUtils.getPropDefault("jmeterPlugin.perfmon.interval", 1000);
    }

    public void setData(CollectionProperty rows) {
        setProperty(rows);
    }

    public JMeterProperty getMetricSettings() {
        return getProperty(DATA_PROPERTY);
    }

    public void sampleOccurred(SampleEvent event) {
        // just dropping regular test samples
    }

    @Override
    public synchronized void run() {
        while (true) {
            processConnectors();
            try {
                this.wait(interval);
            } catch (InterruptedException ex) {
                log.debug("Monitoring thread was interrupted", ex);
                break;
            }
        }
    }

    //ensure we start only on one host (if multiple slaves)
    private synchronized static boolean isWorkingHost(String host) {
        if (workerHost == null) {
            workerHost = host;
            return true;
        } else {
            return host.equals(workerHost);
        }
    }

    @Override
    public void testStarted(String host) {

        if (!isWorkingHost(host)) {
            return;
        }

        //ensure the data will be saved
        if (getProperty(FILENAME) == null || getProperty(FILENAME).getStringValue().trim().length() == 0) {
            if (autoGenerateFiles) {
                setupSaving(getAutoFileName());
            } else {
                try {
                    File tmpFile = File.createTempFile("perfmon_", ".jtl");
                    tmpFile.delete(); // required to have CSV header
                    setupSaving(tmpFile.getAbsolutePath());
                } catch (IOException ex) {
                    log.info("PerfMon metrics will not be recorded! Please run the test with -JforcePerfmonFile=true", ex);
                }
            }
        }

        log.debug("PerfMon metrics will be stored in " + getPropertyAsString(FILENAME));
        if (!getSaveConfig().saveAsXml() && getSaveConfig().saveFieldNames()) {
            perfMonNotifier.addFile(getPropertyAsString(FILENAME));
        } else {
            log.warn("Perfmon file saving setting is not CSV with header line, cannot upload it to Loadosophia.org: " + getPropertyAsString(FILENAME));
        }
        initiateConnectors();

        workerThread = new Thread(this);
        workerThread.start();

        super.testStarted(host);
    }

    private void setupSaving(String fileName) {
        SampleSaveConfiguration config = getSaveConfig();
        JMeterPluginsUtils.doBestCSVSetup(config);
        setSaveConfig(config);
        setFilename(fileName);
        log.info("PerfMon metrics will be stored in " + new File(fileName).getAbsolutePath());
    }

    @Override
    public void testEnded(String host) {
        if (workerThread == null) {
            return;
        }
        workerHost = null;
        workerThread.interrupt();
        shutdownConnectors();

        //reset autoFileName for next test run
        autoFileBaseName = null;
        counter = 0;
        super.testEnded(host);
    }

    private void initiateConnectors() {
        oldValues.clear();
        JMeterProperty prop = getMetricSettings();
        connectors.clear();
        if (!(prop instanceof CollectionProperty)) {
            log.warn("Got unexpected property: " + prop);
            return;
        }
        CollectionProperty rows = (CollectionProperty) prop;

        for (int i = 0; i < rows.size(); i++) {
            ArrayList row = (ArrayList) rows.get(i).getObjectValue();
            String host = ((JMeterProperty) row.get(0)).getStringValue();
            int port = ((JMeterProperty) row.get(1)).getIntValue();
            String metric = ((JMeterProperty) row.get(2)).getStringValue();
            String params = ((JMeterProperty) row.get(3)).getStringValue();
            initiateConnector(host, port, i, metric, params);
        }

        Iterator it = connectors.keySet().iterator();
        while (it.hasNext()) {
            Object key = it.next();
            try {
                connectors.get(key).connect();
            } catch (IOException ex) {
                log.error("Error connecting to agent", ex);
                connectors.put(key, new UnavailableAgentConnector(ex));
            }
        }
    }

    private void initiateConnector(String host, int port, int index, String metric, String params) {
        InetSocketAddress addr = new InetSocketAddress(host, port);
        String stringKey = addr.toString() + "#" + index;

        // handle label parameter
        MetricParams paramsParsed = MetricParams.createFromString(params);
        String label;
        if (paramsParsed.getLabel().isEmpty()) {
            label = host + " " + metric;
            if (params != null && !params.isEmpty()) {
                label = label + " " + params;
            }
        } else {
            label = host + " " + metric + " " + paramsParsed.getLabel();

            String[] tokens = params.split("(? it = connectors.keySet().iterator();
        while (it.hasNext()) {
            Object key = it.next();
            final PerfMonAgentConnector conn = connectors.get(key);
            log.debug("Shutting down " + conn.toString());
            //Fix ConcurrentModificationException if more than one host
            //connectors.remove(key);
            it.remove();
            conn.disconnect();
        }
    }

    private void processConnectors() {
        Iterator it = connectors.keySet().iterator();
        while (it.hasNext()) {
            Object key = it.next();
            PerfMonAgentConnector connector = connectors.get(key);
            try {
                connector.generateSamples(this);
            } catch (IOException e) {
                log.error(e.getMessage());
                connectors.put(key, new UnavailableAgentConnector(e));
            }
        }
    }

    //need floating point precision for memory and cpu
    @Override
    public void generateSample(double value, String label) {
        PerfMonSampleResult res = new PerfMonSampleResult();
        res.setSampleLabel(label);
        res.setValue(value);
        res.setSuccessful(true);
        SampleEvent e = new SampleEvent(res, PERFMON);
        super.sampleOccurred(e);
    }

    @Override
    public void generateErrorSample(String label, String errorMsg) {
        PerfMonSampleResult res = new PerfMonSampleResult();
        res.setSampleLabel(label);
        res.setValue(-1L);
        res.setResponseMessage(errorMsg);
        res.setSuccessful(false);
        SampleEvent e = new SampleEvent(res, PERFMON);
        super.sampleOccurred(e);
        log.error("Perfmon plugin error: " + errorMsg);
    }

    @Override
    public void generate2Samples(long[] values, String label1, String label2) {
        generate2Samples(values, label1, label2, 1d);
    }

    //float precision required for net io
    @Override
    public void generate2Samples(long[] values, String label1, String label2, double dividingFactor) {
        if (oldValues.containsKey(label1) && oldValues.containsKey(label2)) {
            generateSample(((double) (values[0] - oldValues.get(label1).longValue())) / dividingFactor, label1);
            generateSample(((double) (values[1] - oldValues.get(label2).longValue())) / dividingFactor, label2);
        }
        oldValues.put(label1, new Long(values[0]));
        oldValues.put(label2, new Long(values[1]));
    }
}