3.7. Reporting

In this section we will reveal as much details about reporting facilities as possible. First we describe the architecture and then we discuss an approach to develop new reporters, destinations and accumulators.

Bear in mind that the reporting itself can have a significant influence on the system being measured. Be careful when extending this part of PerfCake.

3.7.1. Reporting Architecture

Figure 3.3. Reporting Architecture


The SenderTask instance measures the time needed to send a message by using a MessageSender. It creates a MeasurementUnit which is the basic unit carrying a single measurement. The information stored in the MeasurementUnit are mainly time values of the events in the process of sending a request.

All the MeasurementUnits are immediately reported to the ReportManager which in turn reports this unit to all its Reporters. A Reporter is supposed to process the value as it needs. The Reporter typically accumulates (by utilizing Accumulators) the values for later because not all MesurementUnits are reported to the output (from practical reasons like the number of records and performance impacts).

When it is the right time for the Reporter to report the Measurement to the output (as specified in the scenario configuration by using the reporting Period), it must publish the results. It is up to the Reporter to make sure the results are reported at the right moments.

The Reporter is supposed to create a Measurement object carrying the accumulated results and pass it to all its Destinations.

The Destination is a representation of the place where the results should be reported. It can be a terminal console, a comma-separeted values (CSV) file, a chart or any custom result repository.

3.7.2. Reporters

A Reporter takes multiple MeasurementUnits and combines them into a single Measurement. The core method is report() that is called each time a new MeasurementUnit is ready.

The Reporter should not report anything unless it has been started with the start() method. If it is properly started, it should regularly report to all registered Destinations depending on the configured reporting Periods.

The Reporter can assume that RunInfo has been set before its start() method was called for the first time.

It is the pure responsibility of the Reporter to publish Measurement results to the Destinations in the configured Periods. All PeriodTypes must be supported.

For easier development, it is advised to inherit from the AbstractReporter class which provides some common functionality including proper results publishing. One should directly implement this interface only when there is a serious reason. The Reporter must be thread safe as it is heavily used from multiple threads at the same time.

All the values reported in the Measurement should be accompanied by a unit. This is accomplished by providing the Quantity type.

A nice example of a very simple Reporter is IterationsPerSecondReporter.

The AbstractReporter can also automatically accumulate individual MeasurementUnits. It is just important that its getAccumulator() method returns the correct Accumulator for the given MeasurementUnit results map key. More on accumulator can be found in Section 3.7.4, “Accumulators”.

3.7.3. Destinations

A Destination is a channel to which performance measurement results can be reported. The Destination is registered with a Reporter and is completely controlled by the Reporter. The only responsibility of the Destination is to open a reporting channel, report Measurements, and close the reporting channel.

It is the role of the Measurement object to provide all the information to be reported (including value types, names, units and custom labels).

The core method of the Destination is report() and it needs to be thread-safe as it is used by multiple threads at the same time.

One of the simplest Destinations is ConsoleDestination.

3.7.4. Accumulators

An Accumulator is a tool for reporters to accumulate multiple values from MeasurementUnits into a single Mesurement.

The Accumulator must be thread-safe as it is called from multiple threads at the same time.

It has three simple methods to reset the Accumulator to the default/empty state (reset()), to accumulate a new value (add()), and to obtain the current result (getResult()).

The Accumulator is not obliged to remember all the added values as long as it is capable of providing the correct results. E.g. while counting an average, it is sufficient to store the sum and the count of added values. In some cases, it might be necessary to store them all (e.g. harmonic mean).