package com.yahoo.dtf.actions.stats;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import com.yahoo.dtf.actions.Action;
import com.yahoo.dtf.exception.ActionException;
import com.yahoo.dtf.exception.DTFException;
import com.yahoo.dtf.exception.ParseException;
import com.yahoo.dtf.query.Cursor;
import com.yahoo.dtf.recorder.Event;
import com.yahoo.dtf.stats.GenCalcStats;
/**
* @dtf.tag stats
*
* @dtf.since 1.0
* @dtf.author Rodney Gomes
*
* @dtf.tag.desc The stats tag is capable of calculating some statistical
* analysis over the events identified in the result set of the
* attribute <code>cursor</code>. The calculated statistics are
* thrown as events to be used by the testcase as it best sees fit.
*
* @dtf.event cursor.fieldname
* @dtf.event.attr avg_val
* @dtf.event.attr.desc Each field of the specified event that is of a number
* type will have its average value calculated and recorded
* in this attribute.
*
* @dtf.event cursor.fieldname
* @dtf.event.attr tot_val
* @dtf.event.attr.desc Each field of the specified event that is of a number
* type will have its total value calculated and recorded
* in this attribute.
*
* @dtf.event cursor.fieldname
* @dtf.event.attr min_val
* @dtf.event.attr.desc Each field of the specified event that is of a number
* type will have its minimum value calculated and recorded
* in this attribute.
*
* @dtf.event cursor.fieldname
* @dtf.event.attr max_val
* @dtf.event.attr.desc Each field of the specified event that is of a number
* type will have its maximum value calculated and recorded
* in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr min_dur
* @dtf.event.attr.desc The minimum duration of any event in milliseconds
* recorded will be saved in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr max_dur
* @dtf.event.attr.desc The maximum duration of any event in milliseconds
* recorded will be saved in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr avg_dur
* @dtf.event.attr.desc The average duration of any event in milliseconds
* recorded will be saved in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr tot_dur
* @dtf.event.attr.desc The total duration of any event in milliseconds recorded
* will be saved in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr csv_dur
* @dtf.event.attr.desc This property contains a CSV contents for all of the
* latencies for the events sampled including the number
* of times it occurred like so:
*
* <pre>
* 23,455
* 50,412
* 100,20
* </pre>
*
* Where the first column is the latency (duration in ms)
* and the second column is the number of events that had
* this duration. With this you can easily generate a
* graph using gnuplot, with the following gnuplot script
* and having outputted the property into a file called
* "latency.data":
*
* <pre>
* set title "Latency graph of some sample data"
*
* set xdata time
* set timefmt "%s"
*
* set terminal png
* set output "graph.png"
*
* set xlabel 'Number of events'
* set ylabel 'Duration (ms)'
*
* plot 'latency.data' using 1:2 with linespoints title 'latencies'
* </pre>
*
* You can also easily import the CSV file into your
* favorite spreadsheet software and generate any graphs
* you'd like.
*
* @dtf.event cursor
* @dtf.event.attr tot_occ
* @dtf.event.attr.desc The total occurrences of this event will be saved in
* this attribute.
*
* @dtf.event cursor
* @dtf.event.attr avg_occ
* @dtf.event.attr.desc The number of occurrences of this event per second over
* the duration of all events is recorded in this attribute.
*
* @dtf.event cursor
* @dtf.event.attr MONITOR_states
* @dtf.event.attr.desc Generated only when you have used the monitor attribute
* and will contain a comma separated list of the various
* states that specific field had. the MONITOR name is
* replaced with the name of the fields you were monitoring.
*
* @dtf.event cursor.FIELD_VALUE
* @dtf.event.attr max_int
* @dtf.event.attr.desc this event will be generated for every FIELD in the
* monitors attribute and it will have the format
* FIELD_VALUE.max_int being that the VALUE is one of the
* various values that field had. So for each VALUE that
* the FIELD can assume there will be another event
* generated containing the maximum interval during which
* that VALUE occurred without any changes.
*
* @dtf.event cursor.FIELD_VALUE
* @dtf.event.attr min_int
* @dtf.event.attr.desc this event will be generated for every FIELD in the
* monitors attribute and it will have the format
* FIELD_VALUE.min_int being that the VALUE is one of the
* various values that FIELD had. So for each VALUE that
* the FIELD can assume there will be another event
* generated containing the minimum interval during which
* that VALUE occurred without any changes.
*
* @dtf.event cursor.FIELD_VALUE
* @dtf.event.attr tot_int
* @dtf.event.attr.desc this event will be generated for every FIELD in the
* monitors attribute and it will have the format
* FIELD_VALUE.avg_int being that the VALUE is one of the
* various values that FIELD had. So for each VALUE that
* the FIELD can assume there will be another event
* generated containing the total amount of time that the
* specified field was in the VALUE state.
*
* @dtf.event cursor.FIELD_VALUE
* @dtf.event.attr avg_int
* @dtf.event.attr.desc this event will be generated for every FIELD in the
* monitors attribute and it will have the format
* FIELD_VALUE.avg_int being that the VALUE is one of the
* various values that FIELD had. So for each VALUE that
* the FIELD can assume there will be another event
* generated containing the average amount of time that the
* specified field was in the VALUE state.
*
* @dtf.tag.example
* <script>
* <query uri="storage://OUTPUT/${recorder.filename}"
* type="${recorder.type}"
* event="dtf.event"
* cursor="cursor1">
* <where>
* <eq op1="runid" op2="${runid}"/>
* </where>
* </query>
*
* <record type="object" uri="property://txtrecorder">
* <stats cursor="cursor1" event="stats"/>
* </record>
* </script>
*
* @dtf.tag.example
* <script>
* <query uri="storage://OUTPUT/${recorder.filename}"
* type="${recorder.type}"
* event="dtf.event"
* cursor="cursor1"/>
*
* <record type="object" uri="property://txtrecorder">
* <stats cursor="cursor1" event="stats"/>
* </record>
* </script>
*/
public class Stats extends Action {
/**
* @dtf.attr cursor
* @dtf.attr.desc Identifies the cursor to use when calculating statistics.
*/
private String cursor = null;
/**
* @dtf.attr event
* @dtf.attr.desc Specifies the event name to attach the statistics that
* are calculated.
*/
private String event = null;
/**
* @dtf.attr monitor
* @dtf.attr.desc specify a field to monitor the value changes and generate
* statistic about the amount of time spent in each state.
* When you use this attribute there are some new events that
* are generated, they are mentioned above.
*
*/
private String monitor = null;
public Stats() { }
public void execute() throws DTFException {
if ( getLogger().isDebugEnabled() )
getLogger().debug("Starting to analyze " + this);
Cursor cursor = retCursor(getCursor());
if ( cursor == null )
throw new DTFException("Cursor [" + getCursor() + "] does not exist");
GenCalcStats calcStats = new GenCalcStats(getMonitor());
LinkedHashMap<String, String> props =
calcStats.calcStats(cursor);
Event event = new Event(getEvent());
Iterator<Entry<String,String>> entries = props.entrySet().iterator();
while ( entries.hasNext() ) {
Entry<String, String> entry = entries.next();
event.addAttribute(entry.getKey(), entry.getValue());
}
getRecorder().record(event);
if ( getLogger().isDebugEnabled() )
getLogger().debug("Finished analyzing " + this);
}
public String getCursor() throws ParseException { return replaceProperties(cursor); }
public void setCursor(String cursor) { this.cursor = cursor; }
public String getEvent() throws ActionException, ParseException { return replaceProperties(event); }
public void setEvent(String event) { this.event = event; }
public String getMonitor() throws ParseException { return replaceProperties(monitor); }
public void setMonitor(String monitor) { this.monitor = monitor; }
}