SYNOPSIS
      use Mail::Log::Parse;

      $object = Mail::Log::Parse->new({  log_file => '/path/to/logfile'  });
      %line_info = %{object->next()};
  
      $line_num = $object->get_line_num();
  
      if ( $object->go_forward($amount) ) {
        ...
      }
  
      if ( $object->go_backward($amount) ) {
        ...
      }
  
      %line_info = %{object->previous()};

DESCRIPTION
    This is the root-level module for a generic mail log file parser. It is
    capable of opening either a compressed or uncompressed logfile, and
    either stepping through it line by line, or seaking around in it based
    on the logical lines. (Lines not pertaining to the type of log currently
    being searched are skipped, as if they don't exist.)

    On it's own it doesn't actually do much: You'll need a subclass that can
    parse a particular program's log entries. But such subclasses are
    designed to be easy to write and use.

USAGE
    This is an object-oriented module. Avalible object methods are below.

    In a string context, it will return a string specifying the path to the
    file and the current line number. In a boolean context, it will return
    whether it has been correctly initilized. (Whether it has a file.)
    Numeric context throws an error.

    Oh, and interator context ('<>') returns the same as 'next'...

  new (constructor)

    The base constructor for the Mail::Log::Parse classes. It takes an
    (optional) hash containing path to the logfile as an arugment, and
    returns the new object.

    Example:

      $object = Mail::Log::Parse->new({  log_file => '/path/to/logfile'  });

    Note that it is an error to call any method other than `set_logfile' if
    you have not passed it in the constructor.

  set_logfile

    Sets the logfile that this object will attempt to parse. It will throw
    exceptions if it can't open the file for any reason, and will return
    true on success.

    Files can be compressed or uncompressed: If they are compressed, then
    `IO::Uncompress::AnyUncompress' must be installed with the relevant
    decompression libraries. (As well as version 0.17 or better of
    File::Temp.) Currently only 'tgz', 'zip', 'gz', and 'bz2' archives are
    supported, but there is no technical reason not to support more. (It
    just keeps a couple of lines of code shorter.)

    Note that to support seeking in the file the log will be uncompressed to
    disk before it is read: If there is insufficent space to do so, we may
    have trouble. It also means this method may take a while to return for
    large compressed logs.

    Example:

      $object->set_logfile('path/to/file');

  next

    Returns a reference to a hash of the next parsable line of the log, or
    undef on end of file/failure.

    There are a couple of required keys that any parser must implement:

    timestamp, program, id, text.

    Where `timestamp' must the the unix timestamp, `program' must be the
    name of the program that reported the logline (Sub-programs are
    recommened to be listed, if possible), `id' is the tracking ID for that
    message, as reported by the program, and `text' is the text following
    any 'standard' headers. (Usually, minus those already required keys.)

    This version is just a placeholder: It will return a
    'Mail::Log::Exceptions::Unimplemented' exception if called. It is
    expected to be overridden by the subclass. (And is the only method
    needed to be overridden.)

    Other 'standard' fields that are expected in a certain format (but are
    not required to always be present) are 'from', 'to', 'size', 'subject'.
    'to' should point to an array of addresses. (As listed in the log. That
    includes angle brackets, usually.)

    Example:

      while $hash_ref ( $object->next() ) {
        ...
      }

    or...

      while $hash_ref ( <$object> ) {
        ...
      }

  previous

    Returns a reference to a hash of the previous line of the log, or undef
    on failure/beginning of file.

    See `next' for details: It works nearly exactly the same. (In fact, it
    calls next as a parser.)

  go_forward

    Goes forward a specifed number of (logical) lines, or 1 if unspecified.
    It will throw an error if it fails to seek as requested.

    Returns true on success.

    Example:

      $object->go_forward(4);

  go_backward

    Goes backward a specifed number of (logcial) lines, or 1 if unspecified.
    It will throw an error if it fails to seek as requested.

    If the seek would go beyond the beginning of the file, it will go to the
    beginning of the file.

    Returns true on success.

    Example:

      $object->go_backward(4);

  go_to_beginning

    Goes to the beginning of the file, no matter how far away that is.

    Returns true on success.

  go_to_end

    Goes to the end of the file, no matter where it is.

    This attempts to be efficient about it, skipping where it can.

    Returns true on success.

  get_line_number

    Returns the current logical line number.

    Note that line numbers start at zero, where 0 is the absolute beginning
    of the file.

    Example:

      $line_num = $object->get_line_number();

  go_to_line_number

    Goes to a specific logical line number. (Preferably one that exits...)

UTLITY METHODS
    The following methods are not for general consumption: They are
    specifically provided for use in implementing subclasses. Using them
    incorrectly, or outside a subclass, can get the object into an invalid
    state.

    ONLY USE IF YOU ARE IMPLEMENTING A SUBCLASS.

  _set_current_position_as_next_line

    Sets the current position in the file as the next 'line' position in
    sequence.

    Call once you have determined that the current line of data (as returned
    from `_get_data_line') is parsable in the currently understood format.

  _get_data_line

    Returns the next line of data, as a string, from the logfile. This is
    raw data from the logfile, seperated by the current input seperator.

  Suggested usage:

    Suggestion on how to use the above two methods to implement a 'next'
    routine in a subclass:

      sub next {
            my ($self) = @_;

            # The hash we will return.
            my %line_info = ( program => '' );

            # Some temp variables.
            my $line;

            # In a mixed-log enviornment, we can't count on any 
            # particular line being something we can parse.  Keep
            # going until we can.
            while ( $line_info{program} !~ m/$program_name/ ) {
                    # Read the line.
                    $line = $self->_get_data_line() or return undef;

                    # Program name.
                    $line_info{program} = $line ~= m/$regrex/;
            }

            # Ok, let's update our info.
            $self->_set_current_position_as_next_line();

            # Continue parsing
            ...

BUGS
    `go_forward' and `go_backward' at the moment don't test for negative
    numbers. They may or may not work with a negative number of lines: It
    depends where you are in the file and what you've read so far.

    Those two methods should do slightly better on 'success' testing, to
    return better values. (They basically always return true at the moment.)

    `get_line_number' will return one less than the true line number if you
    are at the end of the file, and the buffer was completely filled. (So
    that the end of the file is the last space of the buffer.) Changing the
    buffer size or just going back and re-reading so that the buffer is
    restarted at a different location will allow you to retrieve the correct
    file length.

REQUIRES
    Scalar::Util, File::Basename, IO::File, Mail::Log::Exceptions

RECOMMENDS
    IO::Uncompress::AnyUncompress

AUTHOR
    Daniel T. Staal

    DStaal@usa.net

SEE ALSO
    Parse::Syslog::Mail, which does some of what this module does. (This
    module is a result of running into what that module doesn't support.
    Namely seeking through a file, both forwards and back.)

HISTORY
    Nov 18, 2008 - Now buffers reading, and prefers data from the buffer.

    Oct 24, 2008 - File::Temp now optional; only required for uncompressed
    files. Added go_to_line_number for slightly better functionality.

    Oct 14, 2008 - Found that I need File::Temp of at least version 0.17.

    Oct 13, 2008 - Fixed tests so they do a better job of checking if they
    need to skip.

    Oct 6, 2008 - Inital version.

COPYRIGHT and LICENSE
    Copyright (c) 2008 Daniel T. Staal. All rights reserved. This program is
    free software; you can redistribute it and/or modify it under the same
    terms as Perl itself.

    This copyright will expire in 30 years, or 5 years after the author's
    death, whichever is longer.

