# 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.