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

NAME

Log::Dynamic - OOish dynamic and customizable logging

SYNOPSIS

Object instatiation

   use Log::Dynamic;

   # Set up logging so that _ALL_ log types are valid
   my $log = Log::Dynamic->open (
       file => 'logs/my.log',
       mode => 'append',
   );

      ## OR ##

   # Set up logging so that there is a set list of valid types
   my $log = Log::Dynamic->open (
       file  => 'logs/my.log',
       mode  => 'append',
       types => [qw/ foo bar baz /],
   );

      ## OR ##

   # Set up logging so that there is a set list of valid types
   # and override the default invalid type handler
   my $log = Log::Dynamic->open (
       file         => 'logs/my.log',
       mode         => 'append',
       types        => [qw/ foo bar baz /],
       invalid_type => sub { "INVALID TYPE: ".(shift)."\n" },
   );

Basic logging

   # Just like many other logging packages: log(TYPE, MESSAGE)
   $log->log('INFO', 'I can has info?');
   $log->log('ERROR', 'Oh crapz! Someone killed a kittah!');

Custom logging

   # Call any log type you like as an object method. For 
   # example, if you are logging cache hits and misses you 
   # might want to do something like:
   if ($CACHE->{$key}) {
       $log->cache_hit("Got hit on key $key");
       return $CACHE->{$key};
   } else {
       $log->cache_miss("Awww... Key $key was a miss");
       $CACHE->{$key} = do_expensive_operation(@args);
   }

Other usage

   # Use the object as a file handle for print() statements
   # from within your script or application:
   print {$$log} "This is a special message. Pay attention!\n";

DESCRIPTION

Yet another darn logger? Why d00d?

Well, I wanted to write a lite weight logging module that...

  • developers could use in a way that felt natural to them and it would just work,

  • was adaptable enough that it could be used in dynamic, ever changing environments,

  • was flexible enough to satisfy most logging needs without too much overhead,

  • and gave developers full control over handling the myriad of log events that occur in large applications.

Log::Dynamic still has a ways to go, but the direction seems promising. Comments and suggestions are always welcome.

LOG FORMAT

Currently Log::Dynamic has only one format for the log entries. This looks like:

    TIME/DATE STAMP [LOG TYPE] LOG MESSAGE (CALLER INFO)

Eventually this module will have support user defined log formats, as it should having a name like Log::Dynamic.

LOG TYPES

Log "type" refers to the string displayed in the square brackets of your log output. In the following example the type is 'BEER ERROR':

    Thu Nov  8 21:14:12 2007 [BEER ERROR] Need more (main bottles.pl 99)

For those unfamiliar with logging this is especially useful when grep-ing through your logs for specific types of errors, ala:

    % grep -i 'beer error' /path/to/my.log

As stated above, by default there is no set list of types that this module supports. If you want to have a new type start showing up in your logs just call an object method of that name and Log::Dynamic will automatically do what you want:

    $log->new_type('Hai!');

Limiting types

By default Log::Dynamic supports any log type you throw at it. However, if you would like to define a finite set of valid (supported) log types you may do so using the 'types' parameter durning object instantiation. For example, if you would like only the types 'info', 'warn', and 'error' to be valid log types within your application you would instantiate you object like:

    my $log = Log::Dynamic->open (
        file  => 'my.log',
        types => [qw/ info warn error /],
    );

If you decide to define a set of valid types and your application attempts to log with an invalid type then, by default, Log::Dynamic will croak with an appropriate error. _HOWEVER_, if you don't want to go around your application wrapping each log call in an eval then you may override this behavior using the 'invalid_type' parameter:

    my $log = Log::Dynamic->open (
        file         => 'my.log',
        types        => [qw/ info warn error /],
        invalid_type => sub { warn (shift)." is bad! Moving on...\n" }
    );

If you choose to override the default invalid type handler Log::Dynamic will execute the provided subroutine and will pass it one parameter: the string of the invalid type that your application attempted to use.

METHODS

open()

This is the object constructor. (Sure, you can still use new() if you wish) The open() method has a number of available parameters, each with several allowed values. They are:

  • file (required)

    Values: file name, STDOUT or STDERR

  • mode (optional)

    Values: 'append' or 'clobber'

    The default value is 'append'.

  • types (optional)

    Values: [qw/ array ref of your valid types /]

    By default Log::Dynamic lets you call _ANY_ type as a method. However, if you would like to limit the set of valid types you can do that using this parameter. Once the list is set, if an invalid type is called Log::Dynamic croaks with a message. (Or excutes a method that can be specified using the following parameter)

  • invalid_type (optional)

    Value: code ref to handle invalid types

    See LIMITING LOG TYPES above.

  • ucase (optional)

    Values: 0 or 1

    By default all types are forced to uppercase when printed to the logs. For example, a call to $log->SomeError('Foo') would show up in the log as '... [SOMEERROR] Foo ...'. If you would like to maintain case then set the 'ucase' flag to a non-true value.

Here is an example instantiation for logging to a file that you want to clobber:

    my $log = Log::Dynamic->open (
        file => '/path/to/logs/my.log',
        mode => 'clobber',
    );

Here is an example instantiation for logging to STDERR:

    my $log = Log::Dynamic->open (file => STDERR);

As you can see there is no need to quote STDERR and STDOUT, but it will still work if you choose to quote them.

close()

Close the file handle.

log()

Your basic log subroutine. just give it the log type and the log message:

    $log->log('TYPE','MESSAGE');

Message Types are discussed above.

dump()

Use this subroutine if you would like to dump the contents of a data structure or object to your log file. This sub is different from the other logging subs in that it requires you pass it a hash reference of arg/value pairs. Only the 'data' arg is required, however there are several others you can use to customize your dump output. They are:

  • data (required)

    Reference to the data structure or object you want to dump.

  • dump_name (optional)

    The name you want to give to your dumped data structure. Default: 'anonymous data'.

  • dump_type (optional)

    The type string that you want to associate with this dump. Default: 'DUMP'.

  • begin_msg (optional)

    Message printed before the dump. Default: 'BEGIN dump for:'.

  • end_msg (optional)

    Message printed after the dump, Default: 'END dump for:'.

Here is example output if you specify _only_ the required 'data' argument:

Code:

    my $data = { foo => 'abc', bar => [qw/baz bing] };
    $log->dump({ data => $data });

Output:

    Sat Nov 24 14:05:29 2007 [DUMP] BEGIN dump for: 'anonymous data'
    $VAR1 = {
              'bar' => [
                         'baz',
                         'bing'
                       ],
              'foo' => 'abc'
            };
    END dump for: 'anonymous data' (main my-script.pl 21)

Here is example output where all arguments are specified:

Code:

    my $whoop = { foo => 'abc', bar => [qw/baz bing] };
    $log->dump({
        data      => $whoop,
        dump_name => 'Whoop',
        dump_type => 'its a whoop!',
        begin_msg => 'Lets start rocking with:',
        end_msg   => 'Chill out, its over for:',
    });

Output:

    Sat Nov 24 14:13:26 2007 [ITS A WHOOP!] Lets start rocking with: 'Whoop'
    $VAR1 = {
              'bar' => [
                         'baz',
                         'bing'
                       ],
              'foo' => 'abc'
            };
    Chill out, its over for: 'Whoop' (main whoop.pl 123)

ucase()

Change the boolean value for the 'ucase' flag. Recommended usage:

    $log->ucase(0);  # The case for types will be preserved
                     #   - OR -
    $log->ucase(1);  # Types will be forced to uppercase (default)

The 'ucase' flag is explained above in the description for the open() method.

Custom Methods

Log any type of message you want simply by calling the type as an object method. For example, if you want to log a message with a type of ALARM you would do:

    $log->alarm('OONTZ!');

This would print a log entry that looks like:

    Thu Nov  8 21:14:12 2007 [ALARM] OONTZ! (main techno.pl 42)

This functionality was the impetus for writing this module. What ever type you want to see in the log JUST USE IT! =)

OTHER USAGE

While most OO modules bless a reference to a data structure, this module blesses a reference to an open file handle. Why did I do that? Because I can and I felt like doing something different. The only "special" thing this really lets you do is use the object as a file handle from within your script or application. All you have to do is dereference it when you use it. For example:

    # Normal log entry
    $log->info('This is information');

    # Special log entry
    print {$$log} "*** Hai. I am special. Pls give me attention! ***\n";

Obviously if you use the object in this special way you will not get any of the nice additional information (timestamp, log type, and caller information) that you would get when using the normal way. This simply gives you the flexibility to print anything you want to your log. A useful example would be a dump of an object or data structure:

    use Data::Dumper;
    print {$$log} "Object dump:\n" . Dumper($object);

BUGS

None that I know of yet.

AUTHOR

James Conerly <jconerly@cpan.org> 2007

LICENSE

This software is free to use. If you use pieces of my code in your scripts or applications all I ask is that you site me. Other than that, log away my friends.

SEE ALSO

 Carp, Data::Dumper