org.apache.jmeter.visualizers.StatGraphVisualizer Maven / Gradle / Ivy
/*
* 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;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.action.SaveGraphics;
import org.apache.jmeter.gui.util.FileDialoger;
import org.apache.jmeter.gui.util.FilePanel;
import org.apache.jmeter.gui.util.HeaderAsPropertyRendererWrapper;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.save.CSVSaveService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.gui.GuiUtils;
import org.apache.jorphan.gui.JLabeledTextField;
import org.apache.jorphan.gui.NumberRenderer;
import org.apache.jorphan.gui.ObjectTableModel;
import org.apache.jorphan.gui.ObjectTableSorter;
import org.apache.jorphan.gui.RateRenderer;
import org.apache.jorphan.gui.RendererUtils;
import org.apache.jorphan.reflect.Functor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Aggregrate Table-Based Reporting Visualizer for JMeter. Props to the people
* who've done the other visualizers ahead of me (Stefano Mazzocchi), who I
* borrowed code from to start me off (and much code may still exist). Thank
* you!
*
*/
public class StatGraphVisualizer extends AbstractVisualizer implements Clearable, ActionListener {
private static final long serialVersionUID = 242L;
private static final String PCT1_LABEL = JMeterUtils.getPropDefault("aggregate_rpt_pct1", "90");
private static final String PCT2_LABEL = JMeterUtils.getPropDefault("aggregate_rpt_pct2", "95");
private static final String PCT3_LABEL = JMeterUtils.getPropDefault("aggregate_rpt_pct3", "99");
private static final Float PCT1_VALUE = new Float(Float.parseFloat(PCT1_LABEL)/100);
private static final Float PCT2_VALUE = new Float(Float.parseFloat(PCT2_LABEL)/100);
private static final Float PCT3_VALUE = new Float(Float.parseFloat(PCT3_LABEL)/100);
private static final Logger log = LoggerFactory.getLogger(StatGraphVisualizer.class);
private static final String[] COLUMNS = {
"sampler_label", //$NON-NLS-1$
"aggregate_report_count", //$NON-NLS-1$
"average", //$NON-NLS-1$
"aggregate_report_median", //$NON-NLS-1$
"aggregate_report_xx_pct1_line", //$NON-NLS-1$
"aggregate_report_xx_pct2_line", //$NON-NLS-1$
"aggregate_report_xx_pct3_line", //$NON-NLS-1$
"aggregate_report_min", //$NON-NLS-1$
"aggregate_report_max", //$NON-NLS-1$
"aggregate_report_error%", //$NON-NLS-1$
"aggregate_report_rate", //$NON-NLS-1$
"aggregate_report_bandwidth", //$NON-NLS-1$
"aggregate_report_sent_bytes_per_sec" //$NON-NLS-1$
};
private static final String[] GRAPH_COLUMNS = {"average",//$NON-NLS-1$
"aggregate_report_median", //$NON-NLS-1$
"aggregate_report_xx_pct1_line", //$NON-NLS-1$
"aggregate_report_xx_pct2_line", //$NON-NLS-1$
"aggregate_report_xx_pct3_line", //$NON-NLS-1$
"aggregate_report_min", //$NON-NLS-1$
"aggregate_report_max"}; //$NON-NLS-1$
private static final String TOTAL_ROW_LABEL =
JMeterUtils.getResString("aggregate_report_total_label"); //$NON-NLS-1$
private static final Font FONT_DEFAULT = UIManager.getDefaults().getFont("TextField.font"); //$NON-NLS-1$
private static final Font FONT_SMALL = new Font("SansSerif", Font.PLAIN, (int) Math.round(FONT_DEFAULT.getSize() * 0.8)); //$NON-NLS-1$
private static final int REFRESH_PERIOD = JMeterUtils.getPropDefault("jmeter.gui.refresh_period", 500);
private JTable myJTable;
private JScrollPane myScrollPane;
private transient ObjectTableModel model;
/**
* Lock used to protect tableRows update + model update
*/
private final transient Object lock = new Object();
private final Map tableRows = new ConcurrentHashMap<>();
private AxisGraph graphPanel = null;
private JPanel settingsPane = null;
private JSplitPane spane = null;
private JTabbedPane tabbedGraph = new JTabbedPane(SwingConstants.TOP);
private JButton displayButton =
new JButton(JMeterUtils.getResString("aggregate_graph_display")); //$NON-NLS-1$
private JButton saveGraph =
new JButton(JMeterUtils.getResString("aggregate_graph_save")); //$NON-NLS-1$
private JButton saveTable =
new JButton(JMeterUtils.getResString("aggregate_graph_save_table")); //$NON-NLS-1$
private JButton chooseForeColor =
new JButton(JMeterUtils.getResString("aggregate_graph_choose_foreground_color")); //$NON-NLS-1$
private JButton syncWithName =
new JButton(JMeterUtils.getResString("aggregate_graph_sync_with_name")); //$NON-NLS-1$
private JCheckBox saveHeaders = // should header be saved with the data?
new JCheckBox(JMeterUtils.getResString("aggregate_graph_save_table_header")); //$NON-NLS-1$
private JLabeledTextField graphTitle =
new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_user_title")); //$NON-NLS-1$
private JLabeledTextField maxLengthXAxisLabel =
new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_max_length_xaxis_label"), 8);//$NON-NLS-1$
private JLabeledTextField maxValueYAxisLabel =
new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_yaxis_max_value"), 8);//$NON-NLS-1$
/**
* checkbox for use dynamic graph size
*/
private JCheckBox dynamicGraphSize = new JCheckBox(JMeterUtils.getResString("aggregate_graph_dynamic_size")); // $NON-NLS-1$
private JLabeledTextField graphWidth =
new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_width"), 6); //$NON-NLS-1$
private JLabeledTextField graphHeight =
new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_height"), 6); //$NON-NLS-1$
private String yAxisLabel = JMeterUtils.getResString("aggregate_graph_response_time");//$NON-NLS-1$
private String yAxisTitle = JMeterUtils.getResString("aggregate_graph_ms"); //$NON-NLS-1$
private boolean saveGraphToFile = false;
private int defaultWidth = 400;
private int defaultHeight = 300;
private JComboBox columnsList = new JComboBox<>(GRAPH_COLUMNS);
private List eltList = new ArrayList<>();
private JCheckBox columnSelection = new JCheckBox(JMeterUtils.getResString("aggregate_graph_column_selection"), false); //$NON-NLS-1$
private JTextField columnMatchLabel = new JTextField();
private JButton applyFilterBtn = new JButton(JMeterUtils.getResString("graph_apply_filter")); // $NON-NLS-1$
private JCheckBox caseChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_case"), false); // $NON-NLS-1$
private JCheckBox regexpChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_regexp"), true); // $NON-NLS-1$
private JComboBox titleFontNameList = new JComboBox<>(keys(StatGraphProperties.getFontNameMap()));
private JComboBox titleFontSizeList = new JComboBox<>(StatGraphProperties.getFontSize());
private JComboBox titleFontStyleList = new JComboBox<>(keys(StatGraphProperties.getFontStyleMap()));
private JComboBox valueFontNameList = new JComboBox<>(keys(StatGraphProperties.getFontNameMap()));
private JComboBox valueFontSizeList = new JComboBox<>(StatGraphProperties.getFontSize());
private JComboBox valueFontStyleList = new JComboBox<>(keys(StatGraphProperties.getFontStyleMap()));
private JComboBox fontNameList = new JComboBox<>(keys(StatGraphProperties.getFontNameMap()));
private JComboBox fontSizeList = new JComboBox<>(StatGraphProperties.getFontSize());
private JComboBox fontStyleList = new JComboBox<>(keys(StatGraphProperties.getFontStyleMap()));
private JComboBox legendPlacementList = new JComboBox<>(keys(StatGraphProperties.getPlacementNameMap()));
// Default checked
private JCheckBox drawOutlinesBar = new JCheckBox(JMeterUtils.getResString("aggregate_graph_draw_outlines"), true); // $NON-NLS-1$
// Default checked
private JCheckBox numberShowGrouping = new JCheckBox(JMeterUtils.getResString("aggregate_graph_number_grouping"), true); // $NON-NLS-1$
// Default checked
private JCheckBox valueLabelsVertical = new JCheckBox(JMeterUtils.getResString("aggregate_graph_value_labels_vertical"), true); // $NON-NLS-1$
private Color colorBarGraph = Color.YELLOW;
private Color colorForeGraph = Color.BLACK;
private int nbColToGraph = 1;
private Pattern pattern = null;
private Deque newRows = new ConcurrentLinkedDeque<>();
public StatGraphVisualizer() {
super();
model = createObjectTableModel();
final Color red = new Color(202, 0, 0);
final Color blue = new Color(49, 49, 181);
final Color green = new Color(42, 121, 42);
final Color yellow = new Color(242, 226, 8);
final Color purple = new Color(202, 10, 232);
eltList.add(new BarGraph(JMeterUtils.getResString("average"), true,
red));
eltList.add(new BarGraph(
JMeterUtils.getResString("aggregate_report_median"), false,
blue));
eltList.add(
new BarGraph(
MessageFormat.format(
JMeterUtils.getResString(
"aggregate_report_xx_pct1_line"),
PCT1_LABEL),
false, green));
eltList.add(
new BarGraph(
MessageFormat.format(
JMeterUtils.getResString(
"aggregate_report_xx_pct2_line"),
PCT2_LABEL),
false, yellow));
eltList.add(
new BarGraph(
MessageFormat.format(
JMeterUtils.getResString(
"aggregate_report_xx_pct3_line"),
PCT3_LABEL),
false, purple));
eltList.add(
new BarGraph(JMeterUtils.getResString("aggregate_report_min"),
false, Color.LIGHT_GRAY));
eltList.add(
new BarGraph(JMeterUtils.getResString("aggregate_report_max"),
false, Color.DARK_GRAY));
clearData();
init();
}
static final Object[][] getColumnsMsgParameters() {
return new Object[][] { null,
null,
null,
null,
new Object[]{PCT1_LABEL},
new Object[]{PCT2_LABEL},
new Object[]{PCT3_LABEL},
null,
null,
null,
null,
null,
null};
}
private String[] keys(Map map) {
return map.keySet().toArray(ArrayUtils.EMPTY_STRING_ARRAY);
}
/**
* @return array of String containing column names
*/
public static final String[] getColumns() {
String[] columns = new String[COLUMNS.length];
System.arraycopy(COLUMNS, 0, columns, 0, COLUMNS.length);
return columns;
}
/**
* Creates that Table model
* @return ObjectTableModel
*/
static ObjectTableModel createObjectTableModel() {
return new ObjectTableModel(getLabels(COLUMNS),
SamplingStatCalculator.class,
new Functor[] {
new Functor("getLabel"), //$NON-NLS-1$
new Functor("getCount"), //$NON-NLS-1$
new Functor("getMeanAsNumber"), //$NON-NLS-1$
new Functor("getMedian"), //$NON-NLS-1$
new Functor("getPercentPoint", //$NON-NLS-1$
new Object[] { PCT1_VALUE }),
new Functor("getPercentPoint", //$NON-NLS-1$
new Object[] { PCT2_VALUE }),
new Functor("getPercentPoint", //$NON-NLS-1$
new Object[] { PCT3_VALUE }),
new Functor("getMin"), //$NON-NLS-1$
new Functor("getMax"), //$NON-NLS-1$
new Functor("getErrorPercentage"), //$NON-NLS-1$
new Functor("getRate"), //$NON-NLS-1$
new Functor("getKBPerSecond"), //$NON-NLS-1$
new Functor("getSentKBPerSecond") }, //$NON-NLS-1$
new Functor[] { null, null, null, null, null, null, null, null, null, null, null, null, null },
new Class[] { String.class, Long.class, Long.class, Long.class, Long.class,
Long.class, Long.class, Long.class, Long.class, Double.class,
Double.class, Double.class, Double.class});
}
// Column formats
static final Format[] getFormatters() {
return new Format[]{
null, // Label
null, // count
null, // Mean
null, // median
null, // 90%
null, // 95%
null, // 99%
null, // Min
null, // Max
new DecimalFormat("#0.000%"), // Error %age //$NON-NLS-1$
new DecimalFormat("#.00000"), // Throughput //$NON-NLS-1$
new DecimalFormat("#0.00"), // Throughput //$NON-NLS-1$
new DecimalFormat("#0.00") // pageSize //$NON-NLS-1$
};
}
// Column renderers
static final TableCellRenderer[] getRenderers() {
return new TableCellRenderer[]{
null, // Label
null, // count
null, // Mean
null, // median
null, // 90%
null, // 95%
null, // 99%
null, // Min
null, // Max
new NumberRenderer("#0.00%"), // Error %age //$NON-NLS-1$
new RateRenderer("#.0"), // Throughput //$NON-NLS-1$
new NumberRenderer("#0.00"), // Received bytes per sec //$NON-NLS-1$
new NumberRenderer("#0.00"), // Sent bytes per sec //$NON-NLS-1$
};
}
/**
*
* @param keys I18N keys
* @return labels
*/
static String[] getLabels(String[] keys) {
String[] labels = new String[keys.length];
for (int i = 0; i < labels.length; i++) {
labels[i]=MessageFormat.format(JMeterUtils.getResString(keys[i]), getColumnsMsgParameters()[i]);
}
return labels;
}
/**
* We use this method to get the data, since we are using
* ObjectTableModel, so the calling getDataVector doesn't
* work as expected.
* @param model {@link ObjectTableModel}
* @param formats Array of {@link Format} array can contain null formatters in this case value is added as is
* @return the data from the model
*/
public static List> getAllTableData(ObjectTableModel model, Format[] formats) {
List> data = new ArrayList<>();
if (model.getRowCount() > 0) {
for (int rw=0; rw < model.getRowCount(); rw++) {
int cols = model.getColumnCount();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy