package com.github.johrstrom.listener.updater; import com.github.johrstrom.collector.JMeterCollectorRegistry; import com.github.johrstrom.listener.ListenerCollectorConfig; import org.apache.jmeter.assertions.AssertionResult; import org.apache.jmeter.samplers.SampleEvent; import org.apache.jmeter.threads.JMeterContextService; import org.apache.jmeter.threads.JMeterVariables; import java.util.HashMap; import java.util.Map; /** * The Updater family of classes are meant to update the actual Collectors given the configuration. The main problem * it tries to solve is tying a Prometheus Collector (with a type like 'Historgram', labels, etc.) to the JMeter data * that collector is measuring. * * Note: This class assumes that the Collector object passed into the constructor is valid. I.e., it is not null and * registered. Of course, being null has much more serious consequences. * * @author Jeff Ohrstrom * */ public abstract class AbstractUpdater { public static String NULL = "null"; protected ListenerCollectorConfig config; protected static final JMeterCollectorRegistry registry = JMeterCollectorRegistry.getInstance(); // helper lookup table for sample variables, so we don't loop over arrays every update. private Map<String,Integer> varIndexLookup; /** * All subclasses should have this and only this constructor signature. * * @param cfg the configuration of the collector */ public AbstractUpdater(ListenerCollectorConfig cfg) { //this.collector = c; this.config = cfg; this.buildVarLookup(); } /** * Updates the collector it was instantiated with with the given event e. * * @param e */ public abstract void update(SampleEvent e); public static class AssertionContext { public AssertionResult assertion; public SampleEvent event; public AssertionContext(AssertionResult a, SampleEvent e) { this.assertion = a; this.event = e; } } /** * Helper function to extract the label values from the Sample Event. Values * depend on how the Updater was configured. * * @param event * @return the label values. */ protected String[] labelValues(SampleEvent event) { String[] labels = config.getLabels(); String[] values = new String[labels.length]; JMeterVariables vars = JMeterContextService.getContext().getVariables(); for(int i = 0; i < labels.length; i++) { String name = labels[i]; String value = null; // reserved keyword for the sampler's label (the name) if(name.equalsIgnoreCase("label")) { value = event.getResult().getSampleLabel(); } else if(name.equalsIgnoreCase("code")) { // code also reserved value = event.getResult().getResponseCode(); // try to find it as a plain'ol variable. } else if (this.varIndexLookup.get(name) != null){ int idx = this.varIndexLookup.get(name); value = event.getVarValue(idx); // lastly look in sample_variables }else if (vars != null){ value = vars.get(name); } values[i] = (value == null || value.isEmpty()) ? NULL : value; } return values; } protected String[] labelValues(AssertionContext ctx) { String[] labels = config.getLabels(); String[] values = new String[labels.length]; JMeterVariables vars = JMeterContextService.getContext().getVariables(); for(int i = 0; i < labels.length; i++) { String name = labels[i]; String value = null; if(name.equalsIgnoreCase("label")) { value = ctx.assertion.getName(); // try to find it as a plain'ol variable. } else if (this.varIndexLookup.get(name) != null){ int idx = this.varIndexLookup.get(name); value = ctx.event.getVarValue(idx); // lastly look in sample_variables }else if (vars != null){ value = vars.get(name); } values[i] = (value == null || value.isEmpty()) ? NULL : value; } return values; } private void buildVarLookup() { this.varIndexLookup = new HashMap<String,Integer>(); for(int i = 0; i < SampleEvent.getVarCount(); i++) { String name = SampleEvent.getVarName(i); if(inLabels(name)) { this.varIndexLookup.put(name, i); } } } private boolean inLabels(String searchFor) { String[] labels = config.getLabels(); for(int i = 0; i < labels.length; i++) { if(labels[i].equalsIgnoreCase(searchFor)) { return true; } } return false; } }