diff options
author | Jos Boumans <kane@xs4all.net> | 2006-10-13 19:12:57 +0200 |
---|---|---|
committer | Steve Peters <steve@fisharerojo.org> | 2006-10-19 13:49:56 +0000 |
commit | f0ac4cdb6e00777d18589f0326b32a86989110af (patch) | |
tree | 89b825198ca2a9c752487cafc39538a32c5660a1 /lib | |
parent | d116c547427fb631beb1664235978ae153bc51e3 (diff) | |
download | perl-f0ac4cdb6e00777d18589f0326b32a86989110af.tar.gz |
Add Log::Message and Log::Message::Simple to the core
From: "Jos Boumans" <kane@xs4all.net>
Message-ID: <13003.80.127.35.68.1160752377.squirrel@webmail.xs4all.nl>
p4raw-id: //depot/perl@29052
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Log/Message.pm | 600 | ||||
-rw-r--r-- | lib/Log/Message/Config.pm | 197 | ||||
-rw-r--r-- | lib/Log/Message/Handlers.pm | 191 | ||||
-rw-r--r-- | lib/Log/Message/Item.pm | 192 | ||||
-rw-r--r-- | lib/Log/Message/Simple.pm | 293 | ||||
-rw-r--r-- | lib/Log/Message/Simple/t/01_use.t | 8 | ||||
-rw-r--r-- | lib/Log/Message/Simple/t/02_imports.t | 68 | ||||
-rw-r--r-- | lib/Log/Message/Simple/t/03_functions.t | 76 | ||||
-rw-r--r-- | lib/Log/Message/t/01_Log-Message-Config.t | 84 | ||||
-rw-r--r-- | lib/Log/Message/t/02_Log-Message.t | 175 | ||||
-rw-r--r-- | lib/Log/Message/t/conf/LoadMe.pl | 1 | ||||
-rw-r--r-- | lib/Log/Message/t/conf/config_file | 30 |
12 files changed, 1915 insertions, 0 deletions
diff --git a/lib/Log/Message.pm b/lib/Log/Message.pm new file mode 100644 index 0000000000..6b61265f9d --- /dev/null +++ b/lib/Log/Message.pm @@ -0,0 +1,600 @@ +package Log::Message;
+
+use strict;
+
+use Params::Check qw[check];
+use Log::Message::Item;
+use Log::Message::Config;
+use Locale::Maketext::Simple Style => 'gettext';
+
+local $Params::Check::VERBOSE = 1;
+
+BEGIN {
+ use vars qw[$VERSION @ISA $STACK $CONFIG];
+
+ $VERSION = 0.01;
+
+ $STACK = [];
+}
+
+
+=pod
+
+=head1 NAME
+
+Log::Message - A generic message storing mechanism;
+
+=head1 SYNOPSIS
+
+ use Log::Message private => 0, config => '/our/cf_file';
+
+ my $log = Log::Message->new( private => 1,
+ level => 'log',
+ config => '/my/cf_file',
+ );
+
+ $log->store('this is my first message');
+
+ $log->store( message => 'message #2',
+ tag => 'MY_TAG',
+ level => 'carp',
+ extra => ['this is an argument to the handler'],
+ );
+
+ my @last_five_items = $log->retrieve(5);
+
+ my @items = $log->retrieve( tag => qr/my_tag/i,
+ message => qr/\d/,
+ remove => 1,
+ );
+
+ my @items = $log->final( level => qr/carp/, amount => 2 );
+
+ my $first_error = $log->first()
+
+ # croak with the last error on the stack
+ $log->final->croak;
+
+ # empty the stack
+ $log->flush();
+
+
+=head1 DESCRIPTION
+
+Log::Message is a generic message storage mechanism.
+It allows you to store messages on a stack -- either shared or private
+-- and assign meta-data to it.
+Some meta-data will automatically be added for you, like a timestamp
+and a stack trace, but some can be filled in by the user, like a tag
+by which to identify it or group it, and a level at which to handle
+the message (for example, log it, or die with it)
+
+Log::Message also provides a powerful way of searching through items
+by regexes on messages, tags and level.
+
+=head1 Hierarchy
+
+There are 4 modules of interest when dealing with the Log::Message::*
+modules:
+
+=over 4
+
+=item Log::Message
+
+Log::Message provides a few methods to manipulate the stack it keeps.
+It has the option of keeping either a private or a public stack.
+More on this below.
+
+=item Log::Message::Item
+
+These are individual message items, which are objects that contain
+the user message as well as the meta-data described above.
+See the L<Log::Message::Item> manpage to see how to extract this
+meta-data and how to work with the Item objects.
+You should never need to create your own Item objects, but knowing
+about their methods and accessors is important if you want to write
+your own handlers. (See below)
+
+=item Log::Message::Handlers
+
+These are a collection of handlers that will be called for a level
+that is used on a L<Log::Message::Item> object.
+For example, if a message is logged with the 'carp' level, the 'carp'
+handler from L<Log::Message::Handlers> will be called.
+See the L<Log::Message::Handlers> manpage for more explanation about how
+handlers work, which one are available and how to create your own.
+
+=item Log::Message::Config
+
+Per Log::Message object, there is a configuration required that will
+fill in defaults if the user did not specify arguments to override
+them (like for example what tag will be set if none was provided),
+L<Log::Message::Config> handles the creation of these configurations.
+
+Configuration can be specified in 4 ways:
+
+=over 4
+
+=item *
+
+As a configuration file when you C<use Log::Message>
+
+=item *
+
+As arguments when you C<use Log::Message>
+
+=item *
+
+As a configuration file when you create a new L<Log::Message> object.
+(The config will then only apply to that object if you marked it as
+private)
+
+=item *
+
+As arguments when you create a new Log::Message object.
+
+You should never need to use the L<Log::Message::Config> module yourself,
+as this is transparently done by L<Log::Message>, but its manpage does
+provide an explanation of how you can create a config file.
+
+=back
+
+=back
+
+=head1 Options
+
+When using Log::Message, or creating a new Log::Message object, you can
+supply various options to alter its behaviour.
+Of course, there are sensible defaults should you choose to omit these
+options.
+
+Below an explanation of all the options and how they work.
+
+=over 4
+
+=item config
+
+The path to a configuration file to be read.
+See the manpage of L<Log::Message::Config> for the required format
+
+These options will be overridden by any explicit arguments passed.
+
+=item private
+
+Whether to create, by default, private or shared objects.
+If you choose to create shared objects, all Log::Message objects will
+use the same stack.
+
+This means that even though every module may make its own $log object
+they will still be sharing the same error stack on which they are
+putting errors and from which they are retrieving.
+
+This can be useful in big projects.
+
+If you choose to create a private object, then the stack will of
+course be private to this object, but it will still fall back to the
+shared config should no private config or overriding arguments be
+provided.
+
+=item verbose
+
+Log::Message makes use of another module to validate its arguments,
+which is called L<Params::Check>, which is a lightweight, yet
+powerful input checker and parser. (See the L<Params::Check>
+manpage for details).
+
+The verbose setting will control whether this module will
+generate warnings if something improper is passed as input, or merely
+silently returns undef, at which point Log::Message will generate a
+warning.
+
+It's best to just leave this at its default value, which is '1'
+
+=item tag
+
+The tag to add to messages if none was provided. If neither your
+config, nor any specific arguments supply a tag, then Log::Message will
+set it to 'NONE'
+
+Tags are useful for searching on or grouping by. For example, you
+could tag all the messages you want to go to the user as 'USER ERROR'
+and all those that are only debug information with 'DEBUG'.
+
+At the end of your program, you could then print all the ones tagged
+'USER ERROR' to STDOUT, and those marked 'DEBUG' to a log file.
+
+=item level
+
+C<level> describes what action to take when a message is logged. Just
+like C<tag>, Log::Message will provide a default (which is 'log') if
+neither your config file, nor any explicit arguments are given to
+override it.
+
+See the Log::Message::Handlers manpage to see what handlers are
+available by default and what they do, as well as to how to add your
+own handlers.
+
+=item remove
+
+This indicates whether or not to automatically remove the messages
+from the stack when you've retrieved them.
+The default setting provided by Log::Message is '0': do not remove.
+
+=item chrono
+
+This indicates whether messages should always be fetched in
+chronological order or not.
+This simply means that you can choose whether, when retrieving items,
+the item most recently added should be returned first, or the one that
+had been added most long ago.
+
+The default is to return the newest ones first
+
+=back
+
+=cut
+
+
+### subs ###
+sub import {
+ my $pkg = shift;
+ my %hash = @_;
+
+ $CONFIG = new Log::Message::Config( %hash )
+ or die loc(qq[Problem initialising %1], __PACKAGE__);
+
+}
+
+=head1 Methods
+
+=head2 new
+
+This creates a new Log::Message object; The parameters it takes are
+described in the C<Options> section below and let it just be repeated
+that you can use these options like this:
+
+ my $log = Log::Message->new( %options );
+
+as well as during C<use> time, like this:
+
+ use Log::Message option1 => value, option2 => value
+
+There are but 3 rules to keep in mind:
+
+=over 4
+
+=item *
+
+Provided arguments take precedence over a configuration file.
+
+=item *
+
+Arguments to new take precedence over options provided at C<use> time
+
+=item *
+
+An object marked private will always have an empty stack to begin with
+
+=back
+
+=cut
+
+sub new {
+ my $class = shift;
+ my %hash = @_;
+
+ my $conf = new Log::Message::Config( %hash, default => $CONFIG ) or return undef;
+
+ if( $conf->private || $CONFIG->private ) {
+
+ return _new_stack( $class, config => $conf );
+
+ } else {
+ my $obj = _new_stack( $class, config => $conf, stack => $STACK );
+
+ ### if it was an empty stack, this was the first object
+ ### in that case, set the global stack to match it for
+ ### subsequent new, non-private objects
+ $STACK = $obj->{STACK} unless scalar @$STACK;
+
+ return $obj;
+ }
+}
+
+sub _new_stack {
+ my $class = shift;
+ my %hash = @_;
+
+ my $tmpl = {
+ stack => { default => [] },
+ config => { default => bless( {}, 'Log::Message::Config'),
+ required => 1,
+ strict_type => 1
+ },
+ };
+
+ my $args = check( $tmpl, \%hash, $CONFIG->verbose ) or (
+ warn(loc(q[Could not create a new stack object: %1],
+ Params::Check->last_error)
+ ),
+ return
+ );
+
+
+ my %self = map { uc, $args->{$_} } keys %$args;
+
+ return bless \%self, $class;
+}
+
+sub _get_conf {
+ my $self = shift;
+ my $what = shift;
+
+ return defined $self->{CONFIG}->$what()
+ ? $self->{CONFIG}->$what()
+ : defined $CONFIG->$what()
+ ? $CONFIG->$what()
+ : undef; # should never get here
+}
+
+=head2 store
+
+This will create a new Item object and store it on the stack.
+
+Possible arguments you can give to it are:
+
+=over 4
+
+=item message
+
+This is the only argument that is required. If no other arguments
+are given, you may even leave off the C<message> key. The argument
+will then automatically be assumed to be the message.
+
+=item tag
+
+The tag to add to this message. If not provided, Log::Message will look
+in your configuration for one.
+
+=item level
+
+The level at which this message should be handled. If not provided,
+Log::Message will look in your configuration for one.
+
+=item extra
+
+This is an array ref with arguments passed to the handler for this
+message, when it is called from store();
+
+The handler will receive them as a normal list
+
+=back
+
+store() will return true upon success and undef upon failure, as well
+as issue a warning as to why it failed.
+
+=cut
+
+### should extra be stored in the item object perhaps for later retrieval?
+sub store {
+ my $self = shift;
+ my %hash = ();
+
+ my $tmpl = {
+ message => {
+ default => '',
+ strict_type => 1,
+ required => 1,
+ },
+ tag => { default => $self->_get_conf('tag') },
+ level => { default => $self->_get_conf('level'), },
+ extra => { default => [], strict_type => 1 },
+ };
+
+ ### single arg means just the message
+ ### otherwise, they are named
+ if( @_ == 1 ) {
+ $hash{message} = shift;
+ } else {
+ %hash = @_;
+ }
+
+ my $args = check( $tmpl, \%hash ) or (
+ warn( loc(q[Could not store error: %1], Params::Check->last_error) ),
+ return
+ );
+
+ my $extra = delete $args->{extra};
+ my $item = Log::Message::Item->new( %$args,
+ parent => $self,
+ id => scalar @{$self->{STACK}}
+ )
+ or ( warn( loc(q[Could not create new log item!]) ), return undef );
+
+ push @{$self->{STACK}}, $item;
+
+ { no strict 'refs';
+
+ my $sub = $args->{level};
+
+ $item->$sub( @$extra );
+ }
+
+ return 1;
+}
+
+=head2 retrieve
+
+This will retrieve all message items matching the criteria specified
+from the stack.
+
+Here are the criteria you can discriminate on:
+
+=over 4
+
+=item tag
+
+A regex to which the tag must adhere. For example C<qr/\w/>.
+
+=item level
+
+A regex to which the level must adhere.
+
+=item message
+
+A regex to which the message must adhere.
+
+=item amount
+
+Maximum amount of errors to return
+
+=item chrono
+
+Return in chronological order, or not?
+
+=item remove
+
+Remove items from the stack upon retrieval?
+
+=back
+
+In scalar context it will return the first item matching your criteria
+and in list context, it will return all of them.
+
+If an error occurs while retrieving, a warning will be issued and
+undef will be returned.
+
+=cut
+
+sub retrieve {
+ my $self = shift;
+ my %hash = ();
+
+ my $tmpl = {
+ tag => { default => qr/.*/ },
+ level => { default => qr/.*/ },
+ message => { default => qr/.*/ },
+ amount => { default => '' },
+ remove => { default => $self->_get_conf('remove') },
+ chrono => { default => $self->_get_conf('chrono') },
+ };
+
+ ### single arg means just the amount
+ ### otherwise, they are named
+ if( @_ == 1 ) {
+ $hash{amount} = shift;
+ } else {
+ %hash = @_;
+ }
+
+ my $args = check( $tmpl, \%hash ) or (
+ warn( loc(q[Could not parse input: %1], Params::Check->last_error) ),
+ return
+ );
+
+ my @list =
+ grep { $_->tag =~ /$args->{tag}/ ? 1 : 0 }
+ grep { $_->level =~ /$args->{level}/ ? 1 : 0 }
+ grep { $_->message =~ /$args->{message}/ ? 1 : 0 }
+ grep { defined }
+ $args->{chrono}
+ ? @{$self->{STACK}}
+ : reverse @{$self->{STACK}};
+
+ my $amount = $args->{amount} || scalar @list;
+
+ my @rv = map {
+ $args->{remove} ? $_->remove : $_
+ } scalar @list > $amount
+ ? splice(@list,0,$amount)
+ : @list;
+
+ return wantarray ? @rv : $rv[0];
+}
+
+=head2 first
+
+This is a shortcut for retrieving the first item(s) stored on the
+stack. It will default to only retrieving one if called with no
+arguments, and will always return results in chronological order.
+
+If you only supply one argument, it is assumed to be the amount you
+wish returned.
+
+Furthermore, it can take the same arguments as C<retrieve> can.
+
+=cut
+
+sub first {
+ my $self = shift;
+
+ my $amt = @_ == 1 ? shift : 1;
+ return $self->retrieve( amount => $amt, @_, chrono => 1 );
+}
+
+=head2 last
+
+This is a shortcut for retrieving the last item(s) stored on the
+stack. It will default to only retrieving one if called with no
+arguments, and will always return results in reverse chronological
+order.
+
+If you only supply one argument, it is assumed to be the amount you
+wish returned.
+
+Furthermore, it can take the same arguments as C<retrieve> can.
+
+=cut
+
+sub final {
+ my $self = shift;
+
+ my $amt = @_ == 1 ? shift : 1;
+ return $self->retrieve( amount => $amt, @_, chrono => 0 );
+}
+
+=head2 flush
+
+This removes all items from the stack and returns them to the caller
+
+=cut
+
+sub flush {
+ my $self = shift;
+
+ return splice @{$self->{STACK}};
+}
+
+=head1 SEE ALSO
+
+L<Log::Message::Item>, L<Log::Message::Handlers>, L<Log::Message::Config>
+
+=head1 AUTHOR
+
+This module by
+Jos Boumans E<lt>kane@cpan.orgE<gt>.
+
+=head1 Acknowledgements
+
+Thanks to Ann Barcomb for her suggestions.
+
+=head1 COPYRIGHT
+
+This module is
+copyright (c) 2002 Jos Boumans E<lt>kane@cpan.orgE<gt>.
+All rights reserved.
+
+This library is free software;
+you may redistribute and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
+1;
+
+# Local variables:
+# c-indentation-style: bsd
+# c-basic-offset: 4
+# indent-tabs-mode: nil
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Log/Message/Config.pm b/lib/Log/Message/Config.pm new file mode 100644 index 0000000000..eaeb78ba95 --- /dev/null +++ b/lib/Log/Message/Config.pm @@ -0,0 +1,197 @@ +package Log::Message::Config;
+use strict;
+
+use Params::Check qw[check];
+use Module::Load;
+use FileHandle;
+use Locale::Maketext::Simple Style => 'gettext';
+
+BEGIN {
+ use vars qw[$VERSION $AUTOLOAD];
+ $VERSION = 0.01;
+}
+
+sub new {
+ my $class = shift;
+ my %hash = @_;
+
+ ### find out if the user specified a config file to use
+ ### and/or a default configuration object
+ ### and remove them from the argument hash
+ my %special = map { lc, delete $hash{$_} }
+ grep /^config|default$/i, keys %hash;
+
+ ### allow provided arguments to override the values from the config ###
+ my $tmpl = {
+ private => { default => undef, },
+ verbose => { default => 1 },
+ tag => { default => 'NONE', },
+ level => { default => 'log', },
+ remove => { default => 0 },
+ chrono => { default => 1 },
+ };
+
+ my %lc_hash = map { lc, $hash{$_} } keys %hash;
+
+ my $file_conf;
+ if( $special{config} ) {
+ $file_conf = _read_config_file( $special{config} )
+ or ( warn( loc(q[Could not parse config file!]) ), return );
+ }
+
+ my $def_conf = \%{ $special{default} || {} };
+
+ ### make sure to only include keys that are actually defined --
+ ### the checker will assign even 'undef' if you have provided that
+ ### as a value
+ ### priorities goes as follows:
+ ### 1: arguments passed
+ ### 2: any config file passed
+ ### 3: any default config passed
+ my %to_check = map { @$_ }
+ grep { defined $_->[1] }
+ map { [ $_ =>
+ defined $lc_hash{$_} ? $lc_hash{$_} :
+ defined $file_conf->{$_} ? $file_conf->{$_} :
+ defined $def_conf->{$_} ? $def_conf->{$_} :
+ undef
+ ]
+ } keys %$tmpl;
+
+ my $rv = check( $tmpl, \%to_check, 1 )
+ or ( warn( loc(q[Could not validate arguments!]) ), return );
+
+ return bless $rv, $class;
+}
+
+sub _read_config_file {
+ my $file = shift or return;
+
+ my $conf = {};
+ my $FH = new FileHandle;
+ $FH->open("$file") or (
+ warn(loc(q[Could not open config file '%1': %2],$file,$!)),
+ return {}
+ );
+
+ while(<$FH>) {
+ next if /\s*#/;
+ next unless /\S/;
+
+ chomp; s/^\s*//; s/\s*$//;
+
+ my ($param,$val) = split /\s*=\s*/;
+
+ if( (lc $param) eq 'include' ) {
+ load $val;
+ next;
+ }
+
+ ### add these to the config hash ###
+ $conf->{ lc $param } = $val;
+ }
+ close $FH;
+
+ return $conf;
+}
+
+sub AUTOLOAD {
+ $AUTOLOAD =~ s/.+:://;
+
+ my $self = shift;
+
+ return $self->{ lc $AUTOLOAD } if exists $self->{ lc $AUTOLOAD };
+
+ die loc(q[No such accessor '%1' for class '%2'], $AUTOLOAD, ref $self);
+}
+
+sub DESTROY { 1 }
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Message::Config - Configuration options for Log::Message
+
+=head1 SYNOPSIS
+
+ # This module is implicitly used by Log::Message to create a config
+ # which it uses to log messages.
+ # For the options you can pass, see the C<Log::Message new()> method.
+
+ # Below is a sample of a config file you could use
+
+ # comments are denoted by a single '#'
+ # use a shared stack, or have a private instance?
+ # if none provided, set to '0',
+ private = 1
+
+ # do not be verbose
+ verbose = 0
+
+ # default tag to set on new items
+ # if none provided, set to 'NONE'
+ tag = SOME TAG
+
+ # default level to handle items
+ # if none provided, set to 'log'
+ level = carp
+
+ # extra files to include
+ # if none provided, no files are auto included
+ include = mylib.pl
+ include = ../my/other/lib.pl
+
+ # automatically delete items
+ # when you retrieve them from the stack?
+ # if none provided, set to '0'
+ remove = 1
+
+ # retrieve errors in chronological order, or not?
+ # if none provided, set to '1'
+ chrono = 0
+
+=head1 DESCRIPTION
+
+Log::Message::Config provides a standardized config object for
+Log::Message objects.
+
+It can either read options as perl arguments, or as a config file.
+See the Log::Message manpage for more information about what arguments
+are valid, and see the Synopsis for an example config file you can use
+
+=head1 SEE ALSO
+
+L<Log::Message>, L<Log::Message::Item>, L<Log::Message::Handlers>
+
+=head1 AUTHOR
+
+This module by
+Jos Boumans E<lt>kane@cpan.orgE<gt>.
+
+=head1 Acknowledgements
+
+Thanks to Ann Barcomb for her suggestions.
+
+=head1 COPYRIGHT
+
+This module is
+copyright (c) 2002 Jos Boumans E<lt>kane@cpan.orgE<gt>.
+All rights reserved.
+
+This library is free software;
+you may redistribute and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
+# Local variables:
+# c-indentation-style: bsd
+# c-basic-offset: 4
+# indent-tabs-mode: nil
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Log/Message/Handlers.pm b/lib/Log/Message/Handlers.pm new file mode 100644 index 0000000000..d02fb52f20 --- /dev/null +++ b/lib/Log/Message/Handlers.pm @@ -0,0 +1,191 @@ +package Log::Message::Handlers;
+use strict;
+
+=pod
+
+=head1 NAME
+
+Log::Message::Handlers - Message handlers for Log::Message
+
+=head1 SYNOPSIS
+
+ # Implicitly used by Log::Message to serve as handlers for
+ # Log::Message::Item objects
+
+ # Create your own file with a package called
+ # Log::Message::Handlers to add to the existing ones, or to even
+ # overwrite them
+
+ $item->carp;
+
+ $item->trace;
+
+
+=head1 DESCRIPTION
+
+Log::Message::Handlers provides handlers for Log::Message::Item objects.
+The handler corresponding to the level (see Log::Message::Item manpage
+for an explanation about levels) will be called automatically upon
+storing the error.
+
+Handlers may also explicitly be called on an Log::Message::Item object
+if one so desires (see the Log::Message manpage on how to retrieve the
+Item objects).
+
+=head1 Default Handlers
+
+=head2 log
+
+Will simply log the error on the stack, and do nothing special
+
+=cut
+
+sub log { 1 }
+
+=head2 carp
+
+Will carp (see the Carp manpage) with the error, and add the timestamp
+of when it occurred.
+
+=cut
+
+sub carp {
+ my $self = shift;
+ warn join " ", $self->message, $self->shortmess, 'at', $self->when, "\n";
+}
+
+=head2 croak
+
+Will croak (see the Carp manpage) with the error, and add the
+timestamp of when it occurred.
+
+=cut
+
+sub croak {
+ my $self = shift;
+ die join " ", $self->message, $self->shortmess, 'at', $self->when, "\n";
+}
+
+=head2 cluck
+
+Will cluck (see the Carp manpage) with the error, and add the
+timestamp of when it occurred.
+
+=cut
+
+sub cluck {
+ my $self = shift;
+ warn join " ", $self->message, $self->longmess, 'at', $self->when, "\n";
+}
+
+=head2 confess
+
+Will confess (see the Carp manpage) with the error, and add the
+timestamp of when it occurred
+
+=cut
+
+sub confess {
+ my $self = shift;
+ die join " ", $self->message, $self->longmess, 'at', $self->when, "\n";
+}
+
+=head2 die
+
+Will simply die with the error message of the item
+
+=cut
+
+sub die { die shift->message; }
+
+
+=head2 warn
+
+Will simply warn with the error message of the item
+
+=cut
+
+sub warn { warn shift->message; }
+
+
+=head2 trace
+
+Will provide a traceback of this error item back to the first one that
+occurrent, clucking with every item as it comes across it.
+
+=cut
+
+sub trace {
+ my $self = shift;
+
+ for my $item( $self->parent->retrieve( chrono => 0 ) ) {
+ $item->cluck;
+ }
+}
+
+=head1 Custom Handlers
+
+If you wish to provide your own handlers, you can simply do the
+following:
+
+=over 4
+
+=item *
+
+Create a file that holds a package by the name of
+C<Log::Message::Handlers>
+
+=item *
+
+Create subroutines with the same name as the levels you wish to
+handle in the Log::Message module (see the Log::Message manpage for
+explanation on levels)
+
+=item *
+
+Require that file in your program, or add it in your configuration
+(see the Log::Message::Config manpage for explanation on how to use a
+config file)
+
+=back
+
+And that is it, the handler will now be available to handle messages
+for you.
+
+The arguments a handler may receive are those specified by the
+C<extra> key, when storing the message.
+See the Log::Message manpage for details on the arguments.
+
+=head1 SEE ALSO
+
+L<Log::Message>, L<Log::Message::Item>, L<Log::Message::Config>
+
+=head1 AUTHOR
+
+This module by
+Jos Boumans E<lt>kane@cpan.orgE<gt>.
+
+=head1 Acknowledgements
+
+Thanks to Ann Barcomb for her suggestions.
+
+=head1 COPYRIGHT
+
+This module is
+copyright (c) 2002 Jos Boumans E<lt>kane@cpan.orgE<gt>.
+All rights reserved.
+
+This library is free software;
+you may redistribute and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
+1;
+
+# Local variables:
+# c-indentation-style: bsd
+# c-basic-offset: 4
+# indent-tabs-mode: nil
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Log/Message/Item.pm b/lib/Log/Message/Item.pm new file mode 100644 index 0000000000..2ecf82dc18 --- /dev/null +++ b/lib/Log/Message/Item.pm @@ -0,0 +1,192 @@ +package Log::Message::Item;
+
+use strict;
+use Params::Check qw[check];
+use Log::Message::Handlers;
+
+### for the messages to store ###
+use Carp ();
+
+BEGIN {
+ use vars qw[$AUTOLOAD $VERSION];
+
+ $VERSION = $Log::Message::VERSION;
+}
+
+### create a new item.
+### note that only an id (position on the stack), message and a reference
+### to its parent are required. all the other things it can fill in itself
+sub new {
+ my $class = shift;
+ my %hash = @_;
+
+ my $tmpl = {
+ when => { no_override => 1, default => scalar localtime },
+ id => { required => 1 },
+ message => { required => 1 },
+ parent => { required => 1 },
+ level => { default => '' }, # default may be conf dependant
+ tag => { default => '' }, # default may be conf dependant
+ longmess => { default => _clean(Carp::longmess()) },
+ shortmess => { default => _clean(Carp::shortmess())},
+ };
+
+ my $args = check($tmpl, \%hash) or return undef;
+
+ return bless $args, $class;
+}
+
+sub _clean { map { s/\s*//; chomp; $_ } shift; }
+
+sub remove {
+ my $item = shift;
+ my $self = $item->parent;
+
+ return splice( @{$self->{STACK}}, $item->id, 1, undef );
+}
+
+sub AUTOLOAD {
+ my $self = $_[0];
+
+ $AUTOLOAD =~ s/.+:://;
+
+ return $self->{$AUTOLOAD} if exists $self->{$AUTOLOAD};
+
+ local $Carp::CarpLevel = $Carp::CarpLevel + 3;
+
+ { no strict 'refs';
+ return *{"Log::Message::Handlers::${AUTOLOAD}"}->(@_);
+ }
+}
+
+sub DESTROY { 1 }
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Message::Item - Message objects for Log::Message
+
+=head1 SYNOPSIS
+
+ # Implicitly used by Log::Message to create Log::Message::Item objects
+
+ print "this is the message's id: ", $item->id;
+
+ print "this is the message stored: ", $item->message;
+
+ print "this is when it happened: ", $item->when;
+
+ print "the message was tagged: ", $item->tag;
+
+ print "this was the severity level: ", $item->level;
+
+ $item->remove; # delete the item from the stack it was on
+
+ # Besides these methods, you can also call the handlers on
+ # the object specificallly.
+ # See the Log::Message::Handlers manpage for documentation on what
+ # handlers are available by default and how to add your own
+
+
+=head1 DESCRIPTION
+
+Log::Message::Item is a class that generates generic Log items.
+These items are stored on a Log::Message stack, so see the Log::Message
+manpage about details how to retrieve them.
+
+You should probably not create new items by yourself, but use the
+storing mechanism provided by Log::Message.
+
+However, the accessors and handlers are of interest if you want to do
+fine tuning of how your messages are handled.
+
+The accessors and methods are described below, the handlers are
+documented in the Log::Message::Handlers manpage.
+
+=head1 Methods and Accessors
+
+=head2 remove
+
+Calling remove will remove the object from the stack it was on, so it
+will not show up any more in subsequent fetches of messages.
+
+You can still call accessors and handlers on it however, to handle it
+as you will.
+
+=head2 id
+
+Returns the internal ID of the item. This may be useful for comparing
+since the ID is incremented each time a new item is created.
+Therefore, an item with ID 4 must have been logged before an item with
+ID 9.
+
+=head2 when
+
+Returns the timestamp of when the message was logged
+
+=head2 message
+
+The actual message that was stored
+
+=head2 level
+
+The severity type of this message, as well as the name of the handler
+that was called upon storing it.
+
+=head2 tag
+
+Returns the identification tag that was put on the message.
+
+=head2 shortmess
+
+Returns the equivalent of a C<Carp::shortmess> for this item.
+See the C<Carp> manpage for details.
+
+=head2 longmess
+
+Returns the equivalent of a C<Carp::longmess> for this item, which
+is essentially a stack trace.
+See the C<Carp> manpage for details.
+
+=head2 parent
+
+Returns a reference to the Log::Message object that stored this item.
+This is useful if you want to have access to the full stack in a
+handler.
+
+=head1 SEE ALSO
+
+L<Log::Message>, L<Log::Message::Handlers>, L<Log::Message::Config>
+
+=head1 AUTHOR
+
+This module by
+Jos Boumans E<lt>kane@cpan.orgE<gt>.
+
+=head1 Acknowledgements
+
+Thanks to Ann Barcomb for her suggestions.
+
+=head1 COPYRIGHT
+
+This module is
+copyright (c) 2002 Jos Boumans E<lt>kane@cpan.orgE<gt>.
+All rights reserved.
+
+This library is free software;
+you may redistribute and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
+# Local variables:
+# c-indentation-style: bsd
+# c-basic-offset: 4
+# indent-tabs-mode: nil
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/lib/Log/Message/Simple.pm b/lib/Log/Message/Simple.pm new file mode 100644 index 0000000000..46188d0ed6 --- /dev/null +++ b/lib/Log/Message/Simple.pm @@ -0,0 +1,293 @@ +package Log::Message::Simple; + +use strict; +use Log::Message private => 0;; + +BEGIN { + use vars qw[$VERSION]; + $VERSION = 0.01; +} + + +=pod + +=head1 NAME + +Log::Message::Simple + +=head1 SYNOPSIS + + use Log::Message::Simple qw[msg error debug + carp croak cluck confess]; + + use Log::Message::Simple qw[:STD :CARP]; + + ### standard reporting functionality + msg( "Connecting to database", $verbose ); + error( "Database connection failed: $@", $verbose ); + debug( "Connection arguments were: $args", $debug ); + + ### standard carp functionality + carp( "Wrong arguments passed: @_" ); + croak( "Fatal: wrong arguments passed: @_" ); + cluck( "Wrong arguments passed -- including stacktrace: @_" ); + confess("Fatal: wrong arguments passed -- including stacktrace: @_" ); + + ### retrieve individual message + my @stack = Log::Message::Simple->stack; + my @stack = Log::Message::Simple->flush; + + ### retrieve the entire stack in printable form + my $msgs = Log::Message::Simple->stack_as_string; + my $trace = Log::Message::Simple->stack_as_string(1); + + ### redirect output + local $Log::Message::Simple::MSG_FH = \*STDERR; + local $Log::Message::Simple::ERROR_FH = \*STDERR; + local $Log::Message::Simple::DEBUG_FH = \*STDERR; + + ### force a stacktrace on error + local $Log::Message::Simple::STACKTRACE_ON_ERROR = 1 + +=head1 DESCRIPTION + +This module provides standardized logging facilities using the +C<Log::Message> module. + +=head1 FUNCTIONS + +=head2 msg("message string" [,VERBOSE]) + +Records a message on the stack, and prints it to C<STDOUT> (or actually +C<$MSG_FH>, see the C<GLOBAL VARIABLES> section below), if the +C<VERBOSE> option is true. +The C<VERBOSE> option defaults to false. + +Exported by default, or using the C<:STD> tag. + +=head2 debug("message string" [,VERBOSE]) + +Records a debug message on the stack, and prints it to C<STDOUT> (or +actually C<$DEBUG_FH>, see the C<GLOBAL VARIABLES> section below), +if the C<VERBOSE> option is true. +The C<VERBOSE> option defaults to false. + +Exported by default, or using the C<:STD> tag. + +=head2 error("error string" [,VERBOSE]) + +Records an error on the stack, and prints it to C<STDERR> (or actually +C<$ERROR_FH>, see the C<GLOBAL VARIABLES> sections below), if the +C<VERBOSE> option is true. +The C<VERBOSE> options defaults to true. + +Exported by default, or using the C<:STD> tag. + +=cut + +{ package Log::Message::Handlers; + + sub msg { + my $self = shift; + my $verbose = shift || 0; + + ### so you don't want us to print the msg? ### + return if defined $verbose && $verbose == 0; + + my $old_fh = select $Log::Message::Simple::MSG_FH; + print '['. $self->tag (). '] ' . $self->message . "\n"; + select $old_fh; + + return; + } + + sub debug { + my $self = shift; + my $verbose = shift || 0; + + ### so you don't want us to print the msg? ### + return if defined $verbose && $verbose == 0; + + my $old_fh = select $Log::Message::Simple::DEBUG_FH; + print '['. $self->tag (). '] ' . $self->message . "\n"; + select $old_fh; + + return; + } + + sub error { + my $self = shift; + my $verbose = shift; + $verbose = 1 unless defined $verbose; # default to true + + ### so you don't want us to print the error? ### + return if defined $verbose && $verbose == 0; + + my $old_fh = select $Log::Message::Simple::ERROR_FH; + + my $msg = '['. $self->tag . '] ' . $self->message; + + print $Log::Message::Simple::STACKTRACE_ON_ERROR + ? Carp::shortmess($msg) + : $msg . "\n"; + + select $old_fh; + + return; + } +} + +=head2 carp(); + +Provides functionality equal to C<Carp::carp()>, whilst still logging +to the stack. + +Exported by by using the C<:CARP> tag. + +=head2 croak(); + +Provides functionality equal to C<Carp::croak()>, whilst still logging +to the stack. + +Exported by by using the C<:CARP> tag. + +=head2 confess(); + +Provides functionality equal to C<Carp::confess()>, whilst still logging +to the stack. + +Exported by by using the C<:CARP> tag. + +=head2 cluck(); + +Provides functionality equal to C<Carp::cluck()>, whilst still logging +to the stack. + +Exported by by using the C<:CARP> tag. + +=head1 CLASS METHODS + +=head2 Log::Message::Simple->stack() + +Retrieves all the items on the stack. Since C<Log::Message::Simple> is +implemented using C<Log::Message>, consult its manpage for the +function C<retrieve> to see what is returned and how to use the items. + +=head2 Log::Message::Simple->stack_as_string([TRACE]) + +Returns the whole stack as a printable string. If the C<TRACE> option is +true all items are returned with C<Carp::longmess> output, rather than +just the message. +C<TRACE> defaults to false. + +=head2 Log::Message::Simple->flush() + +Removes all the items from the stack and returns them. Since +C<Log::Message::Simple> is implemented using C<Log::Message>, consult its +manpage for the function C<retrieve> to see what is returned and how +to use the items. + +=cut + +BEGIN { + use Exporter; + use Params::Check qw[ check ]; + use vars qw[ @EXPORT @EXPORT_OK %EXPORT_TAGS @ISA ];; + + @ISA = 'Exporter'; + @EXPORT = qw[error msg debug]; + @EXPORT_OK = qw[carp cluck croak confess]; + + %EXPORT_TAGS = ( + STD => \@EXPORT, + CARP => \@EXPORT_OK, + ALL => [ @EXPORT, @EXPORT_OK ], + ); + + my $log = new Log::Message; + + for my $func ( @EXPORT, @EXPORT_OK ) { + no strict 'refs'; + + ### up the carplevel for the carp emulation + ### functions + *$func = sub { local $Carp::CarpLevel += 2 + if grep { $_ eq $func } @EXPORT_OK; + + my $msg = shift; + $log->store( + message => $msg, + tag => uc $func, + level => $func, + extra => [@_] + ); + }; + } + + sub flush { + return reverse $log->flush; + } + + sub stack { + return $log->retrieve( chrono => 1 ); + } + + sub stack_as_string { + my $class = shift; + my $trace = shift() ? 1 : 0; + + return join $/, map { + '[' . $_->tag . '] [' . $_->when . '] ' . + ($trace ? $_->message . ' ' . $_->longmess + : $_->message); + } __PACKAGE__->stack; + } +} + +=head1 GLOBAL VARIABLES + +=over 4 + +=item $ERROR_FH + +This is the filehandle all the messages sent to C<error()> are being +printed. This defaults to C<*STDERR>. + +=item $MSG_FH + +This is the filehandle all the messages sent to C<msg()> are being +printed. This default to C<*STDOUT>. + +=item $DEBUG_FH + +This is the filehandle all the messages sent to C<debug()> are being +printed. This default to C<*STDOUT>. + +=item $STACKTRACE_ON_ERROR + +If this option is set to C<true>, every call to C<error()> will +generate a stacktrace using C<Carp::shortmess()>. +Defaults to C<false> + +=cut + +BEGIN { + use vars qw[ $ERROR_FH $MSG_FH $DEBUG_FH $STACKTRACE_ON_ERROR ]; + + local $| = 1; + $ERROR_FH = \*STDERR; + $MSG_FH = \*STDOUT; + $DEBUG_FH = \*STDOUT; + + $STACKTRACE_ON_ERROR = 0; +} + + +1; + +# Local variables: +# c-indentation-style: bsd +# c-basic-offset: 4 +# indent-tabs-mode: nil +# End: +# vim: expandtab shiftwidth=4: diff --git a/lib/Log/Message/Simple/t/01_use.t b/lib/Log/Message/Simple/t/01_use.t new file mode 100644 index 0000000000..147347c240 --- /dev/null +++ b/lib/Log/Message/Simple/t/01_use.t @@ -0,0 +1,8 @@ +use Test::More 'no_plan'; +use strict; + +my $Class = 'Log::Message::Simple'; + +use_ok( $Class ); + +diag( "Testing $Class version " . $Class->VERSION ) unless $ENV{PERL_CORE}; diff --git a/lib/Log/Message/Simple/t/02_imports.t b/lib/Log/Message/Simple/t/02_imports.t new file mode 100644 index 0000000000..4910b971c9 --- /dev/null +++ b/lib/Log/Message/Simple/t/02_imports.t @@ -0,0 +1,68 @@ +use Test::More 'no_plan'; +use strict; + +my $Class = 'Log::Message::Simple'; +my @Carp = qw[carp croak cluck confess]; +my @Msg = qw[msg debug error]; + + + +### test empty import +{ package Test::A; + + + eval "use $Class ()"; + Test::More::ok( !$@, "using $Class with no import" ); + + for my $func ( @Carp, @Msg ) { + Test::More::ok( !__PACKAGE__->can( $func ), + " $func not imported" ); + } +} + +### test :STD import +{ package Test::B; + + eval "use $Class ':STD'"; + Test::More::ok( !$@, "using $Class with :STD import" ); + + for my $func ( @Carp ) { + Test::More::ok( !__PACKAGE__->can( $func ), + " $func not imported" ); + } + + for my $func ( @Msg ) { + Test::More::ok( __PACKAGE__->can( $func ), + " $func imported" ); + } +} + +### test :CARP import +{ package Test::C; + + eval "use $Class ':CARP'"; + Test::More::ok( !$@, "using $Class with :CARP import" ); + + for my $func ( @Msg ) { + Test::More::ok( !__PACKAGE__->can( $func ), + " $func not imported" ); + } + + for my $func ( @Carp ) { + Test::More::ok( __PACKAGE__->can( $func ), + " $func imported" ); + } +} + +### test all import + +{ package Test::D; + + eval "use $Class ':ALL'"; + Test::More::ok( !$@, "using $Class with :ALL import" ); + + for my $func ( @Carp, @Msg ) { + Test::More::ok( __PACKAGE__->can( $func ), + " $func imported" ); + } +} diff --git a/lib/Log/Message/Simple/t/03_functions.t b/lib/Log/Message/Simple/t/03_functions.t new file mode 100644 index 0000000000..7d8a0d8994 --- /dev/null +++ b/lib/Log/Message/Simple/t/03_functions.t @@ -0,0 +1,76 @@ +use Test::More 'no_plan'; +use strict; + +my $Class = 'Log::Message::Simple'; +my @Carp = qw[carp croak cluck confess]; +my @Msg = qw[msg debug error]; +my $Text = 'text'; +my $Pkg = 'Test::A'; + +use_ok( $Class ); + +{ package Test::A; + + ### set up local equivalents to exported functions + ### so we can print to closed FH without having to worry + ### about warnings + ### close stderr/warnings for that same purpose, as carp + ### & friends will print there + for my $name (@Carp, @Msg) { + no strict 'refs'; + *$name = sub { + local $^W; + + ### do the block twice to avoid 'used only once' + ### warnings + local $Log::Message::Simple::ERROR_FH; + local $Log::Message::Simple::DEBUG_FH; + local $Log::Message::Simple::MSG_FH; + + local $Log::Message::Simple::ERROR_FH; + local $Log::Message::Simple::DEBUG_FH; + local $Log::Message::Simple::MSG_FH; + + + + + local *STDERR; + local $SIG{__WARN__} = sub { }; + + my $ref = $Class->can( $name ); + + + $ref->( @_ ); + }; + } +} + +for my $name (@Carp, @Msg) { + + my $ref = $Pkg->can( $name ); + ok( $ref, "Found function for '$name'" ); + + ### start with an empty stack? + cmp_ok( scalar @{[$Class->stack]}, '==', 0, + " Starting with empty stack" ); + ok(!$Class->stack_as_string," Stringified stack empty" ); + + ### call the func... no output should appear + ### eval this -- the croak/confess functions die + eval { $ref->( $Text ); }; + + my @stack = $Class->stack; + cmp_ok( scalar(@stack), '==', 1, + " Text logged to stack" ); + + for my $re ( $Text, quotemeta '['.uc($name).']' ) { + like( $Class->stack_as_string, qr/$re/, + " Text as expected" ); + } + + ### empty stack again ### + ok( $Class->flush, " Stack flushed" ); + cmp_ok( scalar @{[$Class->stack]}, '==', 0, + " Starting with empty stack" ); + ok(!$Class->stack_as_string," Stringified stack empty" ); +} diff --git a/lib/Log/Message/t/01_Log-Message-Config.t b/lib/Log/Message/t/01_Log-Message-Config.t new file mode 100644 index 0000000000..2f8a021d7e --- /dev/null +++ b/lib/Log/Message/t/01_Log-Message-Config.t @@ -0,0 +1,84 @@ +### Log::Message::Config test suite ### +BEGIN { + if( $ENV{PERL_CORE} ) { + chdir '../lib/Log/Message' if -d '../lib/Log/Message'; + unshift @INC, '../../..'; + } +} + +BEGIN { chdir 't' if -d 't' } + +use strict; +use lib qw[../lib conf]; +use Test::More tests => 6; +use File::Spec; +use File::Basename qw[dirname]; + +use_ok( 'Log::Message::Config' ) or diag "Config.pm not found. Dying", die; +use_ok( 'Log::Message' ) or diag "Module.pm not found. Dying", die; + +{ + my $default = { + private => undef, + verbose => 1, + tag => 'NONE', + level => 'log', + remove => 0, + chrono => 1, + }; + + my $log = Log::Message->new(); + + is_deeply( $default, $log->{CONFIG}, q[Config creation from default] ); +} + +{ + my $config = { + private => 1, + verbose => 1, + tag => 'TAG', + level => 'carp', + remove => 0, + chrono => 1, + }; + + my $log = Log::Message->new( %$config ); + + is_deeply( $config, $log->{CONFIG}, q[Config creation from options] ); +} + +{ + my $file = { + private => 1, + verbose => 0, + tag => 'SOME TAG', + level => 'carp', + remove => 1, + chrono => 0, + }; + + my $log = Log::Message->new( + config => File::Spec->catfile( qw|conf config_file| ) + ); + + is_deeply( $file, $log->{CONFIG}, q[Config creation from file] ); +} + +{ + + my $mixed = { + private => 1, + verbose => 0, + remove => 1, + chrono => 0, + tag => 'MIXED', + level => 'die', + }; + my $log = Log::Message->new( + config => File::Spec->catfile( qw|conf config_file| ), + tag => 'MIXED', + level => 'die', + ); + is_deeply( $mixed, $log->{CONFIG}, q[Config creation from file & options] ); +} + diff --git a/lib/Log/Message/t/02_Log-Message.t b/lib/Log/Message/t/02_Log-Message.t new file mode 100644 index 0000000000..b49c962033 --- /dev/null +++ b/lib/Log/Message/t/02_Log-Message.t @@ -0,0 +1,175 @@ +### Log::Message test suite ### +BEGIN { + if( $ENV{PERL_CORE} ) { + chdir '../lib/Log/Message' if -d '../lib/Log/Message'; + unshift @INC, '../../..'; + } +} + +BEGIN { chdir 't' if -d 't' } + + +use strict; +use lib qw[../lib to_load]; +use Test::More tests => 34; + +### use tests +for my $pkg ( qw[ Log::Message Log::Message::Config + Log::Message::Item Log::Message::Handlers] +) { + use_ok( $pkg ) or diag "'$pkg' not found. Dying"; +} + +### test global stack +{ + my $log = Log::Message->new( private => 0 ); + is( $log->{STACK}, $Log::Message::STACK, q[Using global stack] ); +} + +### test using private stack +{ + my $log = Log::Message->new( private => 1 ); + isnt( $log->{STACK}, $Log::Message::STACK, q[Using private stack] ); + + $log->store('foo'); $log->store('bar'); + + ### retrieval tests + { + my @list = $log->retrieve(); + + ok( @list == 2, q[Stored 2 messages] ); + } + + $log->store('zot'); $log->store('quux'); + + { + my @list = $log->retrieve( amount => 3 ); + + ok( @list == 3, q[Retrieving 3 messages] ); + } + + { + is( $log->first->message, 'foo', q[ Retrieving first message] ); + is( $log->final->message, 'quux', q[ Retrieving final message] ); + } + + { + package Log::Message::Handlers; + + sub test { return shift } + sub test2 { shift; return @_ } + + package main; + } + + $log->store( + message => 'baz', + tag => 'MY TAG', + level => 'test', + ); + + { + ok( $log->retrieve( message => qr/baz/ ), + q[ Retrieving based on message] ); + ok( $log->retrieve( tag => qr/TAG/ ), + q[ Retrieving based on tag] ); + ok( $log->retrieve( level => qr/test/ ), + q[ Retrieving based on level] ); + } + + my $item = $log->retrieve( chrono => 0 ); + + { + ok( $item, q[Retrieving item] ); + is( $item->parent, $log, q[ Item reference to parent] ); + is( $item->message, 'baz', q[ Item message stored] ); + is( $item->id, 4, q[ Item id stored] ); + is( $item->tag, 'MY TAG', q[ Item tag stored] ); + is( $item->level, 'test', q[ Item level stored] ); + } + + { + ### shortmess is very different from 5.6.1 => 5.8, so let's + ### just check that it is filled. + ok( $item->shortmess, q[Item shortmess stored] ); + like( $item->shortmess, qr/\w+/, + q[ Item shortmess stored properly] + ); + + ok( $item->longmess, q[Item longmess stored] ); + like( $item->longmess, qr/Log::Message::store/s, + q[ Item longmess stored properly] + ); + + my $t = scalar localtime; + $t =~ /(\w+ \w+ \d+)/; + + like( $item->when, qr/$1/, q[Item timestamp stored] ); + } + + { + my $i = $item->test; + my @a = $item->test2(1,2,3); + + is( $item, $i, q[Item handler check] ); + is_deeply( $item, $i, q[ Item handler deep check] ); + is_deeply( \@a, [1,2,3], q[ Item extra argument check] ); + } + + { + ok( $item->remove, q[Removing item from stack] ); + ok( (!grep{ $item eq $_ } $log->retrieve), + q[ Item removed from stack] ); + } + + { + $log->flush; + ok( @{$log->{STACK}} == 0, q[Flushing stack] ); + } +} + +### test errors +{ my $log = Log::Message->new( private => 1 ); + + + ### store errors + { ### dont make it print + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= "@_" }; + + my $rv = $log->store(); + ok( !$rv, q[Logging empty message failed] ); + like( $warnings, qr/message/, q[ Spotted the error] ); + } + + ### retrieve errors + { ### dont make it print + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= "@_" }; + + ### XXX whitebox test! + local $Params::Check::VERBOSE = 1; # so the warnings are emitted + + my $rv = $log->retrieve( frobnitz => $$ ); + ok( !$rv, q[Retrieval with bogus args] ); + like( $warnings, qr/not a valid key/, + qq[ Spotted the error] ); + } +} + + + + + + + + + + + + + + + + + diff --git a/lib/Log/Message/t/conf/LoadMe.pl b/lib/Log/Message/t/conf/LoadMe.pl new file mode 100644 index 0000000000..6912615643 --- /dev/null +++ b/lib/Log/Message/t/conf/LoadMe.pl @@ -0,0 +1 @@ +1;
\ No newline at end of file diff --git a/lib/Log/Message/t/conf/config_file b/lib/Log/Message/t/conf/config_file new file mode 100644 index 0000000000..645cbb1a64 --- /dev/null +++ b/lib/Log/Message/t/conf/config_file @@ -0,0 +1,30 @@ + # Below is a sample of a config file you could use
+
+ # comments are denoted by a single '#'
+ # use a shared stack, or have a private instance?
+ # if none provided, set to '0',
+ private = 1
+
+ # do not be verbose
+ verbose = 0
+
+ # default tag to set on new items
+ # if none provided, set to 'NONE'
+ tag = SOME TAG
+
+ # default level to handle items
+ # if none provided, set to 'log'
+ level = carp
+
+ # extra files to include
+ # if none provided, no files are auto included
+ include = LoadMe.pl
+
+ # automatically delete items
+ # when you retrieve them from the stack?
+ # if none provided, set to '0'
+ remove = 1
+
+ # retrieve errors in chronological order, or not?
+ # if none provided, set to '1'
+ chrono = 0
\ No newline at end of file |