BuzzSaw - Reports

Introduction

As well as being able to execute adhoc queries as required an administrator often needs to be able to run regular reports which analyse and summarise particular types of events which have occurred within a given period. These might be hourly, daily, weekly or monthly, all of which can be handled using the BuzzSaw::Reporter module.

The reporter module can be run in one of two modes. It can either be run at specific times (e.g. fron cron) and just execute reports of a certain required type (e.g. hourly). Or alternatively it can be run for all types and it will generate all reports which have not been run within the specified period (e.g. if a weekly report was last generated more than 7 days ago). The last report generation time is recorded so either mode will work.

Typically reports will be generated using the buzzsaw command-line tool like this:

buzzsaw report --all hourly

For full details on the supported options view the help page for the command like this:

buzzsaw help report

One particularly useful flag is --debug which will display a lot more information on stdout.

The report command is configured via the /etc/buzzsaw/reporter.yaml file, that is typically managed using the LCFG buzzsaw component but could also be hand edited for one-off requirements (e.g. for testing new reports).

Writing a new Report Module

First Steps

If you need to generate a report based on tags of a specific name then it is possible to just do this using the generic BuzzSaw::Report module. That is often the best starting point when you are needing to develop a completely new report. You can do something like the following as a simple script:

#!/usr/bin/perl                                                                 
use strict;
use warnings;

use BuzzSaw::Report;

my $report = BuzzSaw::Report->new( name => 'FooBar' );

$report->add_tmpldir("$ENV{HOME}/buzzsaw_templates");

$report->generate();

This will find all events which occurred during the previous day (the default date range) which were tagged as the lower-cased version of the report name (e.g. foobar). A report will be generated (and printed to stdout) using a template which has a name based on the lower-cased version of the report name with a .tt suffix (e.g. foobar.tt). That template will need to exist within one of the default template directories (/usr/share/buzzsaw/templates/reports or /usr/share/buzzsaw/templates) or your own buzzsaw_templates directory which you will need to create if required. Any template you put into your buzzsaw_templates directory will override a template of the same name in any of the default template directories. That makes it simple to test new developments to existing templates.

Writing a template

The template processing stage is carried out by the process_template method. This creates a new Template Toolkit object and passes in various data to the template processor. The following parameters are available in your template:

events
A reference to an array of events which matched the query.
results
A reference to a hash of data returned by the process_events method (empty by default).
params
Parameters set when the report object was created. Currently this is the values for the start, end and tags attributes.

A simple template might look like this:

% FOREACH e IN events -%]
[% e.hostname %],[% e.logtime %],[% e.message %]
[% END -%]

That will just print out a list of the comma-separated values of the hostname, timestamps and messages for each event which matched during the specified period.

The Template Toolkit is a fairly powerful tool for mangling output so it is well worth reading the Template::Manual.

Sending Email

By default the report will be printed to stdout. That is fine when testing new code but not much use if you need to generate reports on a regular basis. The alternative is to get the report sent by email. This is done trivially by setting a value for the email_to attribute. For example:

my $report = BuzzSaw::Report->new( name     => 'FooBar',
                                   email_to => 'fred@example.org' );

More typically this is done by setting parameters in the reporter configuration file. That is usually done using the buzzsaw component resource report_email_to_tag for the particular report tag resource.

You can send email to as many addresses as you like by using a reference to an array instead of a string. The first address in the list goes into the To field and the rest are added in the Cc field. You can also control the From address using the email_from attribute and the Subject using the email_subject attribute.

Creating a module

Once you have created a new report the preferred deployment method is to create a report module and get it and the template file added to the BuzzSaw distribution.

A simple module would look like this:

package BuzzSaw::Report::FooBar;

use Moose;

extends 'BuzzSaw::Report';

no Moose;
__PACKAGE__->meta->make_immutable;

1;

The name of the report is now taken from the final part of the module name (i.e. FooBar). When the generate() method is called this module will generate exactly the same report as the previous example (events in the previous day tagged as foobar). The only difference is that the template (foobar.tt) now needs to be in one of the standard template directories. With the module in place it can be added to one of the report lists in the configuration for the BuzzSaw::Reporter module.

More complex reports

Occasionally it is necessary to analyse the data before generating the report or it might not be possible to do certain processing of the event data in the template. In this case a sub-class of the BuzzSaw::Report module is essential. The most appropriate way to do this is to override the process_events method which by default does nothing.

You need to do something like this in your sub-class:

override 'process_events' => sub {
  my ( $self, @events ) = @_;

  my %results;
  for my $event (@events) {
    # do some processing...
    # stuff data in %results hash...
  }

  return %results;
};

Tip: watch out for that obligatory trailing semi-colon. A good example of processing the events data in this way can be found in the Kernel report.