The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Logwatch::RecordTree - an object to collect and print Logwatch events

VERSION

version 2.056

SYNOPSIS

 use Logwatch::RecordTree ( %options );

 my $tree = Logwatch::RecordTree->new( name => 'Service', ... );

 while ($ThisLine = <>) {
   ...
   # typical Logwatch event parsing from log files:
   elsif (my ($event, $from, $to) = $ThisLine =~ m/.../) {
     $tree->log($event, $from, $to);
   }
   elsif (my ($rx_or_tx, $subject, $ip) = $ThisLine =~ m/.../) {
     $tree->log('Event', [$rx_or_tx, 'Logwatch::RecordTree::IPv4', {limit => 5}], $ip, $subject);
   }
   ...
 }
 ...
 print $tree;    # print the accumulated logs

DESCRIPTION

Logwatch::RecordTree collects events noticed by logwatch service scripts. When the script is finished, it produces a formatted summary ready to print. The tree structure can be extended indefinitely. There are options and hooks for adjusting the format of the summary.

Logwatch::RecordTree can be used as-is, or it can be sub-classed to add specialized formatting. For example, see Logwatch::RecordTree::IPv4 which can be used when events should be keyed and sorted by IP number.

Logwatch::RecordTree overloads the stringification operator via the sprint method.

%options specified at 'use' time are added to the defaults option hash (see defaults method below).

Example service scripts for dovecot, exim, and http-error (Apache's error_log) are provided in the extra/ subdirectory. These scripts would be placed in the /etc/logwatch/services/scripts directory according to the normal Logwatch configuration instructions.

Methods

Logwatch::RecordTree->new( [ options ] );

Creates a new Logwatch::RecordTree object. The following options are available, and are also available as accessors:

Options

name => 'event/group name'

Set or get the name/title of this event or group. Required at new time and cannot be changed after.

This is the label that will appear in the logwatch listing. For example, the top level name might be 'Sendmail:' and the second level names could be 'Sent:', 'Received:', etc.

sprint_name => sub {my $self = shift; ... }

Callback to format the name field. The default simply returns name.

sort_key => 'string'

Get or set a key to use when the parent sort_children method is called. If sort_key is defined for a child, its value is used instead of the child's name

Be careful that 'string' doesn't collide with any names or other sort_keys within this child. Duplications can cause unpredictable sorting problems, including possible loss of output lines.

case_sensitive => true or false

Set or get case sensitivity for sort_children. Default is insensitive (false).

count => integer

Count how many times this event has been logged. Incremented by each call to log.

count_fields => [ array of fields to make up the count ]

Set or get the extended fields to be added to the count. For example, you can make the count field look like "8/14" to indicate that this particular item represents 8 events out of a total of 14.

During sprint, each child's count is unshifted into the first position of the array. Next, all the columns of the children's count_fields are measured, then padded (on the left) to the same length. The padded fields are concatenated to make the 'count' field of each child's line.

no_indent => true or false

Suppress the indentation. This flag is set for a child if it is an only child (in sprint).

no_count => true or false

Suppress the count field. This flag is set for a child if it is an only child and its count is the same as the parent's count.

children

A hash of child Logwtch::RecordTree items keyed by their names.

limit => integer

Limit the number of children printed. When the limit is reached, the list is truncated with a report of how many more are left.

This is a 'soft' limit: if the count is over the limit by three or less, the list is not truncated. This prevents the rather ridiculous 'and 1 more' message.

indent => ' '

The string used to indent the children of this level of the tree. During <Bsprint> for an item, this string is calculated based on the length of the longest count field (or at least 3), then set for each child.

post_callback => sub {my ($self, $path_ref) = @_; ... }

The sprint method prepares an array of lines to print. When the array is fully constructed, this callback is called thusly:

    $self->post_callback->($self, \@path);

where path is the array of names leading to this item. The callback may adjust @{$self->lines}.

columnize => true or false

If this flag is set, Logwatch::RecordTree will convert the child lines into multi-column output. See the sprint_columns method below.

neat_names => true or false

If true, children's names will be printed with padding so that they make a neat column. If this value is negative, the padding will be on the right (names are left-justified).

neat_format => format string

When neat_names is set, sprint calculates the format string and stores it here in each of the children. The default is:

    "%s"

which means no special formatting.

extra => whatever...

Set or get a user defined data field for this item. The value can be any scalar or object.

In the following methods, either $tree or $item is used as the object reference. $item indicates that the particular item at that point of the RecordTree is affected. $tree indicates that the method affects the entire tree, either through class variables or because the method is inherently recursive and may descend down through the RecordTree.

$tree->defaults

Returns a reference to a hash of default options. This hash is a class variable, used for all instances, but each sub-class gets its own independant hash.

The keys of the hash are the option/method names and the values are the default values to be used when a new instance is created. Any options specified in the 'use' directive are added to the hash.

$tree->child_by_name (@path)

Find and return the child by following the names in path. Returns undef if no item exists for the given path.

$item->create_child ($name, [ $type, [ $opts ] ] )

Create (and return) a child Logwatch::RecordTree (or a subclass) and add it to children of $item.

If type is not defined (or is any false value), it is set to this package ('Logwatch::RecordTree'). opts, if set, is a reference to a hash of options. The child is then constructed with:

    $opts->{name} = $name;  # add name to %opts
    $child = $type->new( %{$opts} );

The new child is added to the children hash and returned to the caller.

$item->adopt($child)

Copy $child (a Logwatch::RecordTree object) into $item's children. $item's count is updated (by adding the $child's count.

If $child's name is already in $item's children, then instead of simply adding $child, $item's child adopts (recursively) $childs children.

$tree->log (@path);
$tree->log_no_count (@path);

Log new event or group, adding children as necessary. This method is the primary reason for this module. It is called to create and log events as they are parsed from log file lines. log_no_count is the same except that counts are not incremented.

path is an array representing the event. The elements of path may be the names leading to the event, or they may be a reference to an array. The array consists of:

    name, type, [ \%options ]

where type is the module name to be created (i.e: 'Logwatch::RecordTree::IPv4'), and %options are options to be passed when creating the item. These are all passed directly to create_child (above) when @path doesn't yet exist.

See 'USAGE' below to see how this works in practice.

$item->sort_children

Returns an array of the b<name>s of the children sorted by Sort::Key::Natural's natsort. childrens' names are used as the sort key unless the child has a sort_key assigned, in which case, that is used.

Subclassing and overriding this method can be useful (for example, see Logwatch::RecordTree::IPv4).

$item->curr_indent( [ 'indent string' ] )

Logwatch::RecordTree stores a copy of the current indentation string here when sprint is called.

$tree->sprint( [ $callback ] )

Returns the Logwatch::RecordTree as a formatted string ready for printing.

If callback is set, it is called just before the sprint returns (after post_callback):

    $callback->($self, $path)

where self is the current Logwatch::RecordTree item, and path is a reference to the array of names that lead to this item. callback may make adjustments to $self->lines.

This method is used to overload the stringification operator, so:

    print $tree

is the same as:

    print $tree->sprint;
$item->width( [ integer ] )

Set or get the page or display width. Used for calculating column formatting in the sprint_columns method. Default is 80.

$item->col_width ( [ integer ] )

Set or get the columns width for sprint_columns. If not explicitly set, Logwatch::RecordTree uses the length of the longest child line in lines.

$item->col_count {

Set or get the number of columns for sprint_columns. If not explicitly set, Logwatch::RecordTree calculates it from width (minus indentation) and col_width.

$tree->sprint_columns ([ $width, [ $col_count, [ $col_width ] ] ])

Convert lines into a multi-column display if it fits. width, col_count, and col_width, all of which are optional, control the formatting.

USAGE

Create a $tree with:

    my $tree = Logwatch::RecordTree->new(name => 'Tree');

Calling this:

    $tree->log('One', 'AAA');

creates a new child in $tree named 'One', and a new child in 'One' named 'AAA'. Each child (and $tree itself) have their counts incremented to one.

Subsequently calling:

    $tree->log('One', 'BBB');

increments the counts for $tree and 'One'. Since 'BBB' doesn't exist yet, it is created and added to the children of 'One', and its count is set to one.

If we now call:

    $tree->log('One', 'AAA');

again, this path exists all the way down to 'AAA', so the only effect is to increment the counts along that path.

If we were to print $tree now, we would see:

    3 Tree One
      2 AAA
      1 BBB

$tree has only one child ('One') and its count is 3, the same as $tree's count, so the child is concatenated to the parent and its count is suppressed (making for a cleaner, more readable report).

If we now call:

    $tree->log('Two', 'BBB');
    $tree->log('Two', 'CCC');
    $tree->log(['Three', undef, {sort_key => 'Z'}], 'xxx');
    $tree->log('Two', 'AAA');
    $tree->log('Two', 'BBB');

printing would show:

    8 Tree
      3 One
        2 AAA
        1 BBB
      4 Two
        1 AAA
        2 BBB
        1 CCC
      1 Three xxx

Note that 'Three' sorts alphabetically before 'Two', so we override with "sort_key => 'Z'".

The callback argument to sprint can add last-minute formatting:

 print $tree->sprint(
     sub {
         my ($self, $path) = @_;

         if (@{$path} == 1) {           # entries with exactly one name in the path
             push @{$self->lines}, '';  # add blank line after each top-level entry
         }
     }
 );

gives:

    8 Tree
      3 One
        2 AAA
        1 BBB

      4 Two
        1 AAA
        2 BBB
        1 CCC

      1 Three xxx

SEE ALSO

Logwatch
Logwatch::RecordTree::IPv4

AUTHOR

Reid Augustin <reid@hellosix.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Reid Augustin.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.