Log-Handler-0.90/0000750000000000000000000000000013702611501012223 5ustar rootrootLog-Handler-0.90/README0000644000000000000000000007417713702611501013130 0ustar rootrootNAME Log::Handler - Log messages to several outputs. SYNOPSIS use Log::Handler; my $log = Log::Handler->new(); $log->add( file => { filename => "file.log", maxlevel => "debug", minlevel => "warning", } ); $log->warning("message"); Or use Log::Handler; my $log = Log::Handler->new( screen => { log_to => "STDOUT", maxlevel => "debug", minlevel => "debug", message_layout => "%T [%L] %m (%C)", }, screen => { log_to => "STDOUT", maxlevel => "info", minlevel => "notice", }, screen => { log_to => "STDERR", maxlevel => "warning", minlevel => "emergency", }, ); Or use Log::Handler; my $log = Log::Handler->new(); $log->config( config => "logger.conf" ); # and maybe later $log->reload( config => "logger.conf" ); Or # create a application wide logger package MyApp; use Log::Handler; my $log = Log::Handler->create_logger("myapp"); $log->add(screen => { maxlevel => "info" }); $log->info("info message"); # get logger with get_logger() package MyApp::Admin; use Log::Handler; my $log = Log::Handler->get_logger("myapp"); $log->info("info message from MyApp::Admin"); DESCRIPTION The Log::Handler is a object oriented handler for logging, tracing and debugging. It is very easy to use and provides a simple interface for multiple output objects with lots of configuration parameters. You can easily filter the amount of logged information on a per-output base, define priorities, create patterns to format the messages and reload the complete logging machine. See the documentation for details. IMPORTANT NOTES Note that the default for option newline is now set to TRUE and newlines will be appended automatically to each message if no newline exists. A long time I thought about this serious change and have come to the decision to change it. The default for option mode from Log::Handler::Output::File is now append and not excl anymore. The methods reload() and validate() are new since version 0.62. I tested it with Screen.pm, File.pm and DBI.pm and it runs fine. If you find bugs then open a bug report please :-) LOG LEVELS There are eigth levels available: 7 debug 6 info 5 notice 4 warning, warn 3 error, err 2 critical, crit 1 alert 0 emergency, emerg debug is the highest and emergency is the lowest level. Level debug is the highest level because it basically says to log every peep. LOG LEVEL METHODS Level methods debug() info() notice() warning(), warn() error(), err() critical(), crit() alert() emergency(), emerg() The call of a log level method is very simple: $log->info("Hello World! How are you?"); Or maybe: $log->info("Hello World!", "How are you?"); Both calls would log - if level INFO is active: Feb 01 12:56:31 [INFO] Hello World! How are you? is_* methods is_debug() is_info() is_notice() is_warning(), is_warn() is_error(), is_err() is_critical(), is_crit() is_alert() is_emergency(), is_emerg() These twelve methods could be very useful if you want to kwow if the current level would log the message. All methods returns TRUE if the current set of minlevel and maxlevel would log the message and FALSE if not. SPECIAL LOG METHODS fatal, is_fatal trace dump die log For a full list take a look into the documentation of Log::Handler::Levels. METHODS new() Call new() to create a new log handler object. my $log = Log::Handler->new(); add() Call add() to add a new output object. The method expects 2 parts of options; the options for the handler and the options for the output module you want to use. The output modules got it's own documentation for all options. Example: use Log::Handler; my $log = Log::Handler->new(); $log->add( # Add "file output" file => { # handler options (see Log::Handler) timeformat => "%Y/%m/%d %H:%M:%S", message_layout => "%T [%L] %S: %m", maxlevel => "debug", minlevel => "emergency", die_on_errors => 1, debug_trace => 0, debug_mode => 2, debug_skip => 0, # file options (see Log::Handler::Output::File) filename => "file.log", filelock => 1, fileopen => 1, reopen => 1, autoflush => 1, permissions => "0660", utf8 => 1, } ); Take a look to Log::Handler::Examples for more examples. The following options are possible for the handler: maxlevel and minlevel With these options it's possible to set the log levels for your program. Example: maxlevel => "error" minlevel => "emergency" # or maxlevel => "err" minlevel => "emerg" # or maxlevel => 3 minlevel => 0 It's possible to set the log level as string or as number. The default setting for maxlevel is warning and the default setting for minlevel is emergency. Example: If maxlevel is set to warning and minlevel to emergency then the levels warning, error, critical, alert and emergency would be logged. You can set both to 8 or nothing if you want to disable the logging machine. timeformat The option timeformat is used to set the format for the placeholder %T. The string is converted with POSIX::strftime. The default format is set to "%b %d %H:%M:%S" and looks like Feb 01 12:56:31 If you would set the format to "%Y/%m/%d %H:%M:%S" it would looks like 2007/02/01 12:56:31 dateformat This options works like timeformat. You can set a format that is used for the placeholder %D. It's just useful if you want to split the date and time: $log->add(file => { filename => "file.log", dateformat => "%Y-%m-%d", timeformat => "%H:%M:%S", message_layout => "%D %T %L %m", }); $log->error("an error here"); This looks like 2007-02-01 12:56:31 ERROR an error here This option is not used by default. newline newline is a very helpful option. It let the logger appends a newline to the message if a newline doesn't exist. 0 - do nothing 1 - append a newline if not exist (default) Example: $log->add( screen => { newline => 1, maxlevel => "info", } ); $log->info("message\n"); $log->info("message"); In both cases the message would be logged with a newline at the end. message_layout With this option it's possible to create your own message layout with different placeholders in printf() style. The available placeholders are: %L Log level %T Time or full timestamp (option timeformat) %D Date (option dateformat) %P PID %H Hostname %U User name %G Group name %N Newline %S Program name %C Caller - filename and line number %p Caller - package name %f Caller - file name %l Caller - line number %s Caller - subroutine name %r Runtime in seconds since program start %t Time measurement - replaced with the time since the last call of $log->$level %m Message %% Percent The default message layout is set to "%T [%L] %m". As example the following code $log->alert("foo bar"); would log Feb 01 12:56:31 [ALERT] foo bar If you set message_layout to message_layout => "%T foo %L bar %m (%C)" and call $log->info("baz"); then it would log Feb 01 12:56:31 foo INFO bar baz (script.pl, line 40) Traces will be appended after the complete message. You can create your own placeholders with the method set_pattern(). message_pattern This option is just useful if you want to forward messages to output modules that needs the parts of a message as a hash reference - as example Log::Handler::Output::Forward, Log::Handler::Output::DBI or Log::Handler::Output::Screen. The option expects a list of placeholders: # as a array reference message_pattern => [ qw/%T %L %H %m/ ] # or as a string message_pattern => "%T %L %H %m" The patterns will be replaced with real names as hash keys. %L level %T time %D date %P pid %H hostname %U user %G group %N newline %r runtime %C caller %p package %f filename %l line %s subroutine %S progname %t mtime %m message Here a full code example: use Log::Handler; my $log = Log::Handler->new(); $log->add(forward => { forward_to => \&my_func, message_pattern => [ qw/%T %L %H %m/ ], message_layout => "%m", maxlevel => "info", }); $log->info("a forwarded message"); # now you can access it sub my_func { my $msg = shift; print "Timestamp: $msg->{time}\n"; print "Level: $msg->{level}\n"; print "Hostname: $msg->{hostname}\n"; print "Message: $msg->{message}\n"; } prepare_message prepare_message is useful if you want to do something with the message before it will be logged... maybe you want to create your own layout because message_layout doesn't meet your claim. $log->add( screen => { newline => 1, message_layout => "%m (%t)", message_pattern => [ qw/%T %L %H %m/ ], prepare_message => \&format, } ); $log->error("foo"); $log->error("bar"); $log->error("baz"); sub format { my $m = shift; $m->{message} = sprintf("%-20s %-20s %-20s %s", $m->{time}, $m->{level}, $m->{hostname}, $m->{message}); } The output looks like Mar 08 15:14:20 ERROR h1434036 foo (0.039694) Mar 08 15:14:20 ERROR h1434036 bar (0.000510) Mar 08 15:14:20 ERROR h1434036 baz (0.000274) priority With this option you can set the priority of your output objects. This means that messages will be logged at first to the outputs with a higher priority. If this option is not set then the default priority begins with 10 and will be increased +1 with each output. Example: We add a output with no priority $log->add(file => { filename => "file1.log" }); This output gets the priority of 10. Now we add another output $log->add(file => { filename => "file2.log" }); This output gets the priority of 11... and so on. Messages would be logged at first to the output with the priority of 10 and then to the output with the priority of 11. Now you can add another output and set the priority to 1. $log->add(screen => { dump => 1, priority => 1 }); Messages would be logged now at first to the screen. die_on_errors Set die_on_errors to 0 if you don't want that the handler dies on failed write operations. 0 - to disable it 1 - to enable it If you set die_on_errors to 0 then you have to control it yourself. $log->info("info message") or die $log->errstr(); # or Log::Handler->errstr() # or Log::Handler::errstr() # or $Log::Handler::ERRSTR remove_on_reload This option is set to 1 by default. Take a look to the description of the method reload for more information about this option. filter_message With this option it's possible to set a filter. If the filter is set then only messages will be logged that match the filter. You can pass a regexp, a code reference or a simple string. Example: $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => qr/log this/, # or # filter_message => "log this", # filter_message => '^log only this$', }); $log->info("log this"); $log->info("but not that"); If you pass your own code then you have to check the message yourself. $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => \&my_filter }); # return TRUE if you want to log the message, FALSE if not sub my_filter { my $msg = shift; $msg->{message} =~ /your filter/; } It's also possible to define a simple condition with matches. Just pass a hash reference with the options matchN and condition. Example: $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => { match1 => "log this", match2 => qr/with that/, match3 => "(?:or this|or that)", condition => "(match1 && match2) || match3", } }); NOTE that re-eval in regexes is not valid! Something like match1 => '(?{unlink("file.txt")})' would cause an error! skip_message This is the opposite of option filter_message, but it's only possible to set a simple string or regular expression. $log->add(file => { filename => "file.log", maxlevel => 6, skip => '^do not log this.+$' }); category The parameter category works like filter_caller but is much easier to configure. You can set a comma separated list of modules. As example if you would set the category to category => "MyApp::User" then all messages of MyApp::User and the submodules would be logged. Example: my $log = Log::Handler->new(); $log->add( screen => { maxlevel => "info", category => "MyApp::User, MyApp::Session" } ); package MyApp; $log->info(__PACKAGE__); package MyApp::Products; $log->info(__PACKAGE__); package MyApp::User; $log->info(__PACKAGE__); package MyApp::Users; $log->info(__PACKAGE__); package MyApp::User::Settings; $log->info(__PACKAGE__); package MyApp::Session; $log->info(__PACKAGE__); package MyApp::Session::Settings; $log->info(__PACKAGE__); The messages of MyApp and MyApp::Products would not be logged. The usage of categories is much faster than to filter by caller. filter_caller You can use this option to set a package name. Only messages from this packages will be logged. Example: my $log = Log::Handler->new(); $log->add(screen => { maxlevel => "info", filter_caller => qr/^Foo::Bar\z/, # or # filter_caller => "^Foo::Bar\z", }); package Foo::Bar; $log->info("log this"); package Foo::Baz; $log->info("but not that"); 1; This would only log the message from the package Foo::Bar. except_caller This option is just the opposite of filter_caller. If you want to log messages from all callers but Foo::Bar: except_caller => qr/^Foo::Bar\z/ alias You can set an alias if you want to get the output object later. Example: my $log = Log::Handler->new(); $log->add(screen => { maxlevel => 7, alias => "screen-out", }); my $screen = $log->output("screen-out"); $screen->log(message => "foo"); # or in one step $log->output("screen-out")->log(message => "foo"); debug_trace You can activate a debugger that writes caller() information about each active log level. The debugger is logging all defined values except hints and bitmask. Set debug_trace to 1 to activate the debugger. The debugger is set to 0 by default. debug_mode There are two debug modes: line(1) and block(2) mode. The default mode is 1. The line mode looks like this: use strict; use warnings; use Log::Handler; my $log = Log::Handler->new() $log->add(file => { filename => "*STDOUT", maxlevel => "debug", debug_trace => 1, debug_mode => 1 }); sub test1 { $log->warning() } sub test2 { &test1; } &test2; Output: Apr 26 12:54:11 [WARNING] CALL(4): package(main) filename(./trace.pl) line(15) subroutine(main::test2) hasargs(0) CALL(3): package(main) filename(./trace.pl) line(13) subroutine(main::test1) hasargs(0) CALL(2): package(main) filename(./trace.pl) line(12) subroutine(Log::Handler::__ANON__) hasargs(1) CALL(1): package(Log::Handler) filename(/usr/local/share/perl/5.8.8/Log/Handler.pm) line(713) subroutine(Log::Handler::_write) hasargs(1) CALL(0): package(Log::Handler) filename(/usr/local/share/perl/5.8.8/Log/Handler.pm) line(1022) subroutine(Devel::Backtrace::new) hasargs(1) wantarray(0) The same code example but the debugger in block mode would looks like this: debug_mode => 2 Output: Apr 26 12:52:17 [DEBUG] CALL(4): package main filename ./trace.pl line 15 subroutine main::test2 hasargs 0 CALL(3): package main filename ./trace.pl line 13 subroutine main::test1 hasargs 0 CALL(2): package main filename ./trace.pl line 12 subroutine Log::Handler::__ANON__ hasargs 1 CALL(1): package Log::Handler filename /usr/local/share/perl/5.8.8/Log/Handler.pm line 681 subroutine Log::Handler::_write hasargs 1 CALL(0): package Log::Handler filename /usr/local/share/perl/5.8.8/Log/Handler.pm line 990 subroutine Devel::Backtrace::new hasargs 1 wantarray 0 debug_skip This option let skip the caller() information the count of debug_skip. output() Call output($alias) to get the output object that you added with the option alias. It's possible to access a output directly: $log->output($alias)->log(message => "booo"); For more information take a look to the option alias. flush() Call flush() if you want to send flush to all outputs that can flush. Flush means to flush buffers and/or close and re-open outputs. If you want to send it only to some outputs you can pass the aliases. $log->flush(); # flush all $log->flush("foo", "bar"); # flush only foo and bar If option "die_on_errors" is set to 0 then you can intercept errors with: $log->flush or die $log->errstr; errstr() Call errstr() if you want to get the last error message. This is useful if you set die_on_errors to 0 and the handler wouldn't die on failed write operations. use Log::Handler; my $log = Log::Handler->new(); $log->add(file => { filename => "file.log", maxlevel => "info", die_on_errors => 0, }); $log->info("Hello World!") or die $log->errstr; Or unless ( $log->info("Hello World!") ) { $error_string = $log->errstr; # do something with $error_string } The exception is that the handler dies in any case if the call of new() or add() fails because on missing or wrong settings! config() With this method it's possible to load your output configuration from a file. $log->config(config => "file.conf"); Or $log->config(config => { file => [ { alias => "error_log", filename => "error.log", maxlevel => "warning", minlevel => "emerg", priority => 1 }, { alias => "common_log", filename => "common.log", maxlevel => "info", minlevel => "emerg", priority => 2 }, ], screen => { alias => "screen", maxlevel => "debug", minlevel => "emerg", log_to => "STDERR", }, }); The key "default" is used here to define default parameters for all file outputs. All other keys (error_log, common_log) are used as aliases. Take a look into the documentation of Log::Handler::Config for more information. reload() With the method reload() it's possible to reload the logging machine. Just pass the complete new configuration for all outputs, it works exaclty like config(). At first you should know that it's highly recommended to set a alias for each output. If you don't set a alias then the logger doesn't know which output-objects to reload. If a output-objects doesn't have a alias then the objects will be removed and the new configuration will be added. Example: logger.conf alias = debug filename = debug.log maxlevel = debug minlevel = emerg alias = common filename = common.log maxlevel = info minlevel = emerg Load the configuration $log->config(config => "logger.conf"); Now change the configuration in logger.conf alias = common filename = common.log maxlevel = notice minlevel = emerg alias = sendmail from = bar@foo.example to = foo@bar.example subject = your subject What happends now... The file-output with the alias debug will be removed, the file-output with the alias common will be reloaded and the output with the alias sendmail will be added. If you don't want that output-objects will be removed because they were added internal, then you can set the option remove_on_reload to 0. Example: $log->config(config => "logger.conf"); $log->add( forward => { forward_to => \&my_func, remove_on_reload => 0, } ); The forward-output is not removed after a reload. validate() The method validate() expects the same arguments like config() and reload(). Maybe you want to validate your options before you pass them to config() or reload(). Example: my $log = Log::Handler->new(); $log->config( config => \%config ); # and maybe later if ( $log->validate( config => \%new_config ) ) { $log->reload( config => \%new_config ); } else { warn "unable to reload configuration"; warn $log->errstr; } set_pattern() With this option you can set your own placeholders. Example: $log->set_pattern("%X", "key_name", sub { "value" }); # or $log->set_pattern("%X", "key_name", "value"); Then you can use this pattern in your message layout: $log->add(file => { filename => "file.log", message_layout => "%X %m%N", }); Or use it with message_pattern: sub func { my $m = shift; print "$m->{key_name} $m->{message}\n"; } $log->add(forward => { forward_to => \&func, message_pattern => "%X %m", }); Note: valid character for the key name are: [%\w\-\.]+ set_level() With this method it's possible to change the log level at runtime. To change the log level it's necessary to use a alias - see option alias. $log->set_level( $alias => { # option alias minlevel => $new_minlevel, maxlevel => $new_maxlevel, } ); set_default_param() With this methods it's possible to overwrite the default settings for new outputs. Normally you would do something like $log->add( file => { filename => "debug.log", maxlevel => "info", timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" } ); $log->add( file => { filename => "error.log", maxlevel => "error", timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" } ); Now you can simplify it with $log->set_default_param( timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" ); $logg->add( file => { filename => "debug.log", maxlevel => "info" } ); $log->add( file => { filename => "error.log", maxlevel => "error" } ); create_logger() create_logger() is the same like new() but it creates a global logger. my $log = Log::Handler->create_logger("myapp"); get_logger() With get_logger() it's possible to get a logger that was created with create_logger() or with use Log::Handler "myapp"; Just call my $log = Log::Handler->get_logger("myapp"); If the logger does not exists then a new logger will be created and returned. exists_logger() With exists_logger() it's possible to check if a logger exists and it returns TRUE or FALSE. EXAMPLES Log::Handler::Examples BENCHMARK The benchmark (examples/benchmark/benchmark.pl) runs on a Intel Core i7-920 with the following result: simple pattern output took : 1 wallclock secs ( 1.26 usr + 0.01 sys = 1.27 CPU) @ 78740.16/s (n=100000) default pattern output took : 2 wallclock secs ( 2.08 usr + 0.15 sys = 2.23 CPU) @ 44843.05/s (n=100000) complex pattern output took : 4 wallclock secs ( 3.22 usr + 0.23 sys = 3.45 CPU) @ 28985.51/s (n=100000) message pattern output took : 3 wallclock secs ( 2.72 usr + 0.16 sys = 2.88 CPU) @ 34722.22/s (n=100000) suppressed output took : 0 wallclock secs ( 0.08 usr + 0.00 sys = 0.08 CPU) @ 1250000.00/s (n=100000) filtered caller output took : 2 wallclock secs ( 2.10 usr + 0.68 sys = 2.78 CPU) @ 35971.22/s (n=100000) suppressed caller output took : 1 wallclock secs ( 0.54 usr + 0.00 sys = 0.54 CPU) @ 185185.19/s (n=100000) filtered messages output took : 3 wallclock secs ( 2.62 usr + 0.08 sys = 2.70 CPU) @ 37037.04/s (n=100000) EXTENSIONS Send me a mail if you have questions. PREREQUISITES Prerequisites for all modules: Carp Data::Dumper Fcntl Params::Validate POSIX Time::HiRes Sys::Hostname UNIVERSAL Recommended modules: Config::General Config::Properties DBI IO::Socket Net::SMTP YAML Just for the test suite: File::Spec Test::More EXPORTS No exports. REPORT BUGS Please report all bugs to . AUTHOR Jonny Schulz . QUESTIONS Do you have any questions or ideas? MAIL: IRC: irc.perl.org#perl If you send me a mail then add Log::Handler into the subject. COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Log-Handler-0.90/Makefile.PL0000644000000000000000000000124013702611501014177 0ustar rootroot# Note: this file was auto-generated by Module::Build::Compat version 0.4224 use ExtUtils::MakeMaker; WriteMakefile ( 'INSTALLDIRS' => 'site', 'EXE_FILES' => [], 'PL_FILES' => {}, 'NAME' => 'Log::Handler', 'VERSION_FROM' => 'lib/Log/Handler.pm', 'PREREQ_PM' => { 'Test::More' => 0, 'Params::Validate' => 0, 'Sys::Hostname' => 0, 'Data::Dumper' => 0, 'File::Spec' => 0, 'Carp' => 0, 'Time::HiRes' => 0, 'Fcntl' => 0, 'UNIVERSAL' => 0, 'POSIX' => 0 } ) ; Log-Handler-0.90/ChangeLog0000640000000000000000000005073513702611430014011 0ustar rootroot0.90 Released at 2020-07-12. - Fixed call of Sys::Hostname::hostname. 0.88 Released at 2016-08-08. - Just fixed meta data. 0.87 Released at 2015-06-16. - Implemented new option skip_message. 0.86 Released at 2015-06-12. - Quick fix for dateext and fileopen. 0.85 Released at 2015-06-12. - Added parameter dateext to Output::File. 0.84 Released at 2014-10-24. - Fixed some version conflicts. 0.83 Released at 2014-10-23. - Added method set_default_param. 0.82 Released at 2013-11-03. - Fixed the calls of _raise_error (RT #89989). 0.81 Released at 2013-11-01. - Added param utf-8 to Screen.pm and File.pm. 0.80 Released at 2013-10-04. - Fixed RT #89250 - dump() calls Dumper() now only if the log level is active. 0.79 Released at 2013-09-06. - Added option "category". 0.78 Released at 2013-05-16. - Fixed bug in validate->reload for default configs (RT #85346). 0.77 Released at 2013-05-15. - Just fixed Pod::Coverage testing in 002-pod-coverage.t. 0.76 Released at 2012-11-19. - Fixed dbi_handle in DBI.pm (RT #81155). 0.75 Released at 2012-03-09. - Quick fix and replaced the "defined or" operator // with || in Pattern.pm, line 101 for backward compability with Perl < 5.10. Thanks to all CPAN smoker for the fix test reports! 0.74 Released at 2012-03-07. - Removed "main" from _get_c_sub if caller returns undef. Sorry, that was just for debugging :/ - 3 releases at one day... GRML 0.73 Released at 2012-03-07. - Improved _get_c_sub in Pattern.pm (RT #75596). 0.72 Released at 2012-03-07. - Some code improvements to tune Log::Handler. Each value that is passed to set_pattern and is not code will be embedded into sub{}. - Add option utf8 to Screen.pm (RT #75593). 0.71 Released at 2011-02-10. - Fixed RT#65515 and added dateformat to _split_options(). 0.70 Released at 2011-01-07. - Added exists_logger(). - get_logger() doesn't croak any more if a logger doesn't exists. 0.69 Released at 2010-12-11. - Just a full release. 0.68_01 Released at 2010-12-07. - Added option dbi_handle to DBI.pm. - Bug fix RT #63687. 0.68 Released at 2010-11-24. - Fixed a bug in add(). It wasn't possible to pass more than one output configuration to add(). 0.67 Released at 2010-10-10. - Fixed a bug in set_level. The new level was set correctly but no message was logged because the output wasn't added to the $self->{levels} array. 0.66 Released at 2010-09-27. - Roled back again to 0.65 for different reasons. - Fixed some spelling error in POD (RT #60005). - It's not possible any more to create or get more than one logger with get_logger() and create_logger(). 0.65_04 Released at 2010-09-07. - Fixed a bug with filter_output in Log::Handler and Log::Handler::Output. 0.65_03 Released at 2010-08-27. - Added option filter (unused since 2008-07-25). - Added method filter(). 0.65_02 Released at 2010-08-16. - Rollbacked to 0.65 :-) - Fixed some spelling error in POD (RT #60005). - Added option category that works like filter_caller but it's nicer to configure. 0.65_01 Released at 2010-08-03. - Modified create_logger and get_logger and added the functionalety to create and fetch category loggers. - Fixed some spelling error in POD (RT #60005). 0.65 Released at 2010-08-02. - Modified Log::Handler::Output::DBI. Oracle is unable to handle "select 1". The statement is changed to "select 1 from dual". 0.64 Released at 2010-01-29. - Fixed a bug in Email.pm on line 256 - $string can be uninitialized. 0.63 Released at 2009-11-24. - Fixed a typo in Email.pm (RT #51745). - Added options cc and bcc to Email.pm - this was a feature request. - It's now possible to pass the log level to log() of Sendmail.pm. 0.62 Released at 2009-11-06. - Some bug fixes for reload() but it should run now :-) 0.61_04 Released at 2009-11-04. - Fixed a little bug in Sendmail.pm - the tests returns an error if no /usr/sbin/sendmail is found. - Some code improvements in Handler.pm. 0.61_03 Released at 2009-11-01. - Added a validate() functionality. It's really useable to validate() before reload(). 0.61_02 Released at 2009-11-01. - Fixed a bug in Email.pm - $options -> $opts. - Made some code improvements in Log::Handler. - Added reload() to the test suite. - Added UNIVERSAL to the dependencies. 0.61_01 Released at 2009-10-31. - Added a reload functionality to Log::Handler and all output-modules. 0.60 Released at 2009-10-23. - File.pm: "append" is now the default for option "mode" - Full release. 0.59_02 Released at 2009-10-17. - Default for option newline is now 1. 0.59_01 Released at 2009-10-11. - Kicked deprecated module Log::Handler::Simple. - Kicked _close, _lock, _unlock in File.pm and moved the functionalety into log() and close(). - Kicked deprecated option "reconnect" from DBI.pm. - Added Log::Handler::Output::Sendmail. 0.58 Released at 2009-10-07. - Forget to kick Devel::Backtrace from Log::Handler::Simple. - Log::Handler::Simple will be kicked in the next release. 0.57 Released at 2009-10-06. - Kicked UNIVERSAL::require. - Kicked Devel::Backtrace. 0.56 Released at 2009-06-06. - Just a full version. 0.55_01 Released at 2009-06-05. - Oops... there was no _raise_error routine in Log::Handler::Output::Screen. - Fixed a bug in Handler.pm - the hash reference that were passed to add() were changed (RT #46631). 0.54 Released at 2009-05-27. - Just a full version. 0.53_01 Released at 2009-05-27. - Fixed a bug in Log::Handler::Output::DBI - it was unable to create a valid dsn for sqlite (RT #46407). - Added option dbname - Added option data_source 0.52 Released at 2009-05-24. - No changes, just a full version. 0.51_01 Released at 2009-05-22. - Added method set_level() to Handler.pm to change the log level at runtime. 0.51 Released at 2009-03-07. - Just a full release. 0.50_01 Released at 2009-03-07. - Fixed a bug in the output DBI.pm - if the connection to the database was lost then the message lost as well even if a reconnect was successful. - Added option prepare_message. - Fixed message_pattern - the formatted messages was overwritten if message_pattern was set. - Option reconnect from Log::Handler::Output::DBI is deprecated. 0.50 Released at 2008-11-27. - Added the functionality to create a application logger. New functions are create_logger and get_logger. - Added option expect_caller, what is the opposite of option filter_caller. 0.49 Released at 2008-11-16. - Added patterns %U and %G (user, group). - Fixed a bug in Socket.pm. If the server gone then Log::Handler croaks even if die_on_errors is disabled. - Fixed a bug in Output.pm. $log->error(0) logs nothing. $log->error('foo', undef, 'bar') caused a warning. 0.48 Released at 2008-10-28. - Fixed a bug in Email.pm - no error message if a email couldn't be send. - Added Email::Date to send the date with a email. - EMAIL: if the key $message->{level} exists then the level is used in the subject: "$level: $subject". The level can passed with the option message_pattern. 0.47 Released at 2008-09-04. - Add new config features. Now it's possilbe to add the configuration for the outputs as a array reference. - Add method log() to log with the level as first argument: log($level=>$msg). - Kicked Changes.pm. Not needed any more. 0.46 Released at 2008-07-28. - Fixed Plugin::YAML. It was created as Plugin::Config::YAML. - Did some code/example improvements. 0.45 Released at 2008-07-25. - Kicked $self->{caller_level} and replaced it with Log::Handler::CALLER_LEVEL. The reason is that if dump(), die() or warn() was called then the patterns %p, %f, %c or %s was wrong. - Changed option filter to filter_message and added a new option called filter_caller. 0.44 Released at 2008-06-04. - Fixed set_pattern(). It dies if the key name is something like 'x-name' because $m->{x-name} is not valid. - Changed pattern %R to %r. 0.43 Released at 2008-05-21. - Fixed log() in DBI.pm and Socket.pm - only try to reconnect if persistent + reconnect is set to true. Sorry :( 0.42 Released at 2008-05-21. - Added $|=1 to Screen.pm. - warn() is now a shortcut for warning(). - Add flush() to Handler.pm, Output.pm and File.pm. - Added Perl version 5.6.1 to Build.PL (use warnings & utf8). - Added a licence file. - Fixed reconnect in DBI.pm. - Fixed a lot of POD typos. 0.41 Released at 2008-05-09. - Messages will be send to all outputs, even if a output is not able to log a message. In version 0.40 the handler stopped if a message couldn't be logged. That was bad. - Did a lot code, POD and internal doc improvements. 0.40 Released at 2008-05-04. - A full release - finally :-) - Kicked all _and_trace and _and_die methods. Replaced them with $log->die and $log->trace. - Did some code and POD improvements. 0.39_17 Released at 2008-04-29. - Kicked all _and_croak methods and carp() - to bloated. - message_pattern and message_layout is now builded with eval. - The patterns are not static any more. Changes at runtime are possible with set_pattern(). - Fixed Log::Handler::Output::Email - forget to call sendmail() if buffer is set to 0. - Kicked option interval from Log::Handler::Output::Email. - All log() methods of each output expects now a hash or a hash reference with the message (message => $message). Before this change it was possible to pass a hash reference or a simple string: { message => $message } or just $message. - Some other little improvements: POD, Code, Examples 0.39_16 Released at 2008-04-17. - Kicked all _and_exit() methods - to bloated. - Added Log::Handler::Pattern. - Added Log::Handler::Output::Socket. - Added option filter and alias to the handler. - Added close() to Log::Handler::Output::File. - Added connect() and disconnect() to Log::Handler::Output::DBI. - Added connect() and disconnect() to Log::Handler::Output::Socket. 0.38_15 Released at 2008-03-06. - The old style of Log::Handler version 0.38 is now implemented as Log::Handler::Simple. - POD improved; added Changes.pod, Examples.pod. 0.38_14 Released at 2008-02-17. - Forget to delete some POD parts - sorry :/ 0.38_13 Released at 2008-02-17. - Kicked trace() and option "trace". - Added trace methods for each level. - Added croak, carp, die and warn methods for different levels. - Log::Handler::Levels is now the base class for all level methods. 0.38_12 Released at 2008-02-16. - Released with a lot of POD, code and example improvements. 0.38_11 Released at 2008-02-15. - Changed option message_keys to message_pattern. I hope that was the last change of this name :/ - Fixed POD typos and improved the documentation. 0.38_10 Released at 2008-02-13. - Added Log::Handler::Output::Screen. - Added option "priority" to the handler. 0.38_09 Released at 2008-02-09. - Fixed t/100-config.t. Config::General was loaded, but it's not in the prereq list. Kicked GLOBREF from the validate list for config filename in Log::Handler::Config. 0.38_08 Released at 2008-02-08. - Kicked eval { } from Log::Handler::Output::DBI. - Missed documentation for dbi_params in Log::Handler::Output::DBI. 0.38_07 Released at 2008-02-08. - Replaced options prefix and postfix with message_layout. - Added %m as message to placeholders. - Renamed option setinfo to message_keys. - Added Log::Handler::Output::DBI. - A lot of other code improvements. - Moved the main logic from Log::Handler::Logger back to Log::Handler. - Renamed Log::Handler::Logger to Log::Handler::Output. This module builds the output message and is just for internal usage. - Renamed all output modules from Logger to Output. - Patterns are not global any more. Now the patterns are stored into the Log::Handler object. - Changed option debug to debug_trace. The reason is that the option debug can be used for output objects. 0.38_06 Released at 2008-01-20. - POD improved and a lot of POD typos fixed. - Fixed splitToTree argument for plugin Config::Properties. - Different code improvements. - Add option setinfo to Log::Handler::Logger. - The message is now handled with a hash ref intern. 0.38_05 Released at 2008-01-19. - Very annoyingly... wrong description for Log::Handler::Logger::Forward. 0.38_04 Released at 2008-01-19. - Fixed test for parameter 'filename' in t/03handler.t. - Fixed a lot of typos and improved the code. - Added Log::Handler::Logger::Forward. - Added Log::Handler::Logger::Email with Net::SMTP. - Added fatal() to Log::Handler. 0.38_03 Released at 2008-01-14. - Added Log::Handler::Config.pm. - Added plugins for Config::General, Config::Properties and YAML. - Fixed some POD typos. 0.38_02 Released at 2008-01-13. - Fixed test for 'mode' in t/03handler.t. - POD and intern documentation improved. - Fixed example examples/trace.pl. - Kicked method levels() from Log::Handler::Logger. - Added the option trace. With this option it's possible to deactivate the logging of trace messages to a logfile. 0.38_01 Released at 2008-01-13. - Added Log::Handler::Logger and moved the main logger logic to it. - Now it's possible to define more than one log file. Each log file got it's own Log::Handler::Logger object. - The simple call of "Log::Handler->new()" will not create a default output object for *STDOUT any more, it just creates an empty Log::Handler object. - To add log file the method add() should be used. The first log file can be defined by new(). - The methods close(), set_prefix(), get_prefix are not available any more. - The placeholder <--LEVEL--> for the prefix is changed to %L. In addition there are different other placeholders available and it's possible to define a postfix. - trace() will trace caller informations to all log files that are defined. - Did a lot of other code changes. 0.38 Released at 2007-09-29. - Kicked set_log_buffer() and get_buffer_log(). - Did some code improvements and split _print() into different routines. - The option filename isn't mandatory any more. The default is *STDOUT. 0.37_01 Released at 2007-07-20. - Added set_buffer_log() and get_buffer_log() to Log::Handler. - Kicked now the would_log_* methods. Since 0.33 the is_* methods exists as replacement. 0.37 Released at 2007-07-04. - Added option rewrite_to_stderr. - Replaced syswrite to print in _print(). 0.36 Released at 2007-06-15. - Now it's possible to set utf8 on the filehandle. - Kicked CLOSE(). - Kicked File::Stat because CORE::stat() is much faster and I did some other improvements. Now _print() is ~50% faster if reopen is used. 0.35 Released at 2007-06-05. - Added method trace() to prints caller() to the log file. - Did some code improvements and fixed POD typos. 0.34 Released at 2007-05-22. - Changed the regex /.*\z/ to /.\z|^\z/ to append a newline if not exists because /.*\z/ seems to be a regex bug. 0.33 Released at 2007-05-09. - Added 13 is_* methods. They do the same as the would_log_* methods. - Added close() as replacement for CLOSE(). CLOSE() exists in further releases but is deprecated. - Did some code improvements and fix some POD typos. - Kicked IO::Handle from PREREQS and autoflush myself on the file handle. 0.32 Released at 2007-04-26. I jumped from 0.15 to 0.32 because there exists brownfields on backpan. Replaced my own routine to get caller() informations with Devel::Backtrace. Added the option debugger_skip. 0.15 Released at 2007-04-25. - Fixed typos in POD. - Changed the way to activate the debugger. Now the debugger is activated over the call of new(). 0.14 Released at 2007-04-24. - Fixed "use Log::Handler debug => ...". 0.13 Released at 2007-04-20. - Added the availability to activate debugging with "Log::Handler debug => 1". 0.12 Released at 2007-04-15. - Fixed DESTROY(). DESTROY() tries to unlock $self->{fh} in any case. - Autoflush wasn't set if the option "filename" was set to STDOUT or STDERR or a GLOBREF. 0.11 Released at 2007-04-10 - Add the methods set_prefix() and get_prefix(). Did some POD changes. 0.11_02 Released at 2007-04-04 - Add the alternative to set "nothing" as option for minlevel and maxlevel. 0.11_01 Released at 2007-04-04 - I changed the log levels because they wasn't in the right order. 0 debug is now 7 1 info is now 6 2 notice is now 5 3 warning is now 4 4 error is now 3 5 crit is now 2 6 alert is now 1 7 emergency is now 0 8 nothing is still the same If you set maxlevel and minlevel as strings in your code than you don't need to change your code, but if you used numbers than you must change it! BIG sorry to all users that have to re-write code in this case. - Thanks to betterworld for his tipps about the log levels! (thx pepe ;-)) 0.10 Released at 2007-04-04 - Add the alternative to set STDOUT and STDERR as a string with the option "filename". 0.09 Released at 2007-03-01. - Fixed t/03handler.t. The test log file was in unix format t/Log-Handler-Test-File.log, now it use File::Spec catfile(). - Forget to CLOSE() the log file in t/03handler.t and test fails on Windows. 0.08 Released at 2007-02-25. - Now it's possible to hand off GLOBREF to option filename. There are some options that will be forced automatical: - fileopen => 1 - filelock => 0 - reopen => 0 0.07 Released at 2007-03-13. - Fixed t/03handler.t. 0.06 Released at 2007-03-11. - Fixed a bad typo in SYNOPSIS of POD. - Changed the description in NAME of POD. 0.05 Released at 2007-03-11. - Fixed some typos in the documentation. 0.04 Released at 2007-03-09. - Fixed code in new(). There was three typos. 0.03 Released at 2007-03-09. - Fixed t/03handler.t. - Added some points to the documentation. - Add an example for die_on_errors. - Add an example to call syslog methods. - Different code changes. - Fixed new(). Now it returns undef if open() fails. 0.02 Released at 2007-02-05. - Added eight new methods: would_log_debug() would_log_info() would_log_notice(), would_log_note() would_log_warning() would_log_error(), would_log_err() would_log_critical(), would_log_crit() would_log_alert() would_log_emergency(), would_log_emerg() - Changed the POD. 0.01 Released at 2007-02-04. x.xx Thanks to Larry Wall and all other Perl developers for Perl :-) Log-Handler-0.90/LICENSE0000640000000000000000000000260012235521676013244 0ustar rootrootCopyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Log-Handler-0.90/examples/0000750000000000000000000000000012235521676014056 5ustar rootrootLog-Handler-0.90/examples/logger/0000750000000000000000000000000012235521676015335 5ustar rootrootLog-Handler-0.90/examples/logger/MyApp.pm0000640000000000000000000000034012235521676016717 0ustar rootrootpackage MyApp; use strict; use warnings; use Log::Handler myapp => 'LOG'; sub foo { LOG->info('message from foo'); } sub bar { my $log = Log::Handler->get_logger('myapp'); $log->info('message from bar'); } 1; Log-Handler-0.90/examples/logger/myapp.pl0000640000000000000000000000143512235521676017024 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you example how you can use C. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler myapp => 'LOG'; use lib '.'; use MyApp; LOG->add(screen => { newline => 1, maxlevel => 'info', message_layout => '%L - %m', }); LOG->info('message from main'); MyApp->foo(); MyApp->bar(); Log-Handler-0.90/examples/reload/0000750000000000000000000000000012235521676015324 5ustar rootrootLog-Handler-0.90/examples/reload/logger1.conf0000640000000000000000000000063512235521676017540 0ustar rootroot alias = screen1 maxlevel = info minlevel = emerg priority = 2 message_layout = %L - screen1 %m alias = screen2 maxlevel = info minlevel = emerg priority = 1 message_layout = %L - screen2 %m alias = screen3 maxlevel = info minlevel = emerg priority = 3 message_layout = %L - screen3 %m Log-Handler-0.90/examples/reload/reload.pl0000640000000000000000000000053112235521676017127 0ustar rootroot#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Log::Handler; my $log = Log::Handler->new(); $log->config(config => "logger1.conf"); $log->warning("foo"); $log->info("foo"); print "--------------------------------------\n"; $log->reload(config => "logger2.conf") or die $log->errstr; $log->warning("bar"); $log->info("bar"); Log-Handler-0.90/examples/reload/logger2.conf0000640000000000000000000000043712235521676017541 0ustar rootroot alias = screen1 maxlevel = warning minlevel = emerg priority = 2 message_layout = %T [%L] screen1 %m alias = screen3 maxlevel = warning minlevel = emerg priority = 1 message_layout = %T [%L] screen3 %m Log-Handler-0.90/examples/filter/0000750000000000000000000000000012235521676015343 5ustar rootrootLog-Handler-0.90/examples/filter/filter_message.pl0000640000000000000000000000143712235521676020677 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples how you can filter messages. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { maxlevel => 'info', newline => 1, filter_message => 'log this', } ); $log->info('log this'); $log->info('not that'); Log-Handler-0.90/examples/filter/filter_caller.pl0000640000000000000000000000235612235521676020516 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples how you can filter messages from different callers. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { maxlevel => 'info', newline => 1, message_layout => '%L - (filter:foo) %m', filter_caller => 'foo', } ); $log->add( screen => { maxlevel => 'info', newline => 1, message_layout => '%L - (filter:bar) %m', filter_caller => 'bar', } ); $log->add( screen => { maxlevel => 'info', newline => 1, message_layout => '%L - (except:baz) %m', except_caller => 'baz', } ); package foo; $log->info('foo'); package bar; $log->info('bar'); package baz; $log->info('baz'); 1; Log-Handler-0.90/examples/filter/extended_filter.pl0000640000000000000000000000202112235521676021041 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples how you can filter messages. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { maxlevel => 'info', newline => 1, filter_message => { match1 => 'log this', match2 => qr/with that/, match3 => '(?:or this|or that)', condition => '(match1 && match2) || match3', } } ); $log->info('log this with that'); $log->info('or this'); $log->info('or that'); $log->info('but not that'); Log-Handler-0.90/examples/layout/0000750000000000000000000000000012235521676015373 5ustar rootrootLog-Handler-0.90/examples/layout/layout.pl0000640000000000000000000000236112235521676017250 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples for all patterns. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->set_pattern('%x', 'x-name', 'x-value'); $log->add( screen => { message_layout => 'level %L%N'. 'time %T%N'. 'date %D%N'. 'pid %P%N'. 'hostname %H%N'. 'caller %C%N'. 'package %p%N'. 'filename %f%N'. 'line %l%N'. 'subroutine %s%N'. 'progname %S%N'. 'runtime %r%N'. 'mtime %t%N'. 'message %m%N'. 'procent %%%N'. 'x-name %x%N', } ); $log->error('your message'); Log-Handler-0.90/examples/layout/pattern.pl0000640000000000000000000000203612235521676017407 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples for all patterns. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; use Data::Dumper; my $log = Log::Handler->new(); $log->add(forward => { forward_to => \&my_func, message_pattern => [ qw/%T %L %H/ ], message_layout => '', maxlevel => 'info', }); $log->info('a forwarded message'); # now you can access it sub my_func { my $msg = shift; print "Timestamp: $msg->{time}\n"; print "Level: $msg->{level}\n"; print "Hostname: $msg->{hostname}\n"; print "Message: $msg->{message}\n"; } Log-Handler-0.90/examples/layout/runtime.pl0000640000000000000000000000145612235521676017422 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you an example for the runtime patterns. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { maxlevel => 'debug', message_layout => '%T %L %m runtime: %t, total runtime: %r%N', } ); while ( 1 ) { $log->debug('--->'); sleep 1; } Log-Handler-0.90/examples/text-csv/0000750000000000000000000000000012235521676015633 5ustar rootrootLog-Handler-0.90/examples/text-csv/log-as-csv-string.pl0000640000000000000000000000105012235521676021444 0ustar rootroot#!/usr/bin/perl use strict; use warnings; use Log::Handler; use Text::CSV; my $log = Log::Handler->new(); my $csv = Text::CSV->new(); $log->add( screen => { maxlevel => 'info', newline => 1, message_layout => '%m', message_pattern => '%T %L %P %t', prepare_message => sub { my $m = shift; $csv->combine(@{$m}{qw/time level pid mtime message/}); $m->{message} = $csv->string; }, } ); $log->info('foo'); $log->info('bar'); $log->info('baz'); Log-Handler-0.90/examples/socket/0000750000000000000000000000000012235521676015346 5ustar rootrootLog-Handler-0.90/examples/socket/client.pl0000640000000000000000000000201312235521676017156 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This is a client example for Log::Handler::Output::Socket. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( socket => { peeraddr => '127.0.0.1', peerport => 44444, newline => 1, maxlevel => 'info', die_on_errors => 0, message_layout => '%T [%L] %U %H %S[%P] %m', } ); my $err = Log::Handler->new(); $err->add(screen => { newline => 1 }); while ( 1 ) { $log->info('test') or warn "unable to send message: ", $log->errstr; sleep 1; } Log-Handler-0.90/examples/socket/server.pl0000640000000000000000000000213312235521676017211 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This is a server example for Log::Handler::Output::Socket. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use IO::Socket::INET; use Log::Handler::Output::File; my $sock = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => 44444, Listen => 1, ) or die $!; my $file = Log::Handler::Output::File->new( filename => 'server.log', mode => 'append', fileopen => 1, reopen => 1, ); while ( 1 ) { $file->log(message => "waiting for next connection\n"); while (my $request = $sock->accept) { while (my $message = <$request>) { $file->log(message => $message); } } } Log-Handler-0.90/examples/prepare/0000750000000000000000000000000012235521676015514 5ustar rootrootLog-Handler-0.90/examples/prepare/prepare.pl0000640000000000000000000000203312235521676017506 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION This script shows you examples for all patterns. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; use Data::Dumper; my $log = Log::Handler->new(); $log->add( screen => { newline => 1, message_layout => '%m (%t)', message_pattern => [ qw/%T %L %H %m/ ], prepare_message => \&format, } ); $log->error("foo bar baz"); $log->error("foo bar baz"); $log->error("foo bar baz"); sub format { my $m = shift; $m->{message} = sprintf('%-20s %-20s %-20s %s', $m->{time}, $m->{level}, $m->{hostname}, $m->{message}); } Log-Handler-0.90/examples/config/0000750000000000000000000000000012235521676015323 5ustar rootrootLog-Handler-0.90/examples/config/example.props0000640000000000000000000000020112235521676020035 0ustar rootrootscreen.maxlevel = debug screen.minlevel = emerg screen.newline = 1 screen.message_layout = %T %H[%P] [%L] Config::Properties %m Log-Handler-0.90/examples/config/example.pl0000640000000000000000000000141512235521676017315 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION With this script you can test if the example configurations works. =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->config(config => 'example.conf'); $log->config(config => 'example.yaml'); $log->config(config => 'example.props'); $log->info('info message'); Log-Handler-0.90/examples/config/example.conf0000640000000000000000000000020712235521676017625 0ustar rootroot maxlevel = debug minlevel = emerg newline = 1 message_layout = %T %H[%P] [%L] Config::General - %m Log-Handler-0.90/examples/config/example.yaml0000640000000000000000000000015012235521676017637 0ustar rootroot--- screen: maxlevel: debug minlevel: emerg newline: 1 message_layout: %T %H[%P] [%L] YAML - %m Log-Handler-0.90/examples/category/0000750000000000000000000000000012235521676015673 5ustar rootrootLog-Handler-0.90/examples/category/category.pl0000640000000000000000000000052012235521676020043 0ustar rootroot#!/usr/bin/perl use strict; use warnings; use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { maxlevel => "info", category => "Foo" } ); $log->info("Hello World!"); package Foo; $log->info(__PACKAGE__); package Foo::Bar; $log->info(__PACKAGE__); package Foooo; $log->info(__PACKAGE__); Log-Handler-0.90/examples/benchmark/0000750000000000000000000000000012442404265016002 5ustar rootrootLog-Handler-0.90/examples/benchmark/benchmark.pl0000640000000000000000000001035012235521676020277 0ustar rootroot#!/usr/bin/perl =head1 AUTHOR Jonny Schulz =head1 DESCRIPTION Benchmarks... what else could I say... =head1 POWERED BY _ __ _____ _____ __ __ __ __ __ | |__| | | | \| |__|\ \/ / | . | | | | | | | | > < |____|__|_____|_____|__|\__|__|/__/\__\ =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use strict; use warnings; use Log::Handler; use Benchmark; sub buffer { } my $log1 = Log::Handler->new(); # simple pattern my $log2 = Log::Handler->new(); # default pattern & suppressed my $log3 = Log::Handler->new(); # complex pattern my $log4 = Log::Handler->new(); # message pattern my $log5 = Log::Handler->new(); # filtered caller my $log6 = Log::Handler->new(); # filtered message my $log7 = Log::Handler->new(); # categories $log1->add( forward => { alias => 'simple pattern', maxlevel => 'notice', minlevel => 'notice', forward_to => \&buffer, message_layout => '%L - %m', } ); $log2->add( forward => { alias => 'default pattern & suppressed', maxlevel => 'warning', minlevel => 'warning', forward_to => \&buffer, } ); $log3->add( forward => { alias => 'complex pattern', maxlevel => 'info', minlevel => 'info', forward_to => \&buffer, message_layout => '%T [%L] %H(%P) %m (%C)%N', } ); $log4->add( forward => { alias => 'message pattern', maxlevel => 'error', minlevel => 'error', forward_to => \&buffer, message_layout => '%m', message_pattern => [qw/%T %L %P/], } ); $log5->add( forward => { alias => 'filtered caller', maxlevel => 'emerg', minlevel => 'emerg', forward_to => \&buffer, filter_caller => qr/^Foo\z/, } ); $log5->add( forward => { alias => 'filtered caller', maxlevel => 'emerg', minlevel => 'emerg', forward_to => \&buffer, filter_caller => qr/^Bar\z/, } ); $log5->add( forward => { alias => 'filtered caller', maxlevel => 'emerg', minlevel => 'emerg', forward_to => \&buffer, filter_caller => qr/^Baz\z/, } ); $log6->add( forward => { alias => 'filtered message', maxlevel => 'alert', minlevel => 'alert', forward_to => \&buffer, filter_message => qr/bar/, } ); $log6->add( forward => { alias => 'filtered message', maxlevel => 'alert', minlevel => 'alert', forward_to => \&buffer, filter_message => qr/bar/, } ); $log7->add( forward => { alias => 'categories', maxlevel => 'alert', minlevel => 'alert', forward_to => \&buffer, category => "Cat::Foo", } ); my $count = 100_000; my $message = 'foo bar baz'; run("simple pattern output took", $count, sub { $log1->notice($message) } ); run("default pattern output took", $count, sub { $log2->warning($message) } ); run("complex pattern output took", $count, sub { $log3->info($message) } ); run("message pattern output took", $count, sub { $log4->error($message) } ); run("suppressed output took", $count, sub { $log2->debug($message) } ); run("filtered caller output took", $count, \&Foo::emerg ); run("suppressed caller output took", $count, \&Foo::Bar::emerg ); run("filtered messages output took", $count, sub { $log6->alert($message) } ); run("categorized messages output took", $count, \&Cat::Foo::Bar::alert ); run("suppressed categories output took", $count, \&Cat::Bar::Baz::alert ); sub run { my ($desc, $count, $bench) = @_; my $time = timeit($count, $bench); print sprintf('%-30s', $desc), ' : ', timestr($time), "\n"; } # Filter messages by caller package Foo; sub emerg { $log5->emerg($message) } # Suppressed messages by caller package Foo::Bar; sub emerg { $log5->emerg($message) } package Cat::Foo::Bar; sub alert { $log7->alert($message) } package Cat::Bar::Baz; sub alert { $log7->alert($message) } 1; Log-Handler-0.90/INSTALL0000640000000000000000000000052112235521676013270 0ustar rootrootTo install the module execute the following steps: perl Makefile.PL make make test "make test" do some tests. If you get error messages then you shouldn't install the module. Otherwise your last step is: make install and have a lot of fun with Log::Handler. Don't forget to take a look into the "examples" diretory. :-) Log-Handler-0.90/META.json0000644000000000000000000000630313702611501013653 0ustar rootroot{ "abstract" : "Log messages to several outputs.", "author" : [ "Jonny Schulz" ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4224", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Log-Handler", "prereqs" : { "configure" : { "requires" : { "Module::Build" : "0.42" } }, "runtime" : { "recommends" : { "Config::General" : "0", "Config::Properties" : "0", "DBI" : "0", "Email::Date" : "0", "IO::Socket" : "0", "Net::SMTP" : "0", "YAML" : "0" }, "requires" : { "Carp" : "0", "Data::Dumper" : "0", "Fcntl" : "0", "File::Spec" : "0", "POSIX" : "0", "Params::Validate" : "0", "Sys::Hostname" : "0", "Test::More" : "0", "Time::HiRes" : "0", "UNIVERSAL" : "0" } } }, "provides" : { "Log::Handler" : { "file" : "lib/Log/Handler.pm", "version" : "0.90" }, "Log::Handler::Config" : { "file" : "lib/Log/Handler/Config.pm", "version" : "0.09" }, "Log::Handler::Levels" : { "file" : "lib/Log/Handler/Levels.pm", "version" : "0.07" }, "Log::Handler::Output" : { "file" : "lib/Log/Handler/Output.pm", "version" : "0.10" }, "Log::Handler::Output::DBI" : { "file" : "lib/Log/Handler/Output/DBI.pm", "version" : "0.12" }, "Log::Handler::Output::Email" : { "file" : "lib/Log/Handler/Output/Email.pm", "version" : "0.08" }, "Log::Handler::Output::File" : { "file" : "lib/Log/Handler/Output/File.pm", "version" : "0.08" }, "Log::Handler::Output::Forward" : { "file" : "lib/Log/Handler/Output/Forward.pm", "version" : "0.03" }, "Log::Handler::Output::Screen" : { "file" : "lib/Log/Handler/Output/Screen.pm", "version" : "0.07" }, "Log::Handler::Output::Sendmail" : { "file" : "lib/Log/Handler/Output/Sendmail.pm", "version" : "0.07" }, "Log::Handler::Output::Socket" : { "file" : "lib/Log/Handler/Output/Socket.pm", "version" : "0.08" }, "Log::Handler::Pattern" : { "file" : "lib/Log/Handler/Pattern.pm", "version" : "0.08" }, "Log::Handler::Plugin::Config::General" : { "file" : "lib/Log/Handler/Plugin/Config/General.pm", "version" : "0.02" }, "Log::Handler::Plugin::Config::Properties" : { "file" : "lib/Log/Handler/Plugin/Config/Properties.pm", "version" : "0.03" }, "Log::Handler::Plugin::YAML" : { "file" : "lib/Log/Handler/Plugin/YAML.pm", "version" : "0.03" } }, "release_status" : "stable", "resources" : { "license" : [ "http://dev.perl.org/licenses/" ] }, "version" : "0.90", "x_serialization_backend" : "JSON::PP version 2.97001" } Log-Handler-0.90/MANIFEST.SKIP.bak0000644000000000000000000000041113702611501014656 0ustar rootroot#!include_default # Avoid configuration metadata file ^MYMETA\. # Avoid Module::Build generated and utility files. \bBuild$ \bBuild.bat$ \b_build \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ ^MANIFEST\.SKIP # Avoid archives of this distribution \bLog-Handler-[\d\.\_]+ Log-Handler-0.90/MANIFEST0000644000000000000000000000350313702611501013362 0ustar rootrootBuild.PL ChangeLog debian/changelog debian/compat debian/control debian/copyright debian/dirs debian/docs debian/README.Debian debian/rules examples/benchmark/benchmark.pl examples/category/category.pl examples/config/example.conf examples/config/example.pl examples/config/example.props examples/config/example.yaml examples/filter/extended_filter.pl examples/filter/filter_caller.pl examples/filter/filter_message.pl examples/layout/layout.pl examples/layout/pattern.pl examples/layout/runtime.pl examples/logger/myapp.pl examples/logger/MyApp.pm examples/prepare/prepare.pl examples/reload/logger1.conf examples/reload/logger2.conf examples/reload/reload.pl examples/socket/client.pl examples/socket/server.pl examples/text-csv/log-as-csv-string.pl INSTALL lib/Log/Handler.pm lib/Log/Handler/Config.pm lib/Log/Handler/Examples.pod lib/Log/Handler/Levels.pm lib/Log/Handler/Output.pm lib/Log/Handler/Output/DBI.pm lib/Log/Handler/Output/Email.pm lib/Log/Handler/Output/File.pm lib/Log/Handler/Output/Forward.pm lib/Log/Handler/Output/Screen.pm lib/Log/Handler/Output/Sendmail.pm lib/Log/Handler/Output/Socket.pm lib/Log/Handler/Pattern.pm lib/Log/Handler/Plugin/Config/General.pm lib/Log/Handler/Plugin/Config/Properties.pm lib/Log/Handler/Plugin/YAML.pm LICENSE Makefile.PL MANIFEST This list of files META.json META.yml perl-Log-Handler.spec README README.md t/000-use.t t/001-pod.t t/002-pod-coverage.t t/010-handler.t t/011-handler-set-pattern.t t/012-handler-message-pattern.t t/013-handler-priority.t t/014-handler-reload.t t/015-handler-filter-caller.t t/015-handler-filter-message.t t/016-handler-alias.t t/017-handler-special-levels.t t/018-handler-logger.t t/019-handler-setlevel.t t/020-output-forward.t t/030-output-file.t t/040-output-email.t t/045-output-sendmail.t t/050-output-dbi.t t/060-output-socket.t t/090-test-undef.t t/100-config.t Log-Handler-0.90/README.md0000640000000000000000000000000012433214366013501 0ustar rootrootLog-Handler-0.90/t/0000750000000000000000000000000013702611500012465 5ustar rootrootLog-Handler-0.90/t/010-handler.t0000640000000000000000000000261612235521676014611 0ustar rootrootuse strict; use warnings; use Test::More tests => 15; use File::Spec; use Log::Handler; my $rand_num = int(rand(999999)); my $logfile = File::Spec->catfile('t', "Log-Handler-$rand_num.log"); my $log = Log::Handler->new(); $log->add(file => { filename => [ 't', "Log-Handler-$rand_num.log" ], fileopen => 0, reopen => 0, filelock => 0, mode => 'excl', autoflush => 1, permissions => '0664', timeformat => '', message_layout => 'prefix [%L] %m', maxlevel => 'debug', minlevel => 'emergency', die_on_errors => 1, utf8 => 0, debug_trace => 0, debug_mode => 2, debug_skip => 0, }); ok(1, 'checking new'); ok(!-e $logfile, 'checking fileopen'); ok($log->is_debug, 'checking debug'); ok($log->is_info, 'checking info'); ok($log->is_notice, 'checking notice'); ok($log->is_warning, 'checking warning'); ok($log->is_warn, 'checking warn'); ok($log->is_error, 'checking error'); ok($log->is_err, 'checking err'); ok($log->is_critical, 'checking critical'); ok($log->is_crit, 'checking crit'); ok($log->is_alert, 'checking alert'); ok($log->is_emergency, 'checking emergency'); ok($log->is_emerg, 'checking emerg'); ok($log->is_fatal, 'checking fatal'); if (-e $logfile) { unlink($logfile) or die $!; } Log-Handler-0.90/t/090-test-undef.t0000640000000000000000000000056112235521676015257 0ustar rootrootuse strict; use warnings; use Test::More tests => 4; use Log::Handler; local $SIG{__WARN__} = sub { die @_ }; sub forward { length($_[0]->{message}) } my $log = Log::Handler->new(); ok(1, 'new'); $log->add(forward => { forward_to => \&forward }); ok(1, 'add'); ok($log->error('foo', undef, 'bar'), 'checking undef 1'); ok($log->error(undef), 'checking undef 2'); Log-Handler-0.90/t/012-handler-message-pattern.t0000640000000000000000000000176712235521676017716 0ustar rootrootuse strict; use warnings; use Test::More tests => 15; use Log::Handler; my $CHECKED = 0; my %PATTERN = ( '%L' => 'level', '%T' => 'time', '%D' => 'date', '%P' => 'pid', '%H' => 'hostname', '%C' => 'caller', '%p' => 'package', '%f' => 'filename', '%l' => 'line', '%s' => 'subroutine', '%S' => 'progname', '%r' => 'runtime', '%t' => 'mtime', '%m' => 'message', ); my %PATTERN_REC = map { $_ => 0 } values %PATTERN; sub check_struct { my $m = shift; foreach my $name (keys %$m) { if (exists $PATTERN_REC{$name}) { $PATTERN_REC{$name}++; } } } my $log = Log::Handler->new(); $log->add( forward => { forward_to => \&check_struct, maxlevel => 'debug', minlevel => 'debug', message_layout => '', message_pattern => [ keys %PATTERN ], } ); ok(1, 'new'); $log->debug('foo'); while ( my ($n, $v) = each %PATTERN_REC ) { ok($v, "test pattern $n"); } Log-Handler-0.90/t/100-config.t0000640000000000000000000000436212235521676014441 0ustar rootrootuse strict; use warnings; use Test::More tests => 14; use Log::Handler; my %config = ( file => { default => { timeformat => '%b %d %H:%M:%S', mode => 'excl', message_layout => '%T %H[%P] [%L] %S: %m', debug_mode => 2, fileopen => 0, }, file1 => { filename => 'foo', maxlevel => 'info', newline => 0, priority => 1, } }, screen => [ { alias => 'screen1', dump => 1, priority => 2, maxlevel => 'info', }, { alias => 'screen2', dump => 0, newline => 0, priority => 3, maxlevel => 'info', } ] ); my $log = Log::Handler->new(); $log->config(config => \%config); my %opts; $opts{handler}{file1} = shift @{$log->{levels}->{INFO}}; $opts{handler}{screen1} = shift @{$log->{levels}->{INFO}}; $opts{handler}{screen2} = shift @{$log->{levels}->{INFO}}; $opts{output}{file1} = $log->output('file1'); $opts{output}{screen1} = $log->output('screen1'); $opts{output}{screen2} = $log->output('screen2'); my %cmp = ( output => { file1 => { filename => 'foo', fileopen => 0, }, screen1 => { dump => 1, }, screen2 => { dump => 0, } }, handler => { file1 => { newline => 0, timeformat => '%b %d %H:%M:%S', message_layout => '%T %H[%P] [%L] %S: %m', debug_mode => 2, maxlevel => 6, }, screen1 => { priority => 2, maxlevel => 6, }, screen2 => { newline => 0, priority => 3, maxlevel => 6, } } ); foreach my $x (qw/handler output/) { foreach my $y (qw/file1 screen1 screen2/) { foreach my $k (keys %{$cmp{$x}{$y}}) { my $cmp_val = $cmp{$x}{$y}{$k}; my $opt_val = defined $opts{$x}{$y}{$k} ? $opts{$x}{$y}{$k} : 'n/a'; ok($cmp_val eq $opt_val, "checking config $x:$y:$k ($cmp_val:$opt_val)"); } } } Log-Handler-0.90/t/060-output-socket.t0000640000000000000000000000303712235521676016025 0ustar rootrootuse strict; use warnings; use Test::More; BEGIN { eval "use IO::Socket::INET;"; if ($@) { plan skip_all => "No IO::Socket::INET installed"; exit(0); } if (!$ENV{LOG_HANDLER_SOCK_TEST}) { plan skip_all => "Set \$ENV{LOG_HANDLER_SOCK_TEST} to 1 to enable this test"; exit(0); } }; use Log::Handler::Output::Socket; use IO::Socket::INET; eval { $SIG{ALRM} = sub { die "STOP TEST" }; alarm 60; }; my $sock = IO::Socket::INET->new( LocalAddr => "127.0.0.1", Proto => "tcp", Listen => 1, Timeout => 15 ) or die $!; my $port = $sock->sockport; my $pid = fork; if (!$pid) { my $r = $sock->accept; my $m = <$r> || "empty"; if ($m ne "test message from logger") { die "something wents wrong ($m)"; } $sock->close; waitpid($pid, 0); exit(0); } $sock->close; sleep 1; plan tests => 4; ok(1, "fork"); my $log = Log::Handler::Output::Socket->new( peeraddr => "127.0.0.1", peerport => $port, proto => "tcp", timeout => 15, persistent => 0, reconnect => 0, ); ok(1, "new"); $log->log(message => "test message from logger") or do { ok(0, "testing log() - ".$log->errstr); }; ok(1, "testing log()"); $log->reload( { peeraddr => "localhost", peerport => $port, proto => "tcp", timeout => 15, persistent => 0, reconnect => 0, } ); ok($log->{sockopts}->{PeerAddr} eq "localhost", "checking reload ($log->{sockopts}->{PeerAddr})"); Log-Handler-0.90/t/030-output-file.t0000640000000000000000000000233112235521676015445 0ustar rootrootuse strict; use warnings; use Test::More tests => 6; use File::Spec; use Log::Handler::Output::File; my $rand_num = int(rand(999999)); my $logfile = File::Spec->catfile('t', "Log-Handler-$rand_num.log"); my $log = Log::Handler::Output::File->new( filename => [ 't', "Log-Handler-$rand_num.log" ], permissions => '0664', mode => 'append', autoflush => 0, fileopen => 0, filelock => 0, reopen => 0, ); # write a string to the file $log->log(message => "test\n") or die $!; ok(1, "checking log()"); # checking if the file is readable open(my $fh, '<', $logfile) or do { ok(0, "open logfile ($logfile)"); exit(1); }; ok(1, "open logfile ($logfile)"); my $line = <$fh>; chomp($line); close $fh; ok($line =~ /^test\z/, "checking logfile ($line)"); if ( unlink($logfile) ) { ok(1, "unlink logfile ($logfile)"); } else { ok(0, "unlink logfile ($logfile)"); } $log->reload( { filename => [ 't', "Log-Handler-$rand_num.log" ], autoflush => 1, fileopen => 0, reopen => 0, } ); ok($log->{autoflush} == 1, "checking reload ($log->{autoflush})"); ok($log->{filename} =~ /$rand_num/, "checking reload ($log->{filename})"); Log-Handler-0.90/t/045-output-sendmail.t0000640000000000000000000000133512235521676016333 0ustar rootrootuse strict; use warnings; use Test::More tests => 4; use Log::Handler::Output::Sendmail; ok(1, "use ok"); $Log::Handler::Output::Sendmail::TEST = 1; my $email = Log::Handler::Output::Sendmail->new( from => 'bar@foo.example', to => 'foo@bar.example', subject => 'foo', ); $email->log(message => "b"); $email->log(message => "a"); $email->log(message => "r"); ok($email->{subject} eq "foo", "checking subject ($email->{subject})"); ok($email->{message} eq "bar", "checking buffer ($email->{message})"); $email->reload( { from => 'bar@foo.example', to => 'foo@bar.example', subject => 'baz', } ); ok($email->{subject} eq "baz", "checking reload ($email->{subject})"); Log-Handler-0.90/t/001-pod.t0000640000000000000000000000027212235521676013752 0ustar rootrootuse strict; use Test::More; eval "use Test::Pod"; plan skip_all => "Test::Pod required for testing POD" if $@; my @poddirs = qw( blib ); all_pod_files_ok( all_pod_files( @poddirs ) ); Log-Handler-0.90/t/013-handler-priority.t0000640000000000000000000000212212235521676016463 0ustar rootrootuse strict; use warnings; use Test::More tests => 7; use Log::Handler; my $PRIO_CHECK = 1; sub prio_1 { ok(1 == $PRIO_CHECK, "checking prio 1 ($PRIO_CHECK)"); $PRIO_CHECK++; } sub prio_2 { ok(2 == $PRIO_CHECK, "checking prio 2 ($PRIO_CHECK)"); $PRIO_CHECK++; } sub prio_3 { ok(3 == $PRIO_CHECK, "checking prio 3 ($PRIO_CHECK)"); $PRIO_CHECK++; } sub prio_10 { ok(4 == $PRIO_CHECK, "checking prio 10 ($PRIO_CHECK)"); $PRIO_CHECK++; } sub prio_11 { ok(5 == $PRIO_CHECK, "checking prio 11 ($PRIO_CHECK)"); $PRIO_CHECK++; } sub prio_12 { ok(6 == $PRIO_CHECK, "checking prio 12 ($PRIO_CHECK)"); $PRIO_CHECK++; } my $log = Log::Handler->new(); $log->add(forward => { forward_to => \&prio_3, priority => 3 }); $log->add(forward => { forward_to => \&prio_2, priority => 2 }); $log->add(forward => { forward_to => \&prio_1, priority => 1 }); $log->add(forward => { forward_to => \&prio_10 }); $log->add(forward => { forward_to => \&prio_11 }); $log->add(forward => { forward_to => \&prio_12 }); $log->error('foo'); ok($PRIO_CHECK == 7, 'all prios checked'); Log-Handler-0.90/t/000-use.t0000640000000000000000000000012612235521676013761 0ustar rootrootuse strict; use warnings; use Test::More tests => 1; use Log::Handler; ok(1, "use"); Log-Handler-0.90/t/019-handler-setlevel.t0000640000000000000000000000250712235521676016442 0ustar rootrootuse strict; use warnings; use Test::More tests => 2; use Log::Handler; my $MESSAGE; sub test { $MESSAGE++; } ok(1, "use"); my $log = Log::Handler->new(); $log->add( forward => { alias => "forward0", forward_to => \&test, minlevel => "emerg", maxlevel => "error", } ); $log->add( forward => { alias => "forward1", forward_to => \&test, minlevel => "emerg", maxlevel => "error", } ); $log->add( forward => { alias => "forward2", forward_to => \&test, minlevel => "emerg", maxlevel => "error", } ); # should log nothing $log->notice(); $log->set_level( forward1 => { minlevel => "emerg", maxlevel => "debug", } ); # should only forward1 should log $log->debug(); # disable logging for forward1 and # enable it for forward0 and forward2 $log->set_level( forward0 => { minlevel => "emerg", maxlevel => "debug", } ); $log->set_level( forward1 => { minlevel => "emerg", maxlevel => "error", } ); $log->set_level( forward2 => { minlevel => "emerg", maxlevel => "debug", } ); # should only log to forward0 and forward2 $log->debug(); ok($MESSAGE == 3, "check set_level($MESSAGE)"); Log-Handler-0.90/t/015-handler-filter-caller.t0000640000000000000000000000122112235521676017330 0ustar rootrootuse strict; use warnings; use Test::More tests => 3; use Log::Handler; my $CHECK = 0; my $STRING = ''; ok(1, 'use'); my $log = Log::Handler->new(); ok(2, 'new'); $log->add( forward => { forward_to => \&check, maxlevel => 6, filter_caller => 'Foo::Bar', message_layout => '%p', newline => 0, } ); sub check { my $m = shift; if ($m->{message} eq 'Foo::Bar') { $CHECK++; } } Foo::Bar::baz(); Foo::Baz::baz(); ok($CHECK == 1, "checking filter_caller ($CHECK)"); package Foo::Bar; sub baz { $log->info(); } package Foo::Baz; sub baz { $log->info(); } 1; Log-Handler-0.90/t/050-output-dbi.t0000640000000000000000000000322012235521676015264 0ustar rootrootuse strict; use warnings; use Test::More; BEGIN { eval "use DBI;"; if ($@) { plan skip_all => "No DBI installed"; exit(0); } }; use Log::Handler::Output::DBI; plan tests => 8; my ($ret, $log); $log = Log::Handler::Output::DBI->new( database => "dbname", driver => "mysql", user => "dbuser", password => "dbpass", host => "127.0.0.1", port => 3306, debug => 0, table => "messages", columns => "level message", values => "%level %message", persistent => 0, ); ok(1, "new"); $ret = $log->{statement} eq "insert into messages (level,message) values (?,?)"; ok($ret, "checking statement"); #$ret = $log->{cstr}->[0] eq "dbi:mysql:database=dbname;host=127.0.0.1;port=3306"; $ret = $log->{cstr}->[0] eq "dbi:mysql:database=dbname;host=127.0.0.1;port=3306"; ok($ret, "checking cstr"); $ret = $log->{cstr}->[1] eq "dbuser"; ok($ret, "checking user"); $ret = $log->{cstr}->[2] eq "dbpass"; ok($ret, "checking password"); $ret = $log->{cstr}->[3]->{PrintError} == 0; ok($ret, "checking argument PrintError"); $ret = $log->{cstr}->[3]->{AutoCommit} == 1; ok($ret, "checking argument AutoCommit"); $log->reload( { database => "dbname", driver => "mysql", user => "dbuser", password => "new password", host => "127.0.0.1", port => 3306, debug => 0, table => "messages", columns => "level message", values => "%level %message", persistent => 0, } ); ok($log->{password} eq "new password", "checking reload ($log->{password})"); Log-Handler-0.90/t/011-handler-set-pattern.t0000640000000000000000000000171412235521676017054 0ustar rootrootuse strict; use warnings; use Test::More tests => 5; use Log::Handler; my $CHECKED; sub check_struct { $CHECKED = 1; my $message = shift; my $value = ''; if (ref($message) eq 'HASH') { ok(1, "checking hashref"); $value = $message->{xname}; ok($value eq 'xvalue', "checking scalar ret value"); $value = $message->{yname}; ok($value eq 'yvalue', "checking code ret value"); } else { ok(0, "checking hashref"); } } my $log = Log::Handler->new(); $log->set_pattern('%X', 'xname', 'xvalue'); $log->set_pattern('%Y', 'yname', sub { 'xxxxxx' }); $log->add( forward => { forward_to => \&check_struct, maxlevel => 'debug', minlevel => 'debug', message_layout => '%m', message_pattern => [ qw/%X %Y/ ], } ); ok(1, 'new'); $log->set_pattern('%Y', 'yname', sub { 'yvalue' }); $log->debug('foo'); ok($CHECKED, "call check_struct()"); Log-Handler-0.90/t/002-pod-coverage.t0000640000000000000000000000204112235521676015540 0ustar rootrootuse strict; use warnings; use Test::More; eval "use Test::Pod::Coverage"; if ($@) { plan skip_all => "Test::Pod::Coverage required for testing pod coverage"; exit 0; } my @modules = qw( Log::Handler::Output Log::Handler::Pattern Log::Handler::Output Log::Handler::Output::Forward Log::Handler::Output::File Log::Handler::Output::Sendmail Log::Handler::Output::Socket Log::Handler::Output::Screen Log::Handler::Levels Log::Handler ); eval "use Config::Properties"; if (!$@) { push @modules, "Log::Handler::Plugin::Config::Properties"; } eval "Config::General"; if (!$@) { push @modules, "Log::Handler::Plugin::Config::General"; } eval "YAML"; if (!$@) { push @modules, "Log::Handler::Plugin::YAML"; } eval "DBI"; if (!$@) { push @modules, "Log::Handler::Output::DBI"; } eval "use Email::Date; use Net::SMTP"; if (!$@) { push @modules, "Log::Handler::Output::Email"; } plan tests => scalar @modules; foreach my $mod (@modules) { pod_coverage_ok($mod, "$mod is covered"); } Log-Handler-0.90/t/017-handler-special-levels.t0000640000000000000000000000226512235521676017526 0ustar rootrootuse strict; use warnings; use Test::More tests => 23; use Log::Handler; my $MESSAGES = 13; my $RECEIVED = 0; my %LEVELS = ( DEBUG => 1, INFO => 1, NOTICE => 1, WARNING => 2, ERROR => 2, CRITICAL => 2, ALERT => 1, EMERGENCY => 2, FATAL => 1, ); my @LEVELS = (qw/ debug info notice warning warn error err critical crit alert emergency emerg fatal /); sub forward { my $m = shift; if ($m->{message} =~ /([A-Z]+) foo/) { my $level = $1; if (exists $LEVELS{$level}) { $LEVELS{$level}--; } $RECEIVED++; } } my $log = Log::Handler->new(); $log->add( forward => { maxlevel => 'debug', forward_to => \&forward, message_layout => '%L %m', } ); # die foreach my $level (@LEVELS) { my $ul = uc($level); eval { $log->die($level => 'foo') }; ok($@ =~ /foo/, "test die $level"); } # count messages ok($RECEIVED == $MESSAGES, "count messages ($RECEIVED:$MESSAGES)"); # got all messages? while ( my ($level, $count) = each %LEVELS ) { ok($count == 0, "test level $level ($count)"); } Log-Handler-0.90/t/014-handler-reload.t0000640000000000000000000001035112235521676016054 0ustar rootrootuse strict; use warnings; use Test::More tests => 19; use Log::Handler; my %msg; sub counter { if (shift->{message} =~ /(INFO|WARN).+(unknown\d|forward\d)/) { $msg{$2}{$1}++; } } my $log = Log::Handler->new(); $log->config( config => { forward => [ { alias => "forward1", maxlevel => "info", minlevel => "emerg", priority => 2, forward_to => \&counter, message_layout => "%L - forward1 %m", }, { alias => "forward2", maxlevel => "info", minlevel => "emerg", priority => 1, forward_to => \&counter, message_layout => "%L - forward2 %m", }, { alias => "forward3", maxlevel => "info", minlevel => "emerg", priority => 3, forward_to => \&counter, message_layout => "%L - forward3 %m", }, { maxlevel => "info", minlevel => "emerg", priority => 3, forward_to => \&counter, message_layout => "%L - unknown1 %m", }, ], }, ); $log->warning(1); $log->info(1); $log->reload( config => { forward => [ { alias => "forward1", maxlevel => "warning", minlevel => "emerg", priority => 2, forward_to => \&counter, message_layout => "%T [%L] forward1 %m", }, { alias => "forward3", maxlevel => "warning", minlevel => "emerg", priority => 1, forward_to => \&counter, message_layout => "%T [%L] forward3 %m", }, { alias => "forward4", maxlevel => "warning", minlevel => "emerg", priority => 1, forward_to => \&counter, message_layout => "%T [%L] forward4 %m", }, { alias => "forward5", maxlevel => "warning", minlevel => "emerg", priority => 1, forward_to => \&counter, message_layout => "%T [%L] forward5 %m", }, { maxlevel => "warning", minlevel => "emerg", priority => 3, forward_to => \&counter, message_layout => "%L - unknown2 %m", }, ], } ) or die $log->errstr; ok(1, "reload"); $log->warning(1); $log->info(1); my $f1 = scalar keys %{$msg{forward1}}; my $f2 = scalar keys %{$msg{forward2}}; my $f3 = scalar keys %{$msg{forward3}}; my $f4 = scalar keys %{$msg{forward4}}; my $f5 = scalar keys %{$msg{forward5}}; ok($f1 == 2, "checking forward1 keys ($f1)"); ok($f2 == 2, "checking forward2 keys ($f2)"); ok($f3 == 2, "checking forward3 keys ($f3)"); ok($f4 == 1, "checking forward4 keys ($f4)"); ok($f5 == 1, "checking forward5 keys ($f5)"); ok($msg{forward1}{INFO} == 1, "checking forward1 INFO ($msg{forward1}{INFO})"); ok($msg{forward1}{WARN} == 2, "checking forward1 WARN ($msg{forward1}{WARN})"); ok($msg{forward2}{INFO} == 1, "checking forward2 INFO ($msg{forward2}{INFO})"); ok($msg{forward2}{WARN} == 1, "checking forward2 WARN ($msg{forward2}{WARN})"); ok($msg{forward3}{INFO} == 1, "checking forward3 INFO ($msg{forward3}{INFO})"); ok($msg{forward3}{WARN} == 2, "checking forward3 WARN ($msg{forward3}{WARN})"); ok($msg{forward4}{WARN} == 1, "checking forward3 WARN ($msg{forward4}{WARN})"); ok($msg{forward5}{WARN} == 1, "checking forward3 WARN ($msg{forward5}{WARN})"); ok($msg{forward5}{WARN} == 1, "checking forward3 WARN ($msg{forward5}{WARN})"); ok($msg{unknown1}{INFO} == 1, "checking unknown1 INFO ($msg{unknown1}{INFO})"); ok($msg{unknown1}{WARN} == 1, "checking unknown1 INFO ($msg{unknown1}{WARN})"); ok($msg{unknown2}{WARN} == 1, "checking unknown2 INFO ($msg{unknown1}{WARN})"); ok(!exists $msg{unknown2}{INFO}, "checking unknown2 INFO"); Log-Handler-0.90/t/015-handler-filter-message.t0000640000000000000000000000337012235521676017521 0ustar rootrootuse strict; use warnings; #use Test::More tests => 8; use Test::More tests => 7; use Log::Handler; # Comment out "string 2" becaus ValidatePP.pm is unable to handle # regexes on some perl versions! That is strange, but not a problem # of Log::Handler. my %STRING = ( 'string 1' => 0, # 'string 2' => 0, 'string 3' => 0, 'string 4' => 0, 'string 5' => 0, ); ok(1, 'use'); my $log = Log::Handler->new(); ok(2, 'new'); $log->add( forward => { forward_to => \&check, maxlevel => 6, filter_message => 'string 1$', } ); #$log->add( # forward => { # forward_to => \&check, # maxlevel => 6, # filter_message => qr/STRING\s2$/i, # } #); $log->add( forward => { forward_to => \&check, maxlevel => 6, filter_message => sub { shift->{message} =~ /string\s3$/ }, } ); $log->add( forward => { forward_to => \&check, maxlevel => 6, filter_message => { match1 => 'foo', match2 => qr/bar/, match3 => '(?:string\s4|string\s5)', condition => '(!match1 && !match2) && match3', } } ); ok(3, 'add'); sub check { my $m = shift; if ($m->{message} =~ /(string\s\d+)/) { if (exists $STRING{$1}) { $STRING{$1}++; } else { die "unexpected message $m->{message}"; } } } $log->info('string 1'); #$log->info('string 2'); $log->info('string 3'); $log->info('string 4'); $log->info('string 5'); $log->info('string 1 foo'); #$log->info('string 2 foo'); $log->info('string 3 foo'); $log->info('string 4 foo'); $log->info('string 5 bar'); while ( my ($k, $v) = each %STRING ) { ok($v == 1, "checking if $k match (hits:$v)"); } Log-Handler-0.90/t/040-output-email.t0000640000000000000000000000307412235521676015623 0ustar rootrootuse strict; use warnings; use Test::More; BEGIN { eval "use Net::SMTP;"; if ($@) { plan skip_all => "No Net::SMTP installed"; exit(0); } eval "use Email::Date;"; if ($@) { plan skip_all => "No Email::Date installed"; exit(0); } }; plan tests => 4; use Log::Handler::Output::Email; $Log::Handler::Output::Email::TEST = 1; my $log = Log::Handler::Output::Email->new( host => [ "bloonix.de" ], hello => "EHLO bloonix.de", timeout => 60, debug => 0, from => 'jschulz.cpan@bloonix.de', to => 'jschulz.cpan@bloonix.de', subject => "Log::Handler::Output::Email test", buffer => 20, ); ok(1, "new"); # checking all log levels for would() foreach my $i (1..10) { $log->log(message => "test $i\n") or die $!; } ok(1, "checking log()"); # checking all lines my $match_lines = 0; my $all_lines = 0; foreach my $line ( @{$log->{message_buffer}} ) { ++$all_lines; next unless $line->{message} =~ /^test \d+$/; ++$match_lines; } if ($match_lines == 10) { ok(1, "checking buffer ($all_lines:$match_lines)"); } else { ok(0, "checking buffer ($all_lines:$match_lines)"); } $log->reload( { host => [ "bloonix.de" ], hello => "EHLO bloonix.de", timeout => 60, debug => 0, from => 'jschulz.cpan@bloonix.de', to => 'jschulz.cpan@bloonix.de', subject => "Log::Handler::Output::Email test", buffer => 100, } ); ok($log->{buffer} == 100, "checking reload ($log->{buffer})"); Log-Handler-0.90/t/016-handler-alias.t0000640000000000000000000000056412235521676015706 0ustar rootrootuse strict; use warnings; use Test::More tests => 1; use Log::Handler; my $ALIAS_CHECK = 0; sub alias_check { if ($_[0] =~ /foo/) { $ALIAS_CHECK++ } } my $log = Log::Handler->new(); $log->add( forward => { forward_to => \&alias_check, alias => 'test', } ); $log->output('test')->log('foo'); ok($ALIAS_CHECK, "checking alias"); Log-Handler-0.90/t/018-handler-logger.t0000640000000000000000000000077212235521676016077 0ustar rootrootuse strict; use warnings; use Test::More tests => 8; use Log::Handler lhtest1 => 'LOG'; my $COUNT = 0; ok(1, 'use'); foreach my $logger (qw/lhtest2 lhtest3/) { Log::Handler->create_logger($logger); ok(1, 'create logger'); my $log = Log::Handler->get_logger($logger); $log->add(forward => { forward_to => sub { $COUNT++ }, maxlevel => 'info', }); ok(1, 'add screen output'); $log->info(); ok(1, 'log'); } ok($COUNT == 2, "check counter ($COUNT)"); Log-Handler-0.90/t/020-output-forward.t0000640000000000000000000000465312235521676016202 0ustar rootrootuse strict; use warnings; use Test::More tests => 27; use Log::Handler; my @LINES = (); my $RELOAD = 0; sub save_lines { my $foo = shift; next unless $foo eq "foo"; push @LINES, $_[0]->{message}; } sub reload { $RELOAD++; } my $log = Log::Handler->new(); $log->add( forward => { alias => "forward", forward_to => \&save_lines, arguments => [ "foo" ], maxlevel => "debug", minlevel => "emergency", message_layout => "prefix [%L] %m postfix", } ); ok(1, "add forward"); ok($log->is_debug, "checking is_debug"); ok($log->is_info, "checking is_info"); ok($log->is_notice, "checking is_notice"); ok($log->is_warning, "checking is_warning"); ok($log->is_error, "checking is_error"); ok($log->is_err, "checking is_err"); ok($log->is_critical, "checking is_critical"); ok($log->is_crit, "checking is_crit"); ok($log->is_alert, "checking is_alert"); ok($log->is_emergency, "checking is_emergency"); ok($log->is_emerg, "checking is_emerg"); ok($log->is_fatal, "checking is_fatal"); ok($log->debug("DEBUG"), "checking debug"); ok($log->info("INFO"), "checking info"); ok($log->notice("NOTICE"), "checking notice"); ok($log->warning("WARNING"), "checking warning"); ok($log->error("ERROR"), "checking error"); ok($log->err("ERROR"), "checking err"); ok($log->critical("CRITICAL"), "checking critical"); ok($log->crit("CRITICAL"), "checking crit"); ok($log->alert("ALERT"), "checking alert"); ok($log->emergency("EMERGENCY"), "checking emergency"); ok($log->emerg("EMERGENCY"), "checking emerg"); ok($log->fatal("FATAL"), "checking fatal"); # checking all lines that should be forwarded my $match_lines = 0; my $all_lines = 0; foreach my $line ( @LINES ) { ++$all_lines; next unless $line =~ /^prefix \[([A-Z]+)\] ([A-Z]+) postfix/; next unless $1 eq $2; ++$match_lines; } if ($match_lines == 12) { ok(1, "checking buffer ($all_lines:$match_lines)"); } else { ok(0, "checking buffer ($all_lines:$match_lines)"); } $log->reload( config => { forward => { alias => "forward", forward_to => \&reload, maxlevel => "debug", minlevel => "debug", } } ); $log->notice("foo"); $log->info("bar"); $log->debug("baz"); ok($RELOAD == 1, "checking reload ($RELOAD)"); Log-Handler-0.90/META.yml0000644000000000000000000000421713702611501013505 0ustar rootroot--- abstract: 'Log messages to several outputs.' author: - 'Jonny Schulz' build_requires: {} configure_requires: Module::Build: '0.42' dynamic_config: 1 generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Log-Handler provides: Log::Handler: file: lib/Log/Handler.pm version: '0.90' Log::Handler::Config: file: lib/Log/Handler/Config.pm version: '0.09' Log::Handler::Levels: file: lib/Log/Handler/Levels.pm version: '0.07' Log::Handler::Output: file: lib/Log/Handler/Output.pm version: '0.10' Log::Handler::Output::DBI: file: lib/Log/Handler/Output/DBI.pm version: '0.12' Log::Handler::Output::Email: file: lib/Log/Handler/Output/Email.pm version: '0.08' Log::Handler::Output::File: file: lib/Log/Handler/Output/File.pm version: '0.08' Log::Handler::Output::Forward: file: lib/Log/Handler/Output/Forward.pm version: '0.03' Log::Handler::Output::Screen: file: lib/Log/Handler/Output/Screen.pm version: '0.07' Log::Handler::Output::Sendmail: file: lib/Log/Handler/Output/Sendmail.pm version: '0.07' Log::Handler::Output::Socket: file: lib/Log/Handler/Output/Socket.pm version: '0.08' Log::Handler::Pattern: file: lib/Log/Handler/Pattern.pm version: '0.08' Log::Handler::Plugin::Config::General: file: lib/Log/Handler/Plugin/Config/General.pm version: '0.02' Log::Handler::Plugin::Config::Properties: file: lib/Log/Handler/Plugin/Config/Properties.pm version: '0.03' Log::Handler::Plugin::YAML: file: lib/Log/Handler/Plugin/YAML.pm version: '0.03' recommends: Config::General: '0' Config::Properties: '0' DBI: '0' Email::Date: '0' IO::Socket: '0' Net::SMTP: '0' YAML: '0' requires: Carp: '0' Data::Dumper: '0' Fcntl: '0' File::Spec: '0' POSIX: '0' Params::Validate: '0' Sys::Hostname: '0' Test::More: '0' Time::HiRes: '0' UNIVERSAL: '0' resources: license: http://dev.perl.org/licenses/ version: '0.90' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Log-Handler-0.90/Build.PL0000640000000000000000000000172712235521676013544 0ustar rootrootuse strict; use warnings; use Module::Build; my $build = Module::Build->new( create_readme => 1, create_makefile_pl => "traditional", license => "perl", module_name => "Log::Handler", dist_author => "Jonny Schulz", sign => 0, recommends => { "Config::General" => 0, "Config::Properties" => 0, "DBI" => 0, "IO::Socket" => 0, "Net::SMTP" => 0, "Email::Date" => 0, "YAML" => 0, }, requires => { "Carp" => 0, "Data::Dumper" => 0, "Fcntl" => 0, "File::Spec" => 0, "Params::Validate" => 0, "POSIX" => 0, "Test::More" => 0, "Time::HiRes" => 0, "Sys::Hostname" => 0, "UNIVERSAL" => 0, }, ); $build->create_build_script; Log-Handler-0.90/debian/0000750000000000000000000000000012422402505013445 5ustar rootrootLog-Handler-0.90/debian/rules0000750000000000000000000000012712377565454014553 0ustar rootroot#!/usr/bin/make -f %: dh $@ override_dh_auto_test: AUTOMATED_TESTING=0 dh_auto_test Log-Handler-0.90/debian/README.Debian0000640000000000000000000000024112376707077015530 0ustar rootrootThis software is copyright protected! The author is Jonny Schulz Copyright: Copyright (C) 2010-2014 by Jonny Schulz. All rights reserved. Log-Handler-0.90/debian/compat0000640000000000000000000000000212377565336014671 0ustar rootroot7 Log-Handler-0.90/debian/docs0000640000000000000000000000003212377565441014336 0ustar rootrootChangeLog INSTALL LICENSE Log-Handler-0.90/debian/dirs0000640000000000000000000000000012377565434014344 0ustar rootrootLog-Handler-0.90/debian/copyright0000640000000000000000000000022512376735463015424 0ustar rootrootThis software is copyright protected! The author is Jonny Schulz Copyright (C) 2010-2014 by Jonny Schulz. All rights reserved. Log-Handler-0.90/debian/control0000640000000000000000000000124112400220040015033 0ustar rootrootSource: liblog-handler-perl Section: perl Priority: optional Build-Depends: debhelper (>= 8), perl Build-Depends-Indep: libdbi-perl, libemail-date-perl, libmro-compat-perl, libparams-validate-perl, libtest-pod-coverage-perl, libtest-pod-perl Maintainer: Jonny Schulz Standards-Version: 3.9.3 Homepage: http://www.bloonix.de/ Package: liblog-handler-perl Architecture: all Depends: ${perl:Depends}, liblog-handler-perl Suggests: libconfig-general-perl, libdbi-perl, libemail-date-perl, libmro-compat-perl, libyaml-perl Description: Log messages to several outputs. Log::Handler is an easy-to-use Perl module for logging, debugging, and tracing. Log-Handler-0.90/debian/changelog0000640000000000000000000000073312422402453015325 0ustar rootrootliblog-handler-perl (0.84-1) unstable; urgency=low * Fixed some version conflicts. -- Jonny Schulz Fri, 24 Oct 2014 07:00:00 +0200 liblog-handler-perl (0.83-1) unstable; urgency=low * Added method set_default_param. -- Jonny Schulz Thu, 23 Oct 2014 22:00:00 +0200 liblog-handler-perl (0.82-1) unstable; urgency=low * Initial self-created debian release. -- Jonny Schulz Mon, 25 Aug 2014 15:00:00 +0200 Log-Handler-0.90/perl-Log-Handler.spec0000640000000000000000000000252313702611114016136 0ustar rootrootSummary: Log Handler Name: perl-Log-Handler Version: 0.90 Release: 1%{dist} License: GPL+ or Artistic Group: Development/Libraries Distribution: RHEL and CentOS Packager: Jonny Schulz Vendor: Bloonix BuildArch: noarch BuildRoot: %{_tmppath}/Log-Handler-%{version}-%{release}-root Source0: http://search.cpan.org/CPAN/authors/id/B/BL/BLOONIX/Log-Handler-%{version}.tar.gz Provides: perl(Log::Handler) Requires: perl(Params::Validate) AutoReqProv: no %description Log::Handler is an easy-to-use Perl module for logging, debugging, and tracing. %prep %setup -q -n Log-Handler-%{version} %build %{__perl} Build.PL installdirs=vendor %{__perl} Build %install %{__perl} Build install destdir=%{buildroot} create_packlist=0 find %{buildroot} -name .packlist -exec %{__rm} {} \; find %{buildroot} -type f -name .packlist -exec rm -f {} ';' find %{buildroot} -type f -name '*.bs' -a -size 0 -exec rm -f {} ';' %{_fixperms} %{buildroot}/* %clean rm -rf %{buildroot} %files %defattr(-,root,root,-) %doc ChangeLog INSTALL LICENSE %{perl_vendorlib}/* %{_mandir}/man3/* %changelog * Fri Oct 24 2014 Jonny Schulz - 0.84-1 - Fixed some version conflicts. * Thu Oct 23 2014 Jonny Schulz - 0.83-1 - Added method set_default_param. * Mon Aug 25 2014 Jonny Schulz - 0.82-1 - Initial self-created RPM release. Log-Handler-0.90/lib/0000750000000000000000000000000012235521676013006 5ustar rootrootLog-Handler-0.90/lib/Log/0000750000000000000000000000000013702611105013512 5ustar rootrootLog-Handler-0.90/lib/Log/Handler.pm0000640000000000000000000015117513702611105015440 0ustar rootroot=head1 NAME Log::Handler - Log messages to several outputs. =head1 SYNOPSIS use Log::Handler; my $log = Log::Handler->new(); $log->add( file => { filename => "file.log", maxlevel => "debug", minlevel => "warning", } ); $log->warning("message"); Or use Log::Handler; my $log = Log::Handler->new( screen => { log_to => "STDOUT", maxlevel => "debug", minlevel => "debug", message_layout => "%T [%L] %m (%C)", }, screen => { log_to => "STDOUT", maxlevel => "info", minlevel => "notice", }, screen => { log_to => "STDERR", maxlevel => "warning", minlevel => "emergency", }, ); Or use Log::Handler; my $log = Log::Handler->new(); $log->config( config => "logger.conf" ); # and maybe later $log->reload( config => "logger.conf" ); Or # create a application wide logger package MyApp; use Log::Handler; my $log = Log::Handler->create_logger("myapp"); $log->add(screen => { maxlevel => "info" }); $log->info("info message"); # get logger with get_logger() package MyApp::Admin; use Log::Handler; my $log = Log::Handler->get_logger("myapp"); $log->info("info message from MyApp::Admin"); =head1 DESCRIPTION The C is a object oriented handler for logging, tracing and debugging. It is very easy to use and provides a simple interface for multiple output objects with lots of configuration parameters. You can easily filter the amount of logged information on a per-output base, define priorities, create patterns to format the messages and reload the complete logging machine. See the documentation for details. =head1 IMPORTANT NOTES Note that the default for option C is now set to TRUE and newlines will be appended automatically to each message if no newline exists. A long time I thought about this serious change and have come to the decision to change it. The default for option C from Log::Handler::Output::File is now C and not C anymore. The methods C and C are new since version 0.62. I tested it with Screen.pm, File.pm and DBI.pm and it runs fine. If you find bugs then open a bug report please :-) =head1 LOG LEVELS There are eigth levels available: 7 debug 6 info 5 notice 4 warning, warn 3 error, err 2 critical, crit 1 alert 0 emergency, emerg C is the highest and C is the lowest level. Level C is the highest level because it basically says to log every peep. =head1 LOG LEVEL METHODS =head2 Level methods =over 4 =item B =item B =item B =item B, B =item B, B =item B, B =item B =item B, B =back The call of a log level method is very simple: $log->info("Hello World! How are you?"); Or maybe: $log->info("Hello World!", "How are you?"); Both calls would log - if level INFO is active: Feb 01 12:56:31 [INFO] Hello World! How are you? =head2 is_* methods =over 4 =item B =item B =item B =item B, B =item B, B =item B, B =item B =item B, B =back These twelve methods could be very useful if you want to kwow if the current level would log the message. All methods returns TRUE if the current set of C and C would log the message and FALSE if not. =head1 SPECIAL LOG METHODS =over 4 =item B, B =item B =item B =item B =item B =back For a full list take a look into the documentation of L. =head1 METHODS =head2 new() Call C to create a new log handler object. my $log = Log::Handler->new(); =head2 add() Call C to add a new output object. The method expects 2 parts of options; the options for the handler and the options for the output module you want to use. The output modules got it's own documentation for all options. Example: use Log::Handler; my $log = Log::Handler->new(); $log->add( # Add "file output" file => { # handler options (see Log::Handler) timeformat => "%Y/%m/%d %H:%M:%S", message_layout => "%T [%L] %S: %m", maxlevel => "debug", minlevel => "emergency", die_on_errors => 1, debug_trace => 0, debug_mode => 2, debug_skip => 0, # file options (see Log::Handler::Output::File) filename => "file.log", filelock => 1, fileopen => 1, reopen => 1, autoflush => 1, permissions => "0660", utf8 => 1, } ); Take a look to L for more examples. The following options are possible for the handler: =over 4 =item B and B With these options it's possible to set the log levels for your program. Example: maxlevel => "error" minlevel => "emergency" # or maxlevel => "err" minlevel => "emerg" # or maxlevel => 3 minlevel => 0 It's possible to set the log level as string or as number. The default setting for C is C and the default setting for C is C. Example: If C is set to C and C to C then the levels C, C, C, C and C would be logged. You can set both to 8 or C if you want to disable the logging machine. =item B The option C is used to set the format for the placeholder C<%T>. The string is converted with C. The default format is set to S<"%b %d %H:%M:%S"> and looks like Feb 01 12:56:31 If you would set the format to S<"%Y/%m/%d %H:%M:%S"> it would looks like 2007/02/01 12:56:31 =item B This options works like C. You can set a format that is used for the placeholder C<%D>. It's just useful if you want to split the date and time: $log->add(file => { filename => "file.log", dateformat => "%Y-%m-%d", timeformat => "%H:%M:%S", message_layout => "%D %T %L %m", }); $log->error("an error here"); This looks like 2007-02-01 12:56:31 ERROR an error here This option is not used by default. =item B C is a very helpful option. It let the logger appends a newline to the message if a newline doesn't exist. 0 - do nothing 1 - append a newline if not exist (default) Example: $log->add( screen => { newline => 1, maxlevel => "info", } ); $log->info("message\n"); $log->info("message"); In both cases the message would be logged with a newline at the end. =item B With this option it's possible to create your own message layout with different placeholders in C style. The available placeholders are: %L Log level %T Time or full timestamp (option timeformat) %D Date (option dateformat) %P PID %H Hostname %U User name %G Group name %N Newline %S Program name %C Caller - filename and line number %p Caller - package name %f Caller - file name %l Caller - line number %s Caller - subroutine name %r Runtime in seconds since program start %t Time measurement - replaced with the time since the last call of $log->$level %m Message %% Percent The default message layout is set to S<"%T [%L] %m">. As example the following code $log->alert("foo bar"); would log Feb 01 12:56:31 [ALERT] foo bar If you set C to message_layout => "%T foo %L bar %m (%C)" and call $log->info("baz"); then it would log Feb 01 12:56:31 foo INFO bar baz (script.pl, line 40) Traces will be appended after the complete message. You can create your own placeholders with the method C. =item B This option is just useful if you want to forward messages to output modules that needs the parts of a message as a hash reference - as example L, L or L. The option expects a list of placeholders: # as a array reference message_pattern => [ qw/%T %L %H %m/ ] # or as a string message_pattern => "%T %L %H %m" The patterns will be replaced with real names as hash keys. %L level %T time %D date %P pid %H hostname %U user %G group %N newline %r runtime %C caller %p package %f filename %l line %s subroutine %S progname %t mtime %m message Here a full code example: use Log::Handler; my $log = Log::Handler->new(); $log->add(forward => { forward_to => \&my_func, message_pattern => [ qw/%T %L %H %m/ ], message_layout => "%m", maxlevel => "info", }); $log->info("a forwarded message"); # now you can access it sub my_func { my $msg = shift; print "Timestamp: $msg->{time}\n"; print "Level: $msg->{level}\n"; print "Hostname: $msg->{hostname}\n"; print "Message: $msg->{message}\n"; } =item B C is useful if you want to do something with the message before it will be logged... maybe you want to create your own layout because message_layout doesn't meet your claim. $log->add( screen => { newline => 1, message_layout => "%m (%t)", message_pattern => [ qw/%T %L %H %m/ ], prepare_message => \&format, } ); $log->error("foo"); $log->error("bar"); $log->error("baz"); sub format { my $m = shift; $m->{message} = sprintf("%-20s %-20s %-20s %s", $m->{time}, $m->{level}, $m->{hostname}, $m->{message}); } The output looks like Mar 08 15:14:20 ERROR h1434036 foo (0.039694) Mar 08 15:14:20 ERROR h1434036 bar (0.000510) Mar 08 15:14:20 ERROR h1434036 baz (0.000274) =item B With this option you can set the priority of your output objects. This means that messages will be logged at first to the outputs with a higher priority. If this option is not set then the default priority begins with 10 and will be increased +1 with each output. Example: We add a output with no priority $log->add(file => { filename => "file1.log" }); This output gets the priority of 10. Now we add another output $log->add(file => { filename => "file2.log" }); This output gets the priority of 11... and so on. Messages would be logged at first to the output with the priority of 10 and then to the output with the priority of 11. Now you can add another output and set the priority to 1. $log->add(screen => { dump => 1, priority => 1 }); Messages would be logged now at first to the screen. =item B Set C to 0 if you don't want that the handler dies on failed write operations. 0 - to disable it 1 - to enable it If you set C to 0 then you have to control it yourself. $log->info("info message") or die $log->errstr(); # or Log::Handler->errstr() # or Log::Handler::errstr() # or $Log::Handler::ERRSTR =item B This option is set to 1 by default. Take a look to the description of the method C for more information about this option. =item B With this option it's possible to set a filter. If the filter is set then only messages will be logged that match the filter. You can pass a regexp, a code reference or a simple string. Example: $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => qr/log this/, # or # filter_message => "log this", # filter_message => '^log only this$', }); $log->info("log this"); $log->info("but not that"); If you pass your own code then you have to check the message yourself. $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => \&my_filter }); # return TRUE if you want to log the message, FALSE if not sub my_filter { my $msg = shift; $msg->{message} =~ /your filter/; } It's also possible to define a simple condition with matches. Just pass a hash reference with the options C and C. Example: $log->add(file => { filename => "file.log", maxlevel => 6, filter_message => { match1 => "log this", match2 => qr/with that/, match3 => "(?:or this|or that)", condition => "(match1 && match2) || match3", } }); NOTE that re-eval in regexes is not valid! Something like match1 => '(?{unlink("file.txt")})' would cause an error! =item B This is the opposite of option C, but it's only possible to set a simple string or regular expression. $log->add(file => { filename => "file.log", maxlevel => 6, skip => '^do not log this.+$' }); =item B The parameter C works like C but is much easier to configure. You can set a comma separated list of modules. As example if you would set the category to category => "MyApp::User" then all messages of MyApp::User and the submodules would be logged. Example: my $log = Log::Handler->new(); $log->add( screen => { maxlevel => "info", category => "MyApp::User, MyApp::Session" } ); package MyApp; $log->info(__PACKAGE__); package MyApp::Products; $log->info(__PACKAGE__); package MyApp::User; $log->info(__PACKAGE__); package MyApp::Users; $log->info(__PACKAGE__); package MyApp::User::Settings; $log->info(__PACKAGE__); package MyApp::Session; $log->info(__PACKAGE__); package MyApp::Session::Settings; $log->info(__PACKAGE__); The messages of C and C would not be logged. The usage of categories is much faster than to filter by caller. =item B You can use this option to set a package name. Only messages from this packages will be logged. Example: my $log = Log::Handler->new(); $log->add(screen => { maxlevel => "info", filter_caller => qr/^Foo::Bar\z/, # or # filter_caller => "^Foo::Bar\z", }); package Foo::Bar; $log->info("log this"); package Foo::Baz; $log->info("but not that"); 1; This would only log the message from the package C. =item B This option is just the opposite of C. If you want to log messages from all callers but C: except_caller => qr/^Foo::Bar\z/ =item B You can set an alias if you want to get the output object later. Example: my $log = Log::Handler->new(); $log->add(screen => { maxlevel => 7, alias => "screen-out", }); my $screen = $log->output("screen-out"); $screen->log(message => "foo"); # or in one step $log->output("screen-out")->log(message => "foo"); =item B You can activate a debugger that writes C information about each active log level. The debugger is logging all defined values except C and C. Set C to 1 to activate the debugger. The debugger is set to 0 by default. =item B There are two debug modes: line(1) and block(2) mode. The default mode is 1. The line mode looks like this: use strict; use warnings; use Log::Handler; my $log = Log::Handler->new() $log->add(file => { filename => "*STDOUT", maxlevel => "debug", debug_trace => 1, debug_mode => 1 }); sub test1 { $log->warning() } sub test2 { &test1; } &test2; Output: Apr 26 12:54:11 [WARNING] CALL(4): package(main) filename(./trace.pl) line(15) subroutine(main::test2) hasargs(0) CALL(3): package(main) filename(./trace.pl) line(13) subroutine(main::test1) hasargs(0) CALL(2): package(main) filename(./trace.pl) line(12) subroutine(Log::Handler::__ANON__) hasargs(1) CALL(1): package(Log::Handler) filename(/usr/local/share/perl/5.8.8/Log/Handler.pm) line(713) subroutine(Log::Handler::_write) hasargs(1) CALL(0): package(Log::Handler) filename(/usr/local/share/perl/5.8.8/Log/Handler.pm) line(1022) subroutine(Devel::Backtrace::new) hasargs(1) wantarray(0) The same code example but the debugger in block mode would looks like this: debug_mode => 2 Output: Apr 26 12:52:17 [DEBUG] CALL(4): package main filename ./trace.pl line 15 subroutine main::test2 hasargs 0 CALL(3): package main filename ./trace.pl line 13 subroutine main::test1 hasargs 0 CALL(2): package main filename ./trace.pl line 12 subroutine Log::Handler::__ANON__ hasargs 1 CALL(1): package Log::Handler filename /usr/local/share/perl/5.8.8/Log/Handler.pm line 681 subroutine Log::Handler::_write hasargs 1 CALL(0): package Log::Handler filename /usr/local/share/perl/5.8.8/Log/Handler.pm line 990 subroutine Devel::Backtrace::new hasargs 1 wantarray 0 =item B This option let skip the C information the count of C. =back =head2 output() Call C to get the output object that you added with the option C. It's possible to access a output directly: $log->output($alias)->log(message => "booo"); For more information take a look to the option C. =head2 flush() Call C if you want to send flush to all outputs that can flush. Flush means to flush buffers and/or close and re-open outputs. If you want to send it only to some outputs you can pass the aliases. $log->flush(); # flush all $log->flush("foo", "bar"); # flush only foo and bar If option S<"die_on_errors"> is set to 0 then you can intercept errors with: $log->flush or die $log->errstr; =head2 errstr() Call C if you want to get the last error message. This is useful if you set C to C<0> and the handler wouldn't die on failed write operations. use Log::Handler; my $log = Log::Handler->new(); $log->add(file => { filename => "file.log", maxlevel => "info", die_on_errors => 0, }); $log->info("Hello World!") or die $log->errstr; Or unless ( $log->info("Hello World!") ) { $error_string = $log->errstr; # do something with $error_string } The exception is that the handler dies in any case if the call of C or C fails because on missing or wrong settings! =head2 config() With this method it's possible to load your output configuration from a file. $log->config(config => "file.conf"); Or $log->config(config => { file => [ { alias => "error_log", filename => "error.log", maxlevel => "warning", minlevel => "emerg", priority => 1 }, { alias => "common_log", filename => "common.log", maxlevel => "info", minlevel => "emerg", priority => 2 }, ], screen => { alias => "screen", maxlevel => "debug", minlevel => "emerg", log_to => "STDERR", }, }); The key S<"default"> is used here to define default parameters for all file outputs. All other keys (C, C) are used as aliases. Take a look into the documentation of L for more information. =head2 reload() With the method C it's possible to reload the logging machine. Just pass the complete new configuration for all outputs, it works exaclty like C. At first you should know that it's highly recommended to set a alias for each output. If you don't set a alias then the logger doesn't know which output-objects to reload. If a output-objects doesn't have a alias then the objects will be removed and the new configuration will be added. Example: logger.conf alias = debug filename = debug.log maxlevel = debug minlevel = emerg alias = common filename = common.log maxlevel = info minlevel = emerg Load the configuration $log->config(config => "logger.conf"); Now change the configuration in logger.conf alias = common filename = common.log maxlevel = notice minlevel = emerg alias = sendmail from = bar@foo.example to = foo@bar.example subject = your subject What happends now... The file-output with the alias C will be removed, the file-output with the alias C will be reloaded and the output with the alias C will be added. If you don't want that output-objects will be removed because they were added internal, then you can set the option C to 0. Example: $log->config(config => "logger.conf"); $log->add( forward => { forward_to => \&my_func, remove_on_reload => 0, } ); The forward-output is not removed after a reload. =head2 validate() The method C expects the same arguments like C and C. Maybe you want to validate your options before you pass them to C or C. Example: my $log = Log::Handler->new(); $log->config( config => \%config ); # and maybe later if ( $log->validate( config => \%new_config ) ) { $log->reload( config => \%new_config ); } else { warn "unable to reload configuration"; warn $log->errstr; } =head2 set_pattern() With this option you can set your own placeholders. Example: $log->set_pattern("%X", "key_name", sub { "value" }); # or $log->set_pattern("%X", "key_name", "value"); Then you can use this pattern in your message layout: $log->add(file => { filename => "file.log", message_layout => "%X %m%N", }); Or use it with C: sub func { my $m = shift; print "$m->{key_name} $m->{message}\n"; } $log->add(forward => { forward_to => \&func, message_pattern => "%X %m", }); Note: valid character for the key name are: C<[%\w\-\.]+> =head2 set_level() With this method it's possible to change the log level at runtime. To change the log level it's necessary to use a alias - see option C. $log->set_level( $alias => { # option alias minlevel => $new_minlevel, maxlevel => $new_maxlevel, } ); =head2 set_default_param() With this methods it's possible to overwrite the default settings for new outputs. Normally you would do something like $log->add( file => { filename => "debug.log", maxlevel => "info", timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" } ); $log->add( file => { filename => "error.log", maxlevel => "error", timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" } ); Now you can simplify it with $log->set_default_param( timeformat => "%b %d %Y %H:%M:%S", message_layout => "[%T] %L %P %t %m (%C)" ); $logg->add( file => { filename => "debug.log", maxlevel => "info" } ); $log->add( file => { filename => "error.log", maxlevel => "error" } ); =head2 create_logger() C is the same like C but it creates a global logger. my $log = Log::Handler->create_logger("myapp"); =head2 get_logger() With C it's possible to get a logger that was created with C or with use Log::Handler "myapp"; Just call my $log = Log::Handler->get_logger("myapp"); If the logger does not exists then a new logger will be created and returned. =head2 exists_logger() With C it's possible to check if a logger exists and it returns TRUE or FALSE. =head1 EXAMPLES L =head1 BENCHMARK The benchmark (examples/benchmark/benchmark.pl) runs on a Intel Core i7-920 with the following result: simple pattern output took : 1 wallclock secs ( 1.26 usr + 0.01 sys = 1.27 CPU) @ 78740.16/s (n=100000) default pattern output took : 2 wallclock secs ( 2.08 usr + 0.15 sys = 2.23 CPU) @ 44843.05/s (n=100000) complex pattern output took : 4 wallclock secs ( 3.22 usr + 0.23 sys = 3.45 CPU) @ 28985.51/s (n=100000) message pattern output took : 3 wallclock secs ( 2.72 usr + 0.16 sys = 2.88 CPU) @ 34722.22/s (n=100000) suppressed output took : 0 wallclock secs ( 0.08 usr + 0.00 sys = 0.08 CPU) @ 1250000.00/s (n=100000) filtered caller output took : 2 wallclock secs ( 2.10 usr + 0.68 sys = 2.78 CPU) @ 35971.22/s (n=100000) suppressed caller output took : 1 wallclock secs ( 0.54 usr + 0.00 sys = 0.54 CPU) @ 185185.19/s (n=100000) filtered messages output took : 3 wallclock secs ( 2.62 usr + 0.08 sys = 2.70 CPU) @ 37037.04/s (n=100000) =head1 EXTENSIONS Send me a mail if you have questions. =head1 PREREQUISITES Prerequisites for all modules: Carp Data::Dumper Fcntl Params::Validate POSIX Time::HiRes Sys::Hostname UNIVERSAL Recommended modules: Config::General Config::Properties DBI IO::Socket Net::SMTP YAML Just for the test suite: File::Spec Test::More =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . =head1 AUTHOR Jonny Schulz . =head1 QUESTIONS Do you have any questions or ideas? MAIL: IRC: irc.perl.org#perl If you send me a mail then add Log::Handler into the subject. =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler; use strict; use warnings; use Carp; use Params::Validate qw//; use Log::Handler::Output; use Log::Handler::Config; use Log::Handler::Pattern; use UNIVERSAL; use base qw(Log::Handler::Levels); our $VERSION = "0.90"; our $ERRSTR = ""; # $TRACE and $CALLER_LEVEL are both used as global # variables in other packages as well. You shouldn't # manipulate them if you don't know what you do. # # $TRACE is used to turn on/off tracing. # # $CALLER_LEVEL is used to determine the current caller level our $CALLER_LEVEL = 0; our $TRACE = 0; # safe logger by app my %LOGGER; # Some constants... use constant PRIORITY => 10; use constant BOOL_RX => qr/^[01]\z/; use constant NUMB_RX => qr/^\d+\z/; use constant LEVEL_RX => qr/^(?: 8 | nothing | 7 | debug | 6 | info | 5 | notice | 4 | warning | warn | 3 | error | err | 2 | critical | crit | 1 | alert | 0 | emergency | emerg | fatal )\z/x; # to convert minlevel and maxlevel to a number our %LEVEL_BY_STRING = ( DEBUG => 7, INFO => 6, NOTICE => 5, WARNING => 4, WARN => 4, ERROR => 3, ERR => 3, CRITICAL => 2, CRIT => 2, ALERT => 1, EMERGENCY => 0, EMERG => 0, FATAL => 0, ); # to iterate from minlevel to maxlevel and # create an HoA with all active levels our @LEVEL_BY_NUM = qw( EMERGENCY ALERT CRITICAL ERROR WARNING NOTICE INFO DEBUG NOTHING ); # shortcuts for each output our %AVAILABLE_OUTPUTS = ( file => "Log::Handler::Output::File", email => "Log::Handler::Output::Email", sendmail => "Log::Handler::Output::Sendmail", forward => "Log::Handler::Output::Forward", dbi => "Log::Handler::Output::DBI", screen => "Log::Handler::Output::Screen", socket => "Log::Handler::Output::Socket", gearman => "Log::Handler::Output::Gearman", ); # use Log::Handler foo => "LOGFOO", bar => "LOGBAR"; # use Log::Handler qw/foo LOGFOO bar LOGBAR/; sub import { return unless @_ > 1; my $class = shift; my %create = @_ > 1 ? @_ : (@_, undef); my $caller = (caller)[0]; foreach my $appl (keys %create) { my $export = $create{$appl}; my $logger = (); if (!exists $LOGGER{$appl}) { $LOGGER{$appl} = __PACKAGE__->new(); } if ($export) { no strict "refs"; my $method = $caller."::".$export; *{$method} = sub { $LOGGER{$appl} }; } } } sub get_logger { @_ == 2 || croak 'Usage: Log::Handler->get_logger($app)'; my ($class, $logger) = @_; if (!exists $LOGGER{$logger}) { return $class->create_logger($logger); } return $LOGGER{$logger}; } sub create_logger { @_ == 2 || croak 'Usage: Log::Handler->create_logger($app)'; my ($class, $logger) = @_; if (!exists $LOGGER{$logger}) { $LOGGER{$logger} = __PACKAGE__->new(); } return $LOGGER{$logger}; } sub exists_logger { @_ == 2 || croak 'Usage: Log::Handler->exists_logger($app)'; my ($class, $logger) = @_; if (exists $LOGGER{$logger}) { return 1; } return undef; } sub new { my $class = shift; my $self = bless { priority => PRIORITY, # start priority levels => { }, # outputs (Output.pm) stored by active levels alias => { }, # outputs (Output.pm) stored by an alias outputs => [ ], # all Output::* objects - for flush() pattern => # default pattern &Log::Handler::Pattern::get_pattern, param_defaults => { } }, $class; if (@_) { if ($_[0] eq "config") { $self->config(@_); } else { $self->add(@_); } } return $self; } sub add { my ($self, @args) = @_; if ($args[0] && $args[0] eq "config") { return $self->config(@args); } if (@args > 2) { if (@args % 2 != 0) { Carp::croak 'Odd number of arguments to Log::Handler::add'; } while (@args) { my $type = shift @args; my $conf = shift @args; $self->add($type, $conf); } return 1; } # At first the config will be splitted into # the package name (Log::Handler::Output::*), # the options for the handler and the options # for the output-module. my ($package, $h_opts, $o_opts) = $self->_split_config(@args); # In the next step the handler options # must be validated. $h_opts = $self->_validate_options($h_opts); # Create the new output-object. my $output = $self->_new_output($package, $h_opts, $o_opts); # Add the output to $self. $self->_add_output($output); return 1; } sub config { @_ > 1 or Carp::croak 'Usage: $log->config( %param )'; my $self = shift; my $config = Log::Handler::Config->config(@_); # Structure: # $config->{file} = [ output config ]; # $config->{dbi} = [ output config ]; foreach my $type (keys %$config) { for my $c (@{$config->{$type}}) { $self->add($type, $c); } } return 1; } sub validate { my $self = shift; my @v_opts = (); # validated options eval { my $config = Log::Handler::Config->config(@_); foreach my $type (keys %$config) { foreach my $output_config (@{ $config->{$type} }) { my ($package, $h_opts, $o_opts) = $self->_split_config($type, $output_config); $h_opts = $self->_validate_options($h_opts); $o_opts = $package->validate($o_opts) or die $package->errstr; push @v_opts, { p => $package, h => $h_opts, o => $o_opts, n => $output_config }; } } }; if ($@) { return $self->_raise_error($@); } return \@v_opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); if (!$opts) { return undef; } # Store all aliases that were reloaded or added, # because all output-objects that weren't reloaded # should be removed. my %reloaded = (); # Reload in a eval block to prevent that the # program dies - daemons shouldn't die :-) eval { foreach my $output_config (@$opts) { my $package = $output_config->{p}; # package name like Log::Handler::Output::File my $h_opts = $output_config->{h}; # handler options to reload my $o_opts = $output_config->{o}; # output options to reload my $n_opts = $output_config->{n}; # add a new output my $alias = $h_opts->{alias}; $reloaded{$alias} = 1; # If the alias doesn't exists then a new # output-objects is created, otherwise the # output-object is reloaded. if (!$self->output($alias)) { # If the alias does not exists we use # the alias that was generated by validate(). if (!exists $n_opts->{alias}) { $n_opts->{alias} = $h_opts->{alias}; } # Add the new output to Log::Handler $self->add($package => $n_opts); } else { $self->{alias}->{$alias}->reload($h_opts); $self->output($alias)->reload($o_opts) or die $self->output($alias)->errstr; } } }; if ($@) { return $self->_raise_error($@); } # Rebuild the arrays... $self->{levels} = { }; $self->{outputs} = [ ]; foreach my $alias (keys %{ $self->{alias} }) { my $output = $self->{alias}->{$alias}; # Delete all objects that wasn't reloaded and have # set the flag "remove_on_reload". if (!exists $reloaded{$alias} && $output->{remove_on_reload}) { # At this point the output object should be destroyed, # because the last reference was stored here. eval { delete $self->{alias}->{$alias} }; if ($@) { warn $@; } } else { # At this point the output object should be destroyed, $self->_add_output($output); } } return 1; } sub set_default_param { my $self = shift; while (@_) { my $param = shift; my $value = shift; $self->{param_defaults}->{$param} = $value; } } sub set_pattern { (@_ == 3 || @_ == 4) or Carp::croak 'Usage: $log->set_pattern( $pattern, $name, $code )'; my $self = shift; my $pattern = shift; # If no $name is set then we use $pattern as name my ($name, $code) = @_ == 2 ? @_ : ($pattern, @_); if ($pattern !~ /^%[a-ln-z]\z/i) { Carp::croak "invalid pattern '$pattern'"; } if (!defined $name || $name !~ /^[%\w\-\.]+\z/) { Carp::croak "invalid/missing name for pattern '$pattern'"; } if (ref($code) ne "CODE") { my $str = $code; $code = sub { $str }; } # Structure: # $self->{pattern}->{"%X"}->{name} = "name-of-x"; # $self->{pattern}->{"%X"}->{code} = "value-of-x"; $self->{pattern}->{$pattern}->{name} = $name; $self->{pattern}->{$pattern}->{code} = $code; } sub set_level { @_ == 3 or Carp::croak 'Usage: $log->set_level( $alias => { minlevel => $min, maxlevel => $max } )'; my ($self, $name, $new) = @_; my $alias = $self->{alias}; if (!exists $alias->{$name}) { Carp::croak "alias '$name' does not exists"; } if (ref($new) ne "HASH") { Carp::croak "the second parameter to set_level() must be a hash reference"; } if (!defined $new->{minlevel} && !defined $new->{maxlevel}) { Carp::croak "no new level given to set_level()"; } foreach my $level (qw/minlevel maxlevel/) { next unless defined $new->{$level}; if ($new->{$level} =~ LEVEL_RX) { $alias->{$name}->{$level} = $new->{$level}; next if $new->{$level} =~ /^\d\z/; $new->{$level} = uc($new->{$level}); $new->{$level} = $LEVEL_BY_STRING{ $new->{$level} }; $alias->{$name}->{$level} = $new->{$level}; } else { Carp::croak "invalid level set to set_level()"; } } $alias->{$name}->{levels} = { }; my $levels = $self->{levels} = { }; foreach my $level_num ($alias->{$name}->{minlevel} .. $alias->{$name}->{maxlevel}) { my $level = $LEVEL_BY_NUM[ $level_num ]; $alias->{$name}->{levels}->{$level} = 1; if ($level_num < 4) { $alias->{$name}->{levels}->{FATAL} = 1; } } foreach my $output (@{ $self->{outputs} }) { foreach my $level (keys %{$output->{levels}}) { if ($levels->{$level}) { my @old_order = @{$levels->{$level}}; push @old_order, $output; $levels->{$level} = [ map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, $_->{priority} ] } @old_order ]; } else { push @{$levels->{$level}}, $output; } } } return 1; } sub output { @_ == 2 or Carp::croak 'Usage: $log->output( $alias )'; my ($self, $name) = @_; my $alias = $self->{alias}; return exists $alias->{$name} ? $alias->{$name}->{output} : undef; } sub flush { my ($self, @alias) = @_; my $errors = (); if (@alias) { foreach my $name (@alias) { my $output = $self->output($name); next unless $output && UNIVERSAL::can($output, "flush"); if ( !$output->flush ) { if ( defined $errors ) { $errors .= "; " . $output->errstr; } else { $errors = $output->errstr; } } } } else { foreach my $output (@{$self->{outputs}}) { next unless UNIVERSAL::can($output, "flush"); if ( !$output->flush ) { if ( defined $errors ) { $errors .= "; " . $output->errstr; } else { $errors = $output->errstr; } } } } return defined $errors ? $self->_raise_error($errors) : 1; } sub errstr { return $ERRSTR; } # # private stuff # sub _build_params { my $self = shift; my %params = ( timeformat => { type => Params::Validate::SCALAR, default => "%b %d %H:%M:%S", }, dateformat => { type => Params::Validate::SCALAR, default => "%b %d %Y", }, message_layout => { type => Params::Validate::SCALAR, default => "%T [%L] %m", }, message_pattern => { type => Params::Validate::SCALAR | Params::Validate::ARRAYREF, optional => 1, }, prepare_message => { type => Params::Validate::CODEREF, optional => 1, }, newline => { type => Params::Validate::SCALAR, regex => BOOL_RX, default => 1, }, minlevel => { type => Params::Validate::SCALAR, regex => LEVEL_RX, default => 0, }, maxlevel => { type => Params::Validate::SCALAR, regex => LEVEL_RX, default => 4, }, die_on_errors => { type => Params::Validate::SCALAR, regex => BOOL_RX, default => 1, }, priority => { type => Params::Validate::SCALAR, regex => NUMB_RX, default => undef, }, debug_trace => { type => Params::Validate::SCALAR, regex => BOOL_RX, default => 0, }, debug_mode => { type => Params::Validate::SCALAR, regex => NUMB_RX, default => 1, }, debug_skip => { type => Params::Validate::SCALAR, regex => NUMB_RX, default => 0, }, alias => { type => Params::Validate::SCALAR, optional => 1, }, skip_message => { type => Params::Validate::SCALAR, optional => 1 }, filter_message => { type => Params::Validate::SCALAR # "foo" | Params::Validate::SCALARREF # qr/foo/ | Params::Validate::CODEREF # sub { shift->{message} =~ /foo/ } | Params::Validate::HASHREF, # matchN, condition optional => 1, }, filter_caller => { type => Params::Validate::SCALAR | Params::Validate::SCALARREF, optional => 1, }, category => { type => Params::Validate::SCALAR, optional => 1, }, except_caller => { type => Params::Validate::SCALAR | Params::Validate::SCALARREF, optional => 1, }, remove_on_reload => { type => Params::Validate::SCALAR, default => 1, } ); foreach my $param (keys %{$self->{param_defaults}}) { if (!exists $params{$param}) { Carp::croak "parameter '$param' does not exists"; } $params{$param}{default} = $self->{param_defaults}->{$param}; } return \%params; } sub _split_config { my $self = shift; my $type = shift; my $args = shift || { }; my $package = (); # Split the handler and output options from $args. my ($handler_opts, $output_opts) = $self->_split_options($args); # Try to determine which output is wanted... if (exists $AVAILABLE_OUTPUTS{$type}) { $package = $AVAILABLE_OUTPUTS{$type}; } elsif ($type =~ /::/) { $package = $type; } else { $package = "Log::Handler::Output::" . ucfirst($type); } eval "require $package"; if ($@) { Carp::croak($@); } return ($package, $handler_opts, $output_opts); } sub _new_output { my ($self, $package, $h_opts, $o_opts) = @_; my $o_obj = $package->new($o_opts) or Carp::croak $package->errstr; my $o_main_obj = Log::Handler::Output->new($h_opts, $o_obj); return $o_main_obj; } sub _split_options { my ($self, $opts) = @_; my (%handler_opts, %output_opts); # It's possible to pass all options for the handler and for the # output to add(). These options must be splitted. The options # for the handler will be passed to Log::Handler::Output. The # options for the output will be passed - as example - to # Log::Handler::Output::File. my %split_options = map { $_ => 0 } qw( alias debug_mode debug_skip debug_trace die_on_errors filter filter_message filter_caller skip_message except_caller maxlevel message_layout message_pattern prepare_message minlevel newline priority timeformat dateformat remove_on_reload category ); foreach my $key (keys %$opts) { if (exists $split_options{$key}) { $handler_opts{$key} = $opts->{$key}; } else { $output_opts{$key} = $opts->{$key}; } } return (\%handler_opts, \%output_opts); } sub _add_output { my ($self, $output) = @_; my $levels = $self->{levels}; # Structure: # $self->{levels}->{INFO} = [ outputs ordered by priority ] # # All outputs that would log the level INFO will be stored to the # hash-tree $self->{levels}->{INFO}. On this way it's possible # to check very fast if the level is active # # my $levels = $self->{levels}; # if (exists $levels->{INFO}) { ... } # # and loop over all output objects and pass the message to it. foreach my $level (keys %{$output->{levels}}) { if ($levels->{$level}) { my @old_order = @{$levels->{$level}}; push @old_order, $output; $levels->{$level} = [ map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, $_->{priority} ] } @old_order ]; } else { push @{$levels->{$level}}, $output; } } # Structure: # $self->{alias}->{$alias} = $output_object # # All outputs with an alias are stored to this hash tree. # Each output can be fetched with output($alias); if ($output->{alias}) { my $alias = $output->{alias}; $self->{alias}->{$alias} = $output; } # save all outputs here push @{$self->{outputs}}, $output; } sub _validate_options { my ($self, @args) = @_; my $pattern = $self->{pattern}; my $alias = $self->{alias}; my %wanted = (); # Option "filter" is deprecated. if (exists $args[0]{filter}) { $args[0]{filter_message} = delete $args[0]{filter}; } my %options = Params::Validate::validate(@args, $self->_build_params); if ($options{category}) { my $category = $options{category}; $category =~ s/\s//g; $category = "^(?:" . join("|", map { $_ } split(/,/, $category) ) . ")(?:::|\\z)"; $options{category} = qr/$category/; } if (!$options{alias}) { for (;;) { my $rand = rand(); if (exists $alias->{$rand}) { next; } $options{alias} = $rand; last; } } if ($options{filter_message}) { $options{filter_message} = $self->_validate_filter($options{filter_message}); } # set a default priority if not set if (!defined $options{priority}) { $options{priority} = $self->{priority}++; } # replace the level strings with numbers foreach my $opt (qw/minlevel maxlevel/) { next if $options{$opt} =~ /^\d\z/; my $level = uc($options{$opt}); $options{$opt} = $LEVEL_BY_STRING{$level}; } # iterate from minlevel to maxlevel and create # a hash tree with all active levels foreach my $level_num ($options{minlevel} .. $options{maxlevel}) { my $level = $LEVEL_BY_NUM[ $level_num ]; $options{levels}{$level} = 1; next if $level_num > 3; $options{levels}{FATAL} = 1; } if ($options{message_pattern}) { if (!ref($options{message_pattern})) { $options{message_pattern} = [ split /\s+/, $options{message_pattern} ]; } foreach my $p (@{$options{message_pattern}}) { if (!exists $pattern->{$p}) { Carp::croak "undefined pattern '$p'"; } $wanted{$p} = undef; } # If message_pattern is set to "%T %L %m" then the code # should looks like: # # sub { # my ($w, $m) = @_; # %wanted pattern, %message # $m->{$_} = $w->{$_} for qw/time level message/; # } my $func = 'sub { my ($w, $m) = @_; $m->{$_} = $w->{$_} for qw/'; $func .= join(" ", map { $pattern->{$_}->{name} } keys %wanted); $func .= "/ }"; $options{message_pattern_func} = $func; $options{message_pattern_code} = eval $func; Carp::croak $@ if $@; } if ($options{message_layout}) { my (@chunks, $func); # If the message layout is set to "%T [%L] %m" then the code # should looks like: # # sub { # my ($w, $m) = @_; # %wanted pattern, %message # $m->{"message"} = # $w->{"time"} # . " [" # . $w->{"level"} # . "] " # . $w->{"message"} # ); # } foreach my $p ( split /(?:(%[a-zA-Z])|(%)%)/, $options{message_layout} ) { next unless defined $p && length($p); if ( exists $pattern->{$p} ) { $wanted{$p} = undef; my $name = $pattern->{$p}->{name}; push @chunks, "\$w->{'$name'}"; } else { # quote backslash and apostrophe $p =~ s/\\/\\\\/g; $p =~ s/'/\\'/g; push @chunks, "'$p'"; } } if (@chunks) { $func = 'sub { my ($w, $m) = @_; $m->{message} = '; $func .= join(".", @chunks); $func .= " }"; } $options{message_layout_func} = $func; $options{message_layout_code} = eval $func; Carp::croak $@ if $@; } # %m is default delete $wanted{"%m"}; # The references to the patterns are stored to all outputs. # If a pattern will be changed with set_pattern() then the # changed pattern is available for each output. $options{wanted_pattern} = [ map { $pattern->{$_} } keys %wanted ]; return \%options; } sub _validate_filter { my ($self, $args) = @_; my $ref = ref($args); my %filter; # A filter can be passed as CODE, as a Regexp, as a simple string # that will be embed in a Regexp or as a condition. if ($ref eq "CODE") { $filter{code} = $args; } elsif ($ref eq "Regexp") { $filter{code} = sub { $_[0]->{message} =~ $args }; } elsif (!$ref) { $filter{code} = sub { $_[0]->{message} =~ /$args/ }; } else { %filter = %$args; # Structure: # $filter->{code} = &code # $filter->{func} = $code_as_string # $filter->{condition} = $users_condition # $filter->{result}->{matchN} = $result_of_matchN # $filter->{matchN} = qr// # # Each matchN will be checked on the message and the BOOL results # will be stored to $filter->{result}->{matchN}. Then the results # will be passed to &code. &code returns 0 or 1. # # As example if the filter is set to # # filter => { # match1 => qr/foo/, # match2 => qr/bar/, # condition => "(match1 && match2)", # } # # Then the bool results will be saved: # # $filter->{result}->{match1} = $message =~ $filter->{match1}; # $filter->{result}->{match2} = $message =~ $filter->{match2}; # # The code for the filter should looks like: # # $filter->{code} = # sub { # my $m = shift; # ($m->{match1} && $m->{match2}) # } # # &$code($filter->{result}); if (!defined $filter{condition} || $filter{condition} !~ /\w/) { Carp::croak "missing condition for paramater 'filter'"; } # Remove all valid characters from the condition # and check if invalid characters left. my $cond = $filter{condition}; $cond =~ s/match\d+//g; $cond =~ s/[()&|!<>=\s\d]+//; if ($cond) { Carp::croak "invalid characters in condition: '$cond'"; } foreach my $m ($filter{condition} =~ /(match\d+)/g) { if (!exists $filter{$m}) { Carp::croak "missing regexp for $m"; } $ref = ref($filter{$m}); if (!$ref) { $filter{$m} = qr/$filter{$m}/; } elsif ($ref ne "Regexp") { Carp::croak "invalid value for option 'filter:$m'"; } $filter{result}{$m} = ""; } $filter{func} = 'sub { my $m = shift; '; $filter{func} .= $filter{condition}."; }"; $filter{func} =~ s/(match\d+)/\$m->{$1}/g; $filter{code} = eval $filter{func}; } return \%filter; } sub _raise_error { $ERRSTR = $_[1]; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/0000750000000000000000000000000013702610773015101 5ustar rootrootLog-Handler-0.90/lib/Log/Handler/Levels.pm0000640000000000000000000001465412235521676016707 0ustar rootroot=head1 NAME Log::Handler::Levels - All levels for Log::Handler. =head1 DESCRIPTION Base class for Log::Handler. Just for internal usage and documentation. =head1 METHODS =head2 Default log level =over =item B =item B =item B =item B, B =item B, B =item B, B =item B =item B, B =back =head2 Checking for active levels =over =item B =item B =item B =item B, B =item B, B =item B, B =item B =item B, B =back =head2 Special level =over =item B Alternative for the levels C - C. =item B Check if one of the levels C - C is active. =back =head2 Special methods =over =item B This method is very useful if you want to add a full backtrace to your message. Maybe you want to intercept unexpected errors and want to know who called C. $SIG{__DIE__} = sub { $log->trace(emergency => @_) }; By default the backtrace is logged as level C. # would log with the level debug $log->trace('who called who'); If you want to log with another level then you can pass the level as first argument: $log->trace(info => $message); =item B If you want to dump something then you can use C. The default level is C. my %hash = (foo => 1, bar => 2); $log->dump(\%hash); If you want to log with another level then you can pass the level as first argument: $log->dump($level => \%hash); =item B This method logs the message to the output and then call C with the level C by default. $log->die('an emergency error here'); If you want to log with another level, then you can pass the level as first argument: $log->die(fatal => 'an emergency error here'); =item B With this method it's possible to log messages with the log level as first argument: $log->log(info => 'an info message'); Is the same like $log->info('an info message'); and $log->log('an info message'); If you log without a level then the default level is C. =back =head1 PREREQUISITES Carp Data::Dumper =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Levels; use strict; use warnings; use Carp; use Data::Dumper; our $VERSION = '0.07'; my %LEVELS_BY_ROUTINE = ( debug => 'DEBUG', info => 'INFO', notice => 'NOTICE', warning => 'WARNING', warn => 'WARNING', error => 'ERROR', err => 'ERROR', critical => 'CRITICAL', crit => 'CRITICAL', alert => 'ALERT', emergency => 'EMERGENCY', emerg => 'EMERGENCY', fatal => 'FATAL', ); foreach my $routine (keys %LEVELS_BY_ROUTINE) { my $level = $LEVELS_BY_ROUTINE{$routine}; { # start "no strict 'refs'" block no strict 'refs'; # -------------------------------------------------------------- # Creating the syslog level methods # -------------------------------------------------------------- *{"$routine"} = sub { use strict 'refs'; my $self = shift; my $levels = $self->{levels}; my ($errors, $caller); if ( !$levels->{$level} ) { return 1; } foreach my $output ( @{$levels->{$level}} ) { if ($output->{category} || $output->{filter_caller} || $output->{except_caller}) { if (!$caller) { $caller = (caller($Log::Handler::CALLER_LEVEL))[0]; } if ($output->{category}) { my $category = $output->{category}; return 1 if $caller !~ $output->{category}; } elsif ($output->{filter_caller}) { return 1 if $caller !~ $output->{filter_caller}; } elsif ($output->{except_caller}) { return 1 if $caller =~ $output->{except_caller}; } } if ( !$output->log($level, @_) ) { if ( defined $errors ) { $errors .= '; ' . $output->errstr; } else { $errors = $output->errstr; } } } return defined $errors ? $self->_raise_error($errors) : 1; }; # -------------------------------------------------------------- # Creating the is_ methods # -------------------------------------------------------------- *{"is_$routine"} = sub { use strict 'refs'; my $self = shift; my $levels = $self->{levels}; return $levels->{$level} ? 1 : 0; }; } # end "no strict 'refs'" block } sub log { my $self = shift; my $level = @_ > 1 ? lc(shift) : 'info'; if (!exists $LEVELS_BY_ROUTINE{$level}) { $level = 'info'; } local $Log::Handler::CALLER_LEVEL = 1; return $self->$level(@_); } sub trace { my $self = shift; my $level = @_ > 1 ? lc(shift) : 'debug'; if (!exists $LEVELS_BY_ROUTINE{$level}) { $level = 'debug'; } local $Log::Handler::CALLER_LEVEL = 1; local $Log::Handler::TRACE = 1; return $self->$level(@_); } sub die { my $self = shift; my $level = @_ > 1 ? lc(shift) : 'emergency'; if (!exists $LEVELS_BY_ROUTINE{$level}) { $level = 'emergency'; } local $Log::Handler::CALLER_LEVEL = 1; my @caller = caller; $self->$level(@_, "at line $caller[2]"); Carp::croak @_; }; sub dump { my $self = shift; my $level = @_ > 1 ? lc(shift) : 'debug'; my $is_level = "is_$level"; if (!exists $LEVELS_BY_ROUTINE{$level}) { $level = 'debug'; } local $Log::Handler::CALLER_LEVEL = 1; return $self->$is_level ? $self->$level(Dumper(@_)) : 1; } 1; Log-Handler-0.90/lib/Log/Handler/Output.pm0000640000000000000000000001154012537703256016745 0ustar rootroot=head1 NAME Log::Handler::Output - The output builder class. =head1 DESCRIPTION Just for internal usage! =head1 METHODS =head2 new() =head2 log() =head2 reload() =head2 flush() =head2 errstr() =head1 PREREQUISITES Carp UNIVERSAL =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output; use strict; use warnings; use Carp; use UNIVERSAL; our $VERSION = "0.10"; our $ERRSTR = ""; sub new { my ($class, $options, $output) = @_; my $self = bless $options, $class; $self->{output} = $output; return $self; } sub log { my $self = shift; my $level = shift; my $output = $self->{output}; my $message = { }; my $wanted = { message => join(" ", grep defined, @_) }; # The patterns must be generated for each output. The reason # is that each output can have their own time/date format # and the code which is executed can return another value. foreach my $r (@{$self->{wanted_pattern}}) { $wanted->{$r->{name}} = &{$r->{code}}($self, $level); } if ($self->{message_pattern}) { &{$self->{message_pattern_code}}($wanted, $message); } if ($self->{message_layout}) { &{$self->{message_layout_code}}($wanted, $message); } else { $message->{message} = $wanted->{message}; } if ($self->{message_pattern}) { if ($message->{message}) { $wanted->{message} = $message->{message}; } &{$self->{message_pattern_code}}($wanted, $message); } if ($self->{debug_trace} || $Log::Handler::TRACE) { $self->_add_trace($message); } if ($self->{skip_message} && $message->{message} =~ /$self->{skip_message}/) { return 1; } if ($self->{filter_message}) { $self->_filter_msg($message) or return 1; } if ($self->{prepare_message}) { eval { &{$self->{prepare_message}}($message) }; if ($@) { return $self->_raise_error("prepare_message failed - $@"); } } if ($self->{newline} && $message->{message} !~ /(?:\015|\012)\z/) { $message->{message} .= "\n"; } # The substr solution to determine if a newline exists # at the end of the message is ~60% faster than the regex. # Maybe it will be released in the future. #if ($self->{newline}) { # my $last = substr $message->{message}, -1, 1; # if ($last eq "\015" || $last eq "\012" || $last eq "\015\012" || $last eq "\012\015") { # $message->{message} .= "\n"; # } #} $output->log($message) or return $self->_raise_error($output->errstr); return 1; } sub flush { my $self = shift; my $output = $self->{output}; if ( UNIVERSAL::can($output, "flush") ) { $output->flush or return $self->_raise_error($output->errstr); } return 1; } sub reload { my ($self, $opts) = @_; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } } sub errstr { return $ERRSTR; } # # private stuff # sub _add_trace { my ($self, $message) = @_; my @caller = (); my $skip = $self->{debug_skip}; if ( $message->{message} =~ /.\z/ ) { $message->{message} .= "\n"; } for (my $i=0; my @c = caller($i); $i++) { my %frame; @frame{qw/package filename line subroutine hasargs wantarray evaltext is_require/} = @c[0..7]; push @caller, \%frame; } foreach my $i (reverse $skip..$#caller) { $message->{message} .= " " x 3 . "CALL($i):"; my $frame = $caller[$i]; foreach my $key (qw/package filename line subroutine hasargs wantarray evaltext is_require/) { next unless defined $frame->{$key}; if ($self->{debug_mode} == 1) { # line mode $message->{message} .= " $key($frame->{$key})"; } elsif ($self->{debug_mode} == 2) { # block mode $message->{message} .= "\n" . " " x 6 . sprintf("%-12s", $key) . $frame->{$key}; } } $message->{message} .= "\n"; } } sub _filter_msg { my ($self, $message) = @_; my $filter = $self->{filter_message}; my $result = $filter->{result}; my $code = $filter->{code}; my $return = (); if (!$filter->{condition}) { $return = &$code($message) || 0; } else { foreach my $match ( keys %$result ) { $result->{$match} = $message->{message} =~ /$filter->{$match}/ || 0; } $return = &$code($result); } return $return; } sub _raise_error { my $self = shift; $ERRSTR = shift; return undef unless $self->{die_on_errors}; my $class = ref($self); Carp::croak "$class: $ERRSTR"; } 1; Log-Handler-0.90/lib/Log/Handler/Config.pm0000640000000000000000000002540212422400723016636 0ustar rootroot=head1 NAME Log::Handler::Config - The main config loader. =head1 SYNOPSIS use Log::Handler; my $log = Log::Handler->new(); # Config::General $log->config(config => 'file.conf'); # Config::Properties $log->config(config => 'file.props'); # YAML $log->config(config => 'file.yaml'); Or use Log::Handler; my $log = Log::Handler->new(); $log->config( config => 'file.conf' plugin => 'YAML', ); =head1 DESCRIPTION This module makes it possible to load the configuration from a file. The configuration type is determined by the file extension. It's also possible to mix file extensions with another configuration types. =head1 PLUGINS Plugin name File extensions ------------------------------------------ Config::General cfg, conf Config::Properties props, jcfg, jconf YAML yml, yaml If the extension is not defined then C is used by default. =head1 METHODS =head2 config() With this method it's possible to load the configuration for your outputs. The following options are valid: =over 4 =item B With this option you can pass a file name or the configuration as a hash reference. $log->config(config => 'file.conf'); # or $log->config(config => \%config); =item B With this option it's possible to say which plugin you want to use. Maybe you want to use the file extension C with C, which is reserved for the plugin C. Examples: # this would use Config::General $log->config( config => 'file.conf' ); # this would force .conf with YAML $log->config( config => 'file.conf', plugin => 'YAML' ); =item B
If you want to write the configuration into a global configuration file then you can create a own section for the logger: filename = file.log minlevel = emerg maxlevel = warning minlevel = emerg maxlevel = debug foo = bar bar = baz baz = foo Now your configuration is placed in the C section. You can load this section with $log->config( config => 'file.conf', section => 'logger', ); # or if you load the configuration yourself to %config $log->config( config => \%config, section => 'logger', ); # or just $log->config( config => $config{logger} ); =back =head1 PLUGINS Config::General - inspired by the well known apache config format Config::Properties - Java-style property files YAML - optimized for human readability =head1 EXAMPLES =head2 Config structures A very simple configuration looks like: $log->config(config => { file => { alias => 'file1', filename => 'file1.log', maxlevel => 'info', minlevel => 'warn', }, screen => { alias => 'screen1', maxlevel => 'debug', minlevel => 'emerg', } }); Now, if you want to add another file-output then you can pass the outputs with a array reference: $log->config(config => { file => [ { alias => 'file1, filename => 'file1.log', maxlevel => 'info', minlevel => 'warn', }, { alias => 'file2', filename => 'file2.log', maxlevel => 'error', minlevel => 'emergency', } ], screen => { alias => 'screen1', maxlevel => 'debug', minlevel => 'emerg', }, }); It's also possible to pass the outputs as a hash reference. The hash keys "file1" and "file2" will be used as aliases. $log->config(config => { file => { file1 => { filename => 'file1.log', maxlevel => 'info', minlevel => 'warn', }, file2 => { filename => 'file2.log', maxlevel => 'error', minlevel => 'emergency', } }, screen => { alias => 'screen1', maxlevel => 'debug', minlevel => 'emerg', }, }); If you pass the configuration with the alias as a hash key then it's also possible to pass a section called "default". The options from this section will be used as defaults. $log->config(config => { file => { default => { # defaults for all file-outputs mode => 'append', }, file1 => { filename => 'file1.log', maxlevel => 'info', minlevel => 'warn', }, file2 => { filename => 'file2.log', maxlevel => 'error', minlevel => 'emergency', } }, screen => { alias => 'screen1', maxlevel => 'debug', minlevel => 'emerg', }, }); =head2 Examples for the config plugins =head3 Config::General alias = file1 fileopen = 1 reopen = 1 permissions = 0640 maxlevel = info minlevel = warn mode = append timeformat = %b %d %H:%M:%S debug_mode = 2 filename = example.log message_layout = '%T %H[%P] [%L] %S: %m' Or fileopen = 1 reopen = 1 permissions = 0640 maxlevel = info minlevel = warn mode = append timeformat = %b %d %H:%M:%S debug_mode = 2 filename = example.log message_layout = '%T %H[%P] [%L] %S: %m' =head3 YAML --- file: alias: file1 debug_mode: 2 filename: example.log fileopen: 1 maxlevel: info minlevel: warn mode: append permissions: 0640 message_layout: '%T %H[%P] [%L] %S: %m' reopen: 1 timeformat: '%b %d %H:%M:%S' Or --- file: file1: debug_mode: 2 filename: example.log fileopen: 1 maxlevel: info minlevel: warn mode: append permissions: 0640 message_layout: '%T %H[%P] [%L] %S: %m' reopen: 1 timeformat: '%b %d %H:%M:%S' =head3 Config::Properties file.alias = file1 file.reopen = 1 file.fileopen = 1 file.maxlevel = info file.minlevel = warn file.permissions = 0640 file.mode = append file.timeformat = %b %d %H:%M:%S file.debug_mode = 2 file.filename = example.log file.message_layout = '%T %H[%P] [%L] %S: %m' Or file.file1.alias = file1 file.file1.reopen = 1 file.file1.fileopen = 1 file.file1.maxlevel = info file.file1.minlevel = warn file.file1.permissions = 0640 file.file1.mode = append file.file1.timeformat = %b %d %H:%M:%S file.file1.debug_mode = 2 file.file1.filename = example.log file.file1.message_layout = '%T %H[%P] [%L] %S: %m' =head1 PREREQUISITES Carp Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Config; use strict; use warnings; our $VERSION = '0.09'; use Carp; use File::Spec; use Params::Validate; sub config { my $self = shift; my $params = $self->_validate(@_); my $config = $self->_get_config($params); if (ref($config) ne 'HASH') { croak "Bad config structure!"; } # Structure: # $log_config{file} = [ \%a, \%b, \%c ] # $log_config{dbi} = [ \%a, \%b, \%c ] my %log_config; foreach my $type (keys %$config) { my $output = $config->{$type}; my $ref = ref($output); if ($ref eq 'HASH') { push @{$log_config{$type}}, $self->_get_hash_config($output); } elsif ($ref eq 'ARRAY') { push @{$log_config{$type}}, @$output; } else { croak "Bad config structure for '$type'"; } } return \%log_config; } # # private stuff # sub _get_config { my ($self, $params) = @_; my $config = (); my $plugin = $params->{plugin}; if (ref($params->{config})) { $config = $params->{config}; } elsif ($params->{config}) { eval "require $plugin"; if ($@) { croak "unable to load plugin '$plugin' - $@"; } $config = $plugin->get_config($params->{config}); } if ($params->{section}) { return $config->{ $params->{section} }; } return $config; } sub _get_hash_config { my ($self, $config) = @_; my @config = (); my %default = (); if (exists $config->{default}) { %default = %{ $config->{default} }; } foreach my $alias (keys %$config) { next if $alias eq "default"; my $param = $config->{$alias}; if (ref($param) ne 'HASH') { push @config, $config; last; } $param->{alias} = $alias; my %config = (%default, %$param); push @config, \%config; } return @config; } sub _validate { my $self = shift; my %options = Params::Validate::validate(@_, { config => { type => Params::Validate::SCALAR | Params::Validate::HASHREF | Params::Validate::ARRAYREF, optional => 1, }, plugin => { type => Params::Validate::SCALAR, optional => 1, }, section => { type => Params::Validate::SCALAR, optional => 1, }, }); my $ref = ref($options{config}); if ($ref ne 'HASH') { if ($ref eq 'ARRAY') { $options{config} = File::Spec->catfile(@{$options{config}}); } if (!$options{plugin}) { if ($options{config} =~ /\.ya{0,1}ml\z/) { $options{plugin} = 'Log::Handler::Plugin::YAML'; } elsif ($options{config} =~ /\.(?:props|jc(?:onf|fg))\z/) { $options{plugin} = 'Log::Handler::Plugin::Config::Properties'; } else { $options{plugin} = 'Log::Handler::Plugin::Config::General'; } } } return \%options; } 1; Log-Handler-0.90/lib/Log/Handler/Pattern.pm0000640000000000000000000000663613702610772017067 0ustar rootroot=head1 NAME Log::Handler::Output - The pattern builder class. =head1 DESCRIPTION Just for internal usage! =head1 FUNCTIONS =head2 get_pattern =head1 PREREQUISITES Carp POSIX Sys::Hostname Time::HiRes Log::Handler::Output =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Pattern; use strict; use warnings; use POSIX; use Sys::Hostname; use Time::HiRes; use Log::Handler::Output; use constant START_TIME => scalar Time::HiRes::gettimeofday; our $VERSION = "0.08"; my $progname = $0; $progname =~ s@.*[/\\]@@; sub get_pattern { return { '%L' => { name => 'level', code => \&_get_level }, '%T' => { name => 'time', code => \&_get_time }, '%D' => { name => 'date', code => \&_get_date }, '%P' => { name => 'pid', code => \&_get_pid }, '%H' => { name => 'hostname', code => sub { Sys::Hostname::hostname() } }, '%N' => { name => 'newline', code => sub { "\n" } }, '%S' => { name => 'progname', code => sub { $progname } }, '%U' => { name => 'user', code => \&_get_user }, '%G' => { name => 'group', code => \&_get_group }, '%C' => { name => 'caller', code => \&_get_caller }, '%r' => { name => 'runtime', code => \&_get_runtime }, '%t' => { name => 'mtime', code => \&_get_hires }, '%m' => { name => 'message', code => \&_get_message }, '%p' => { name => 'package', code => \&_get_c_pkg }, '%f' => { name => 'filename', code => \&_get_c_file }, '%l' => { name => 'line', code => \&_get_c_line }, '%s' => { name => 'subroutine', code => \&_get_c_sub }, } } # ------------------------------------------ # Arguments: # $_[0] -> Log::Handler::Output object # $_[1] -> Log level # ------------------------------------------ sub _get_level { $_[1] } sub _get_time { POSIX::strftime($_[0]->{timeformat}, localtime) } sub _get_date { POSIX::strftime($_[0]->{dateformat}, localtime) } sub _get_pid { $$ } sub _get_caller { my @c = caller(2+$Log::Handler::CALLER_LEVEL); "$c[1], line $c[2]" } sub _get_c_pkg { (caller(2+$Log::Handler::CALLER_LEVEL))[0] } sub _get_c_file { (caller(2+$Log::Handler::CALLER_LEVEL))[1] } sub _get_c_line { (caller(2+$Log::Handler::CALLER_LEVEL))[2] } sub _get_c_sub { (caller(3+$Log::Handler::CALLER_LEVEL))[3]||"" } sub _get_runtime { return sprintf('%.6f', Time::HiRes::gettimeofday - START_TIME) } sub _get_user { getpwuid($<) || $< } sub _get_group { getgrgid($(+0) || $(+0 } sub _get_hires { my $self = shift; if (!$self->{timeofday}) { $self->{timeofday} = Time::HiRes::gettimeofday; return sprintf('%.6f', $self->{timeofday} - START_TIME); } my $new_time = Time::HiRes::gettimeofday; my $cur_time = $new_time - $self->{timeofday}; $self->{timeofday} = $new_time; return sprintf('%.6f', $cur_time); } 1; Log-Handler-0.90/lib/Log/Handler/Output/0000750000000000000000000000000012537703111016373 5ustar rootrootLog-Handler-0.90/lib/Log/Handler/Output/Forward.pm0000640000000000000000000000744212235521676020356 0ustar rootroot=head1 NAME Log::Handler::Output::Forward - Forward messages to routines. =head1 SYNOPSIS use Log::Handler::Output::Forward; my $forwarder = Log::Handler::Output::Forward->new( forward_to => sub { }, arguments => [ "foo" ], ); $forwarder->log(message => $message); =head1 DESCRIPTION This output module makes it possible to forward messages to sub routines. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::Forward object. The following options are possible: =over 4 =item B This option excepts a code reference. Please note that the message is forwarded as a hash reference. If you change it then this would have an effect to all outputs. =item B With this option you can define arguments that will be passed to the sub routine. In the following example the arguments would be passed as a array to C. my $forwarder = Log::Handler::Output::Forward->new( forward_to => \&Class::method, arguments => [ $self, "foo" ], ); This would call intern: Class::method(@arguments, $message); If this option is not set then the message will be passed as first argument. =back =head2 log() Call C if you want to forward messages to the subroutines. Example: $forwarder->log("this message will be forwarded to all sub routines"); =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 FORWARDED MESSAGE Note that the message will be forwarded as a hash reference. If you make changes to the reference it affects all other outputs. The hash key C contains the message. =head1 PREREQUISITES Carp Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::Forward; use strict; use warnings; use Carp; use Params::Validate qw(); our $VERSION = "0.03"; our $ERRSTR = ""; sub new { my $class = shift; my $options = $class->_validate(@_); return bless $options, $class; } sub log { my $self = shift; my $coderef = $self->{forward_to}; my $message = @_ > 1 ? {@_} : shift; if ($self->{arguments}) { eval { &$coderef(@{$self->{arguments}}, $message) }; } else { eval { &$coderef($message) }; } if ($@) { return $self->_raise_error($@); } return 1; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { $ERRSTR = $@; return undef; } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); if (!$opts) { return undef; } foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } return 1; } sub errstr { return $ERRSTR; } # # private stuff # sub _validate { my $class = shift; my %options = Params::Validate::validate(@_, { forward_to => { type => Params::Validate::CODEREF, }, arguments => { type => Params::Validate::ARRAYREF | Params::Validate::SCALAR, optional => 1, }, }); if (defined $options{arguments} && !ref($options{arguments})) { $options{arguments} = [ $options{arguments} ]; } return \%options; } sub _raise_error { my $self = shift; $ERRSTR = shift; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/Sendmail.pm0000640000000000000000000002255112235521676020504 0ustar rootroot=head1 NAME Log::Handler::Output::Sendmail - Log messages with sendmail. =head1 SYNOPSIS use Log::Handler::Output::Sendmail; my $email = Log::Handler::Output::Sendmail->new( from => 'bar@foo.example', to => 'foo@bar.example', subject => 'your subject', ); $email->log(message => $message); =head1 DESCRIPTION With this output module it's possible to log messages via C. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::Sendmail object. The following options are possible: =over 4 =item B The sender address (From). =item B The receipient address (To). =item B Carbon Copy (Cc). =item B Blind Carbon Copy (Bcc) =item B The subject of the mail. =item B This option is identical with C. =item B
With this options it's possible to set your own header. my $email = Log::Handler::Output::Sendmail->new( from => 'bar@foo.example', to => 'foo@bar.example', header => 'Content-Type: text/plain; charset= UTF-8', ); Or my $email = Log::Handler::Output::Sendmail->new( header => { From => 'bar@foo.example', To => 'foo@bar.example', Subject => 'my subject', 'Content-Type' => text/plain; charset= UTF-8', } ); Or my $email = Log::Handler::Output::Sendmail->new( header => [ 'From: bar@foo.example', 'To: foo@bar.example', 'Subject: my subject', 'Content-Type: text/plain; charset= UTF-8', ] ); =item B The default is set to C. =item B Parameters for C. The default is set to C<-t>. =item B Set the maximum size of the buffer in bytes. All messages will be buffered and if C is exceeded the buffer is flushed and the messages will be send as email. The default is set to 1048576 bytes. Set 0 if you want no buffering and send a mail for each log message. =item B Set 1 if you want to enable debugging. The messages can be fetched with $SIG{__WARN__}. =back =head2 log() Call C if you want to log a message as email. $email->log(message => "this message will be mailed"); If you pass the level then its placed into the subject: $email->log(message => "foo", level => "INFO"); $email->log(message => "bar", level => "ERROR"); $email->log(message => "baz", level => "DEBUG"); The lowest level is used: Subject: ERROR ... You can pass the level with C by setting message_pattern => '%L' =head2 flush() Call C if you want to flush the buffered messages. =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 DESTROY C is defined and called C. =head1 PREREQUISITES Carp Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::Sendmail; use strict; use warnings; use Carp; use Params::Validate qw(); our $VERSION = "0.07"; our $ERRSTR = ""; our $TEST = 0; # is needed to disable flush() for tests my %LEVEL_BY_STRING = ( DEBUG => 7, INFO => 6, NOTICE => 5, WARNING => 4, ERROR => 3, CRITICAL => 2, ALERT => 1, EMERGENCY => 0, FATAL => 0, ); sub new { my $class = shift; my $opts = $class->_validate(@_); my $self = bless $opts, $class; $self->{message} = ""; $self->{length} = 0; return $self; } sub log { my $self = shift; my $class = ref($self); my $message = @_ > 1 ? {@_} : shift; my $length = length($message->{message}); if (!$self->{maxsize}) { if ($self->{debug}) { warn "$class: maxsize disabled, no buffering"; } if ($message->{level}) { $self->{level} = $message->{level}; } $self->{message} = $message->{message}; return $self->_sendmail; } if ($length + $self->{length} > $self->{maxsize}) { if ($self->{debug}) { warn "$class: maxsize of $self->{maxsize} reached"; } $self->flush; } if ($message->{level} && !$self->{level}) { $self->{level} = $message->{level}; } elsif ($self->{level} && $message->{level}) { my $slevel = $self->{level}; my $mlevel = $message->{level}; if ($LEVEL_BY_STRING{$slevel} > $LEVEL_BY_STRING{$mlevel}) { $self->{level} = $message->{level}; } } $self->{message} .= $message->{message}; $self->{length} += $length; if ($self->{debug}) { warn "$class: buffer new message, length $length"; warn "$class: buffer length: $self->{length}"; } return 1; } sub flush { my $self = shift; if ($TEST || !$self->{message}) { return 1; } return $self->_sendmail; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { return $self->_raise_error($@); } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); $self->flush; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } $self->{message} = ""; $self->{length} = 0; return 1; } sub errstr { return $ERRSTR; } sub DESTROY { my $self = shift; $self->flush; } # # private stuff # sub _sendmail { my $self = shift; my $class = ref($self); my $header = $self->{header}; my $sendmail = $self->{sendmail}; if ($self->{params}) { $sendmail .= " $self->{params}"; } if ($self->{debug}) { warn "$class: call <$sendmail>"; warn "$class: header <$header>"; warn "$class: message $self->{length} bytes"; } if ($self->{level}) { $header =~ s/Subject:(.)/Subject: $self->{level}:$1/; $self->{level} = ""; } open my $fh, "|$sendmail" or return $self->_raise_error("unable to execute '$self->{sendmail}' - $!"); my $ret = print $fh $header, "\n", $self->{message}; close $fh; $self->{message} = ""; $self->{length} = 0; if (!$ret) { return $self->_raise_error("unable to write to stdin - $!"); } return 1; } sub _validate { my $class = shift; my %options = Params::Validate::validate(@_, { sender => { type => Params::Validate::SCALAR, optional => 1, }, from => { type => Params::Validate::SCALAR, optional => 1, }, to => { type => Params::Validate::SCALAR, optional => 1, }, cc => { type => Params::Validate::SCALAR, optional => 1, }, bcc => { type => Params::Validate::SCALAR, optional => 1, }, subject => { type => Params::Validate::SCALAR, optional => 1, }, header => { type => Params::Validate::SCALAR | Params::Validate::ARRAYREF | Params::Validate::HASHREF, optional => 1, }, maxsize => { type => Params::Validate::SCALAR, regex => qr/^\d+\z/, default => 1048576, }, sendmail => { type => Params::Validate::SCALAR, default => "/usr/sbin/sendmail", }, params => { type => Params::Validate::SCALAR, default => "-t", }, debug => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, }); if (!$TEST && !-x $options{sendmail}) { Carp::croak "'$options{sendmail}' is not executable"; } if ($options{subject}) { $options{subject} =~ s/\n/ /g; $options{subject} =~ s/(.{78})/$1\n /; if (length($options{subject}) > 998) { warn "Subject to long for email!"; $options{subject} = substr($options{subject}, 0, 998); } } if (ref($options{header})) { my $header = (); if (ref($options{header}) eq "HASH") { foreach my $n (keys %{ $options{header} }) { $header .= "$n: $options{header}{$n}\n"; } } elsif (ref($options{header}) eq "ARRAY") { foreach my $h (@{ $options{header} }) { $header .= "$h\n"; } } $options{header} = $header; } if ($options{header} && $options{header} !~ /(?:\015|\012)\z/) { $options{header} .= "\n"; } foreach my $opt (qw/from to cc bcc subject/) { if ($options{$opt}) { $options{header} .= ucfirst($opt).": $options{$opt}\n"; } } if ($options{sender}) { $options{sendmail} .= " -f $options{sender}"; } return \%options; } sub _raise_error { $ERRSTR = $_[1]; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/DBI.pm0000640000000000000000000003344112235521676017346 0ustar rootroot=head1 NAME Log::Handler::Output::DBI - Log messages to a database. =head1 SYNOPSIS use Log::Handler::Output::DBI; my $db = Log::Handler::Output::DBI->new( # database source database => "database", driver => "mysql", host => "127.0.0.1", port => 3306, # or with "dbname" instead of "database" dbname => "database", driver => "Pg", host => "127.0.0.1", port => 5432, # or with data_source data_source => "dbi:mysql:database=database;host=127.0.0.1;port=3306", # Username and password user => "user", password => "password", # debugging debug => 1, # table, columns and values (as string) table => "messages", columns => "level ctime cdate pid hostname progname message", values => "%level %time %date %pid %hostname %progname %message", # table, columns and values (as array reference) table => "messages", columns => [ qw/level ctime cdate pid hostname progname message/ ], values => [ qw/%level %time %date %pid %hostname %progname %message/ ], # table, columns and values (your own statement) statement => "insert into messages (level,ctime,cdate,pid,hostname,progname,message) values (?,?,?,?,?,?,?)", values => [ qw/%level %time %date %pid %hostname %progname %message/ ], # if you like persistent connections and want to re-connect persistent => 1, ); my %message = ( level => "ERROR", time => "10:12:13", date => "1999-12-12", pid => $$, hostname => "localhost", progname => $0, message => "an error here" ); $db->log(\%message); =head1 DESCRIPTION With this output you can insert messages into a database table. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::DBI object. The following options are possible: =over 4 =item B Set the dsn (data source name). You can use this parameter instead of C, C, C and C. =item B or B Pass the database name. =item B Pass the database driver. =item B Pass the hostname where the database is running. =item B Pass the port where the database is listened. =item B Pass the database user for the connect. =item B Pass the users password. =item B and B With this options you can pass the table name for the insert and the columns. You can pass the columns as string or as array. Example: # the table name table => "messages", # columns as string columns => "level, ctime, cdate, pid, hostname, progname, message", # columns as array columns => [ qw/level ctime cdate pid hostname progname message/ ], The statement would created as follows insert into message (level, ctime, cdate, pid, hostname, progname, mtime, message) values (?,?,?,?,?,?,?) =item B With this option you can pass your own statement if you don't want to you the options C
and C. statement => "insert into message (level, ctime, cdate, pid, hostname, progname, mtime, message)" ." values (?,?,?,?,?,?,?)" =item B With this option you have to set the values for the insert. values => "%level, %time, %date, %pid, %hostname, %progname, %message", # or values => [ qw/%level %time %date %pid %hostname %progname %message/ ], The placeholders are identical with the pattern names that you have to pass with the option C from L. %L level %T time %D date %P pid %H hostname %N newline %C caller %p package %f filename %l line %s subroutine %S progname %r runtime %t mtime %m message Take a look to the documentation of L for all possible patterns. =item B With this option you can enable or disable a persistent database connection and re-connect if the connection was lost. This option is set to 1 on default. =item B This option is useful if you want to pass arguments to L. The default is set to { PrintError => 0, AutoCommit => 1 } C is deactivated because this would print error messages as warnings to STDERR. You can pass your own arguments - and overwrite it - with dbi_params => { PrintError => 1, AutoCommit => 0 } =item B With this option it's possible to enable debugging. The information can be intercepted with C<$SIG{__WARN__}>. =back =head2 log() Log a message to the database. my $db = Log::Handler::Output::DBI->new( database => "database", driver => "mysql", user => "user", password => "password", host => "127.0.0.1", port => 3306, table => "messages", columns => [ qw/level ctime message/ ], values => [ qw/%level %time %message/ ], persistent => 1, ); $db->log( message => "your message", level => "INFO", time => "2008-10-10 10:12:23", ); Or you can connect to the database yourself. You should notice that if the database connection lost then the logger can't re-connect to the database and would return an error. Use C at your own risk. my $dbh = DBI->connect(...); my $db = Log::Handler::Output::DBI->new( dbi_handle => $dbh, table => "messages", columns => [ qw/level ctime message/ ], values => [ qw/%level %time %message/ ], ); =head2 connect() Connect to the database. =head2 disconnect() Disconnect from the database. =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 PREREQUISITES Carp Params::Validate DBI your DBI driver you want to use =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::DBI; use strict; use warnings; use DBI; use Carp; use Params::Validate qw(); our $VERSION = "0.12"; our $ERRSTR = ""; sub new { my $class = shift; my $opts = $class->_validate(@_); my $self = bless $opts, $class; if ($self->{debug}) { warn "Create a new Log::Handler::Output::DBI object"; } return $self; } sub log { my $self = shift; my $message = @_ > 1 ? {@_} : shift; my @values = (); foreach my $v (@{$self->{values}}) { if (ref($v) eq "CODE") { push @values, &$v(); } elsif ($v =~ /^%(.+)/ && exists $message->{$1}) { push @values, $message->{$1}; } else { push @values, $v; } } if ($self->{debug}) { warn "execute: ".@values." bind values"; } $self->connect or return undef; if ( ! $self->{sth}->execute(@values) ) { return $self->_raise_error("DBI execute error: ".DBI->errstr); } if (!$self->{persistent} && !$self->{dbi_handle}) { $self->disconnect or return undef; } return 1; } sub connect { my $self = shift; if ($self->{persistent} && $self->{dbh}) { if ($self->{use_ping}) { if ($self->{dbh}->ping) { return 1; } } else { eval { $self->{dbh}->do($self->{pingstmt}) or die DBI->errstr }; return 1 unless $@; } } if ($self->{debug}) { warn "Connect to the database: $self->{cstr}->[0] ..."; } my $dbh; if ($self->{dbi_handle}) { # If db ping failed and dbi_handle and dbi is set # then it seems that the database is down. if ($self->{dbi}) { return $self->_raise_error("dbi_handle - lost connection"); } $dbh = $self->{dbi_handle}; } else { $dbh = DBI->connect(@{$self->{cstr}}) or return $self->_raise_error("DBI connect error: ".DBI->errstr); } my $sth = $dbh->prepare($self->{statement}) or return $self->_raise_error("DBI prepare error: ".$dbh->errstr); $self->{dbh} = $dbh; $self->{sth} = $sth; return 1; } sub disconnect { my $self = shift; if ($self->{sth}) { $self->{sth}->finish or return $self->_raise_error("DBI finish error: ".$self->{sth}->errstr); delete $self->{sth}; } if ($self->{dbh}) { if ($self->{debug}) { warn "Disconnect from database"; } $self->{dbh}->disconnect or return $self->_raise_error("DBI disconnect error: ".DBI->errstr);; delete $self->{dbh}; } return 1; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { return $self->_raise_error($@); } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); if (!$opts) { return undef; } $self->disconnect; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } return 1; } sub errstr { return $ERRSTR; } # # private stuff # sub _validate { my $class = shift; my %options = Params::Validate::validate(@_, { dbi_handle => { type => Params::Validate::OBJECT, optional => 1, }, data_source => { type => Params::Validate::SCALAR, optional => 1, }, database => { type => Params::Validate::SCALAR, optional => 1, }, dbname => { type => Params::Validate::SCALAR, optional => 1, }, driver => { type => Params::Validate::SCALAR, optional => 1, }, user => { type => Params::Validate::SCALAR, optional => 1, }, password => { type => Params::Validate::SCALAR, optional => 1, }, host => { type => Params::Validate::SCALAR, optional => 1, }, port => { type => Params::Validate::SCALAR, optional => 1, }, table => { type => Params::Validate::SCALAR, depends => [ "columns" ], optional => 1, }, columns => { type => Params::Validate::SCALAR | Params::Validate::ARRAYREF, depends => [ "table" ], optional => 1, }, values => { type => Params::Validate::SCALAR | Params::Validate::ARRAYREF, }, statement => { type => Params::Validate::SCALAR, optional => 1, }, persistent => { type => Params::Validate::SCALAR, default => 1, }, dbi_params => { type => Params::Validate::HASHREF, default => { PrintError => 0, AutoCommit => 1 }, }, use_ping => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, debug => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, }); if (!$options{table} && !$options{statement}) { Carp::croak "Missing one of the mandatory options: 'statement' or 'table' and 'columns'"; } # build the connect string (data source name) my @cstr = (); if (defined $options{data_source}) { @cstr = ($options{data_source}); } elsif ($options{driver} && ($options{database} || $options{dbname})) { $cstr[0] = "dbi:$options{driver}:"; if ($options{database}) { $cstr[0] .= "database=$options{database}"; } else { $cstr[0] .= "dbname=$options{dbname}"; } if ($options{host}) { $cstr[0] .= ";host=$options{host}"; if ($options{port}) { $cstr[0] .= ";port=$options{port}"; } } } elsif (!defined $options{dbi_handle}) { Carp::croak "Missing mandatory options data_source or database/dbname"; } if ($options{user}) { $cstr[1] = $options{user}; if ($options{password}) { $cstr[2] = $options{password}; } } $cstr[3] = $options{dbi_params}; $options{cstr} = \@cstr; # build the statement if (!ref($options{values})) { $options{values} = [ split /[\s,]+/, $options{values} ]; } if (!$options{statement}) { $options{statement} = "insert into $options{table} ("; if (ref($options{columns})) { $options{statement} .= join(",", @{$options{columns}}); } else { $options{statement} .= join(",", split /[\s,]+/, $options{columns}); } $options{statement} .= ") values ("; my @binds; foreach my $v (@{$options{values}}) { $v =~ s/^\s+//; $v =~ s/\s+\z//; push @binds, "?"; } $options{statement} .= join(",", @binds); $options{statement} .= ")"; } if ($options{driver} && $options{driver} =~ /oracle/i) { $options{pingstmt} = "select 1 from dual"; } else { $options{pingstmt} = "select 1"; } return \%options; } sub _raise_error { my $self = shift; $ERRSTR = shift; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/Email.pm0000640000000000000000000002260512235521676017777 0ustar rootroot=head1 NAME Log::Handler::Output::Email - Log messages as email (via Net::SMTP). =head1 SYNOPSIS use Log::Handler::Output::Email; my $email = Log::Handler::Output::Email->new( host => "mx.bar.example", hello => "EHLO my.domain.example", timeout => 120, debug => 0, from => 'bar@foo.example', to => 'foo@bar.example', subject => "your subject", buffer => 0 ); $email->log(message => $message); =head1 DESCRIPTION With this output module it's possible to log messages via email and it used Net::SMTP to do it. The date for the email is generated with C. Net::SMTP is from Graham Barr and it does it's job very well. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::Email object. The following opts are possible: =over 4 =item B With this option you has to define the SMTP host to connect to. host => "mx.host.com" # or host => [ "mx.host.example", "mx.host-backup.example" ] =item B Identify yourself with a HELO. The default is set to C. =item B With this option you can set the maximum time in seconds to wait for a response from the SMTP server. The default is set to 120 seconds. =item B The sender address (MAIL FROM). =item B The receipient address (RCPT TO). Additional options are B and B. =item B The subject of the mail. The default subject is "Log message from $progname". =item B This opts exists only for security. The thing is that it would be very bad if something wents wrong in your program and hundreds of mails would be send. For this reason you can set a buffer to take care. With the buffer you can set the maximum size of the buffer in lines. If you set buffer => 10 then 10 messages would be buffered. Set C to 0 if you want to disable the buffer. The default buffer size is set to 20. =item B With this option it's possible to enable debugging. The information can be intercepted with $SIG{__WARN__}. =back =head2 log() Call C if you want to log a message as email. If you set a buffer size then the message will be pushed into the buffer first. Example: $email->log(message => "this message will be mailed"); If you pass the level then its placed into the subject: $email->log(message => "foo", level => "INFO"); $email->log(message => "bar", level => "ERROR"); $email->log(message => "baz", level => "DEBUG"); The lowest level is used: Subject: ERROR: ... You can pass the level with C by setting message_pattern => '%L' =head2 flush() Call C if you want to flush the buffered lines. =head2 sendmail() Call C if you want to send an email. The difference to C is that the message won't be buffered. =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 DESTROY C is defined and called C. =head1 PREREQUISITES Carp Email::Date Net::SMTP Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::Email; use strict; use warnings; use Carp; use Email::Date; use Net::SMTP; use Params::Validate qw(); our $VERSION = "0.08"; our $ERRSTR = ""; our $TEST = 0; # is needed to disable flush() for tests my %LEVEL_BY_STRING = ( DEBUG => 7, INFO => 6, NOTICE => 5, WARNING => 4, ERROR => 3, CRITICAL => 2, ALERT => 1, EMERGENCY => 0, FATAL => 0, ); sub new { my $class = shift; my $opts = $class->_validate(@_); return bless $opts, $class; } sub log { my $self = shift; my $message = @_ > 1 ? {@_} : shift; my $buffer = $self->{message_buffer}; if ($self->{buffer} == 0) { return $self->sendmail($message); } if (@$buffer < $self->{buffer}) { push @$buffer, $message; } if (@$buffer == $self->{buffer}) { return $self->flush; } return 1; } sub flush { my $self = shift; my $string = (); my $buffer = $self->{message_buffer}; if ($TEST || !@$buffer) { return 1; } # Safe the last message because the newest subject is used my $message = pop @$buffer; while (my $buf = shift @$buffer) { if ($buf->{level} && !$message->{level}) { $message->{level} = $buf->{level}; } elsif ($buf->{level} && $message->{level}) { my $blevel = $buf->{level}; my $mlevel = $message->{level}; if ($LEVEL_BY_STRING{$mlevel} > $LEVEL_BY_STRING{$blevel}) { $message->{level} = $buf->{level}; } } $string .= $buf->{message}; } if (defined $string) { $message->{message} = $string . $message->{message}; } return $self->sendmail($message); } sub sendmail { my $self = shift; my $message = @_ > 1 ? {@_} : shift; my $subject = $message->{subject} || $self->{subject}; my $date = Email::Date::format_date(); my $smtp = (); my $expect = 10; my $success = 0; if ($message->{level}) { $subject = "$message->{level}: $subject"; } foreach my $host (@{$self->{host}}) { $smtp = Net::SMTP->new( Host => $host, Hello => $self->{hello}, Timeout => $self->{timeout}, Debug => $self->{debug}, ); last if $smtp; } if (!$smtp) { return $self->_raise_error("smtp error: unable to connect to ".join(", ", @{$self->{host}})); } if ($smtp->mail($self->{from})) { $success++; } if ($smtp->to($self->{to})) { $success++; } if ($self->{cc}) { if ($smtp->cc($self->{cc})) { $success++; } $expect++; } if ($self->{bcc}) { if ($smtp->bcc($self->{bcc})) { $success++; } $expect++; } if ($smtp->data) { $success++; } if ($smtp->datasend("From: $self->{from}\n")) { $success++; } if ($smtp->datasend("To: $self->{to}\n")) { $success++; } if ($self->{cc}) { if ($smtp->datasend("Cc: $self->{cc}\n")) { $success++; } $expect++; } if ($smtp->datasend("Subject: $subject\n")) { $success++; } if ($smtp->datasend("Date: $date\n")) { $success++; } if ($smtp->datasend($message->{message}."\n")) { $success++; } if ($smtp->dataend) { $success++; } if ($smtp->quit) { $success++; } if ($success != $expect) { return $self->_raise_error("smtp error($success): unable to send mail to $self->{to}"); } return 1; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { return $self->_raise_error($@); } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); if (!$opts) { return undef; } $self->flush; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } return 1; } sub errstr { return $ERRSTR; } sub DESTROY { my $self = shift; $self->flush; } # # private stuff # sub _validate { my $class = shift; my $progname = $0; $progname =~ s@.*[/\\]@@; my %opts = Params::Validate::validate(@_, { host => { type => Params::Validate::ARRAYREF | Params::Validate::SCALAR, }, hello => { type => Params::Validate::SCALAR, default => "EHLO BELO", }, timeout => { type => Params::Validate::SCALAR, regex => qr/^\d+\z/, default => 120, }, debug => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, from => { type => Params::Validate::SCALAR, }, to => { type => Params::Validate::SCALAR, }, cc => { type => Params::Validate::SCALAR, optional => 1, }, bcc => { type => Params::Validate::SCALAR, optional => 1, }, subject => { type => Params::Validate::SCALAR, default => "Log message from $progname", }, buffer => { type => Params::Validate::SCALAR, default => 20, }, }); if (!ref($opts{host})) { $opts{host} = [ $opts{host} ]; } if ($opts{subject}) { $opts{subject} =~ s/\n/ /g; $opts{subject} =~ s/(.{78})/$1\n /g; if (length($opts{subject}) > 998) { warn "Subject to long for email!"; $opts{subject} = substr($opts{subject}, 0, 998); } } $opts{message_buffer} = [ ]; return \%opts; } sub _raise_error { my $self = shift; $ERRSTR = shift; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/Screen.pm0000640000000000000000000001034412235521676020164 0ustar rootroot=head1 NAME Log::Handler::Output::Screen - Log messages to the screen. =head1 SYNOPSIS use Log::Handler::Output::Screen; my $screen = Log::Handler::Output::Screen->new( log_to => "STDERR", dump => 1, ); $screen->log($message); =head1 DESCRIPTION This output module makes it possible to log messages to your screen. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::Screen object. The following options are possible: =over 4 =item B Where do you want to log? Possible is: STDOUT, STDERR and WARN. WARN means to call C. The default is STDOUT. =item B Set this option to 1 if you want that the message will be dumped with C to the screen. =item B, B Set utf8 or utf-8 on STDOUT or STDERR. It depends on the parameter B. utf8 = binmode, $fh, ":utf8"; utf-8 = binmode, $fh, "encoding(utf-8)"; Yes, there is a difference. L L =back =head2 log() Call C if you want to log a message to the screen. Example: $screen->log("this message goes to the screen"); =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 PREREQUISITES Data::Dumper Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::Screen; use strict; use warnings; use Data::Dumper; use Params::Validate qw(); our $VERSION = "0.07"; our $ERRSTR = ""; sub new { my $class = shift; my $options = $class->_validate(@_); return bless $options, $class; } sub log { my $self = shift; my $message = @_ > 1 ? {@_} : shift; local $|=1; if ($self->{dump}) { $message->{message} = Dumper($message); } if ($self->{log_to} eq "STDOUT") { if ($self->{utf8}) { binmode STDOUT, ":utf8"; } elsif ($self->{"utf-8"}) { binmode STDOUT, "encoding(utf-8)"; } print STDOUT $message->{message} or return $self->_raise_error($!); } elsif ($self->{log_to} eq "STDERR") { if ($self->{utf8}) { binmode STDERR, ":utf8"; } elsif ($self->{"utf-8"}) { binmode STDERR, "encoding(utf-8)"; } print STDERR $message->{message} or return $self->_raise_error($!); } elsif ($self->{log_to} eq "WARN") { # hmmm, should I really set utf8 for warnings? warn $message->{message}; } return 1; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { $ERRSTR = $@; return undef; } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); if (!$opts) { return undef; } foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } return 1; } sub errstr { return $ERRSTR; } # # private stuff # sub _validate { my $class = shift; my %options = Params::Validate::validate(@_, { log_to => { type => Params::Validate::SCALAR, regex => qr/^(?:STDOUT|STDERR|WARN)\z/, default => "STDOUT", }, utf8 => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, "utf-8" => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, dump => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, }); return \%options; } sub _raise_error { my $self = shift; $ERRSTR = shift; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/Socket.pm0000640000000000000000000001515412235521676020201 0ustar rootroot=head1 NAME Log::Handler::Output::Socket - Send messages to a socket. =head1 SYNOPSIS use Log::Handler::Output::Socket; my $sock = Log::Handler::Output::Socket->new( peeraddr => "127.0.0.1", peerport => 44444, proto => "tcp", timeout => 10 ); $sock->log(message => $message); =head1 DESCRIPTION With this module it's possible to send messages over the network. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::Socket object. The following options are possible: =over 4 =item B The address of the server. =item B The port to connect to. =item B The protocol you wish to use. Default is TCP. =item B The timeout to send message. The default is 5 seconds. =item B and B With this option you can enable or disable a persistent connection and re-connect if the connection was lost. Both options are set to 1 on default. =item B Do you like to dump the message? If you enable this option then all messages will be dumped with C. =item B Do you want to use another dumper as C? You can do the following as example: use Convert::Bencode_XS; dumper => sub { Convert::Bencode_XS::bencode($_[0]) } # or maybe use JSON::PC; dumper => sub { JSON::PC::convert($_[0]) } =item B This option is only useful if you want to pass your own arguments to C and don't want use C and C. Example: connect => { PerrAddr => "127.0.0.1", PeerPort => 44444, LocalPort => 44445 } This options are passed to C. =back =head2 log() Call C if you want to send a message over the socket. Example: $sock->log("message"); =head2 connect() Connect to the socket. =head2 disconnect() Disconnect from socket. =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() This function returns the last error message. =head1 PREREQUISITES Carp Params::Validate; IO::Socket::INET; Data::Dumper; =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::Socket; use strict; use warnings; use Carp; use Data::Dumper; use IO::Socket::INET; use Params::Validate qw(); our $VERSION = "0.08"; our $ERRSTR = ""; sub new { my $class = shift; my $opts = $class->_validate(@_); my $self = bless $opts, $class; if ($self->{persistent}) { $self->connect or croak $self->errstr; } return $self; } sub log { my $self = shift; my $message = @_ > 1 ? {@_} : shift; my $socket = (); if ($self->{dump}) { $message->{message} = $self->{dumper}(@_ > 1 ? {@_} : shift); } if ($self->{persistent} && $self->{socket}) { $socket = $self->{socket}; } else { $socket = $self->connect or return undef; } # If the peer is done then send() croaks eval { $socket->send($message->{message}) }; if ($@) { if ($self->{persistent} && $self->{reconnect}) { $self->connect or return undef; eval { $socket->send($message->{message}) }; if ($@) { return $self->_raise_error("something curious happends: $@"); } } else { return $self->_raise_error("unable to send message: $@"); } } if (!$self->{persistent}) { $self->disconnect; } return 1; } sub connect { my $self = shift; my $opts = (); if (@_) { $opts = @_ > 1 ? {@_} : shift; } else { $opts = $self->{sockopts}; } $self->{socket} = IO::Socket::INET->new(%$opts) or return $self->_raise_error("unable to connect - $!"); return $self->{socket}; } sub disconnect { my $self = shift; if ($self->{socket}) { $self->{socket}->close; } delete $self->{socket}; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { return $self->_raise_error($@); } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); $self->disconnect; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } if ($self->{persistent}) { $self->connect or croak $self->errstr; } return 1; } sub errstr { return $ERRSTR; } sub DESTROY { my $self = shift; if ($self->{socket}) { $self->{socket}->close; } } # # private stuff # sub _validate { my $class = shift; my %options = Params::Validate::validate(@_, { connect => { type => Params::Validate::HASHREF, optional => 1, }, peeraddr => { type => Params::Validate::SCALAR, optional => 1, }, peerport => { type => Params::Validate::SCALAR, optional => 1, }, proto => { type => Params::Validate::SCALAR, default => "tcp", }, timeout => { type => Params::Validate::SCALAR, default => 5, }, persistent => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 1, }, reconnect => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 1, }, dump => { type => Params::Validate::SCALAR, regex => qr/^[01]\z/, default => 0, }, dumper => { type => Params::Validate::CODEREF, default => \&Dumper, }, }); if ($options{peeraddr} && $options{peerport}) { $options{sockopts}{PeerAddr} = delete $options{peeraddr}; $options{sockopts}{PeerPort} = delete $options{peerport}; $options{sockopts}{Proto} = delete $options{proto}; $options{sockopts}{Timeout} = delete $options{timeout}; } elsif (!$options{connect}) { Carp::croak "missing mandatory parameter connect or peeraddr/peerport"; } return \%options; } sub _raise_error { $ERRSTR = $_[1]; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Output/File.pm0000640000000000000000000002632312536634651017632 0ustar rootroot=head1 NAME Log::Handler::Output::File - Log messages to a file. =head1 SYNOPSIS use Log::Handler::Output::File; my $log = Log::Handler::Output::File->new( filename => "file.log", filelock => 1, fileopen => 1, reopen => 1, mode => "append", autoflush => 1, permissions => "0664", utf8 => 0, ); $log->log(message => $message); =head1 DESCRIPTION Log messages to a file. =head1 METHODS =head2 new() Call C to create a new Log::Handler::Output::File object. The following options are possible: =over 4 =item B With C you can set a file name as a string or as a array reference. If you set a array reference then the parts will be concat with C from C. Set a file name: my $log = Log::Handler::Output::File->new( filename => "file.log" ); Set a array reference: my $log = Log::Handler::Output::File->new( # foo/bar/baz.log filename => [ "foo", "bar", "baz.log" ], # /foo/bar/baz.log filename => [ "", "foo", "bar", "baz.log" ], ); =item B Maybe it's desirable to lock the log file by each write operation because a lot of processes write at the same time to the log file. You can set the option C to 0 or 1. 0 - no file lock 1 - exclusive lock (LOCK_EX) and unlock (LOCK_UN) by each write operation (default) =item B Open a log file transient or permanent. 0 - open and close the logfile by each write operation 1 - open the logfile if C called and try to reopen the file if C is set to 1 and the inode of the file has changed (default) =item B This option works only if option C is set to 1. 0 - deactivated 1 - try to reopen the log file if the inode changed (default) =item How to use B and B Please note that it's better to set C and C to 0 on Windows because Windows unfortunately haven't the faintest idea of inodes. To write your code independent you should control it: my $os_is_win = $^O =~ /win/i ? 0 : 1; my $log = Log::Handler::Output::File->new( filename => "file.log", mode => "append", fileopen => $os_is_win ); If you set C to 0 then it implies that C has no importance. =item B There are three possible modes to open a log file. append - O_WRONLY | O_APPEND | O_CREAT (default) excl - O_WRONLY | O_EXCL | O_CREAT trunc - O_WRONLY | O_TRUNC | O_CREAT C would open the log file in any case and appends the messages at the end of the log file. C would fail by open the log file if the log file already exists. C would truncate the complete log file if it exists. Please take care to use this option. Take a look to the documentation of C to get more information. =item B 0 - autoflush off 1 - autoflush on (default) =item B The option C sets the permission of the file if it creates and must be set as a octal value. The permission need to be in octal and are modified by your process's current "umask". That means that you have to use the unix style permissions such as C. C<0640> is the default permission for this option. That means that the owner got read and write permissions and users in the same group got only read permissions. All other users got no access. Take a look to the documentation of C to get more information. =item B, B utf8 = binmode, $fh, ":utf8"; utf-8 = binmode, $fh, "encoding(utf-8)"; Yes, there is a difference. L L =item B It's possible to set a pattern in the filename that is replaced with a date. If the date - and the filename - changed the file is closed and reopened with the new filename. The filename is converted with C. Example: my $log = Log::Handler::Output::File->new( filename => "file-%Y-%m-%d.log", dateext => 1 ); In this example the file C is created. At the next day the filename changed, the log file C is closed and C is opened. This feature is a small improvement for systems where no logrotate is available like Windows systems. On this way you have the chance to delete old log files without to stop/start a daemon. =back =head2 log() Call C if you want to log messages to the log file. Example: $log->log(message => "this message goes to the logfile"); =head2 flush() Call C if you want to re-open the log file. This is useful if you don't want to use option S<"reopen">. As example if a rotate mechanism moves the logfile and you want to re-open a new one. =head2 validate() Validate a configuration. =head2 reload() Reload with a new configuration. =head2 errstr() Call C to get the last error message. =head2 close() Call C to close the log file yourself - normally you don't need to use it, because the log file will be opened and closed automatically. =head1 PREREQUISITES Carp Fcntl File::Spec Params::Validate =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Output::File; use strict; use warnings; use Carp; use Fcntl qw( :flock O_WRONLY O_APPEND O_TRUNC O_EXCL O_CREAT ); use File::Spec; use Params::Validate qw(); use POSIX; our $VERSION = "0.08"; our $ERRSTR = ""; sub new { my $class = shift; my $opts = $class->_validate(@_); my $self = bless $opts, $class; # open the log file permanent if ($self->{dateext}) { $self->_check_dateext or return undef; } elsif ($self->{fileopen}) { $self->_open or croak $self->errstr; } return $self; } sub log { my $self = shift; my $message = @_ > 1 ? {@_} : shift; if ($self->{dateext}) { $self->_check_dateext or return undef; } if (!$self->{fileopen}) { $self->_open or return undef; } elsif ($self->{reopen}) { $self->_checkino or return undef; } if ($self->{filelock}) { flock($self->{fh}, LOCK_EX) or return $self->_raise_error("unable to lock logfile $self->{filename}: $!"); } print {$self->{fh}} $message->{message} or return $self->_raise_error("unable to print to logfile: $!"); if ($self->{filelock}) { flock($self->{fh}, LOCK_UN) or return $self->_raise_error("unable to unlock logfile $self->{filename}: $!"); } if (!$self->{fileopen}) { $self->close or return undef; } return 1; } sub flush { my $self = shift; if ($self->{fileopen}) { $self->close or return undef; $self->_open or return undef; } return 1; } sub close { my $self = shift; if ($self->{fh}) { CORE::close($self->{fh}) or return $self->_raise_error("unable to close logfile $self->{filename}: $!"); delete $self->{fh}; } return 1; } sub validate { my $self = shift; my $opts = (); eval { $opts = $self->_validate(@_) }; if ($@) { return $self->_raise_error($@); } return $opts; } sub reload { my $self = shift; my $opts = $self->validate(@_); $self->close; foreach my $key (keys %$opts) { $self->{$key} = $opts->{$key}; } if ($self->{fileopen}) { $self->_open or croak $self->errstr; } return 1; } sub errstr { return $ERRSTR; } sub DESTROY { my $self = shift; if ($self->{fh}) { CORE::close($self->{fh}); } } # # private stuff # sub _open { my $self = shift; sysopen(my $fh, $self->{filename}, $self->{mode}, $self->{permissions}) or return $self->_raise_error("unable to open logfile $self->{filename}: $!"); if ($self->{autoflush}) { my $oldfh = select $fh; $| = $self->{autoflush}; select $oldfh; } if ($self->{utf8}) { binmode $fh, ":utf8"; } elsif ($self->{"utf-8"}) { binmode $fh, "encoding(utf-8)"; } if ($self->{reopen}) { $self->{inode} = (stat($self->{filename}))[1]; } $self->{fh} = $fh; return 1; } sub _check_dateext { my $self = shift; my $filename = POSIX::strftime($self->{filename_pattern}, localtime); if ($self->{filename} ne $filename) { $self->{filename} = $filename; if ($self->{fileopen}) { $self->close or return undef; $self->_open or return undef; } } return 1; } sub _checkino { my $self = shift; if (!-e $self->{filename} || $self->{inode} != (stat($self->{filename}))[1]) { $self->close or return undef; $self->_open or return undef; } return 1; } sub _validate { my $class = shift; my $bool_rx = qr/^[10]\z/; my %opts = Params::Validate::validate(@_, { filename => { type => Params::Validate::SCALAR | Params::Validate::ARRAYREF, }, filelock => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 1, }, fileopen => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 1, }, reopen => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 1, }, mode => { type => Params::Validate::SCALAR, regex => qr/^(append|excl|trunc)\z/, default => "append", }, autoflush => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 1, }, permissions => { type => Params::Validate::SCALAR, regex => qr/^[0-7]{3,4}\z/, default => "0640", }, utf8 => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 0, }, "utf-8" => { type => Params::Validate::SCALAR, regex => $bool_rx, default => 0, }, dateext => { type => Params::Validate::SCALAR, optional => 1 } }); if (ref($opts{filename}) eq "ARRAY") { $opts{filename} = File::Spec->catfile(@{$opts{filename}}); } if ($opts{mode} eq "append") { $opts{mode} = O_WRONLY | O_APPEND | O_CREAT; } elsif ($opts{mode} eq "excl") { $opts{mode} = O_WRONLY | O_EXCL | O_CREAT; } elsif ($opts{mode} eq "trunc") { $opts{mode} = O_WRONLY | O_TRUNC | O_CREAT; } $opts{permissions} = oct($opts{permissions}); $opts{filename_pattern} = $opts{filename}; return \%opts; } sub _raise_error { $ERRSTR = $_[1]; return undef; } 1; Log-Handler-0.90/lib/Log/Handler/Plugin/0000750000000000000000000000000012235521676016342 5ustar rootrootLog-Handler-0.90/lib/Log/Handler/Plugin/Config/0000750000000000000000000000000012235521676017547 5ustar rootrootLog-Handler-0.90/lib/Log/Handler/Plugin/Config/Properties.pm0000640000000000000000000000332712235521676022247 0ustar rootroot=head1 NAME Log::Handler::Plugin::Config::Properties - Config loader for Config::Properties. =head1 SYNOPSIS use Log::Handler::Plugin::Config::Properties; my $config = Log::Handler::Plugin::Config::Properties->get_config( $config_file ); =head1 ROUTINES =head2 get_config() Expect the config file name and returns the config as a reference. The configuration uses full stops "." as a delimiter. =head1 CONFIG STYLE file.mylog.reopen = 1 file.mylog.fileopen = 1 file.mylog.maxlevel = info file.mylog.permissions = 0640 file.mylog.mode = append file.mylog.timeformat = %b %d %H:%M:%S file.mylog.debug_mode = 2 file.mylog.minlevel = warn file.mylog.filename = example.log file.mylog.newline = 1 file.mylog.message_layout = %T %H[%P] [%L] %S: %m =head1 PREREQUISITES Config::Properties =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Plugin::Config::Properties; use strict; use warnings; use Config::Properties; our $VERSION = '0.03'; our $SPLITTOTREE = qr/\./; sub get_config { my ($class, $config_file) = @_; my $properties = Config::Properties->new(); open my $fh, '<', $config_file or die "unable to open $config_file: $!"; $properties->load($fh); close $fh; my $config = $properties->splitToTree($SPLITTOTREE); return $config; } 1; Log-Handler-0.90/lib/Log/Handler/Plugin/Config/General.pm0000640000000000000000000000277612235521676021477 0ustar rootroot=head1 NAME Log::Handler::Plugin::Config::General - Config loader for Config::General. =head1 SYNOPSIS use Log::Handler::Plugin::Config::General; my $config = Log::Handler::Plugin::Config::General->get_config( $config_file ); =head1 ROUTINES =head2 get_config() Expect the config file name and returns the config as a reference. =head1 CONFIG STYLE fileopen = 1 reopen = 1 permissions = 0640 maxlevel = info mode = append timeformat = %b %d %H:%M:%S debug_mode = 2 filename = example.log minlevel = warn message_layout = %T %H[%P] [%L] %S: %m newline = 1 =head1 PREREQUISITES Config::General =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Plugin::Config::General; use strict; use warnings; use Config::General; our $VERSION = '0.02'; sub get_config { my ($class, $config_file) = @_; my $config = Config::General->new($config_file); my %config = $config->getall(); return \%config; } 1; Log-Handler-0.90/lib/Log/Handler/Plugin/YAML.pm0000640000000000000000000000246612235521676017453 0ustar rootroot=head1 NAME Log::Handler::Plugin::YAML - Config loader for YAML. =head1 SYNOPSIS use Log::Handler::Plugin::YAML; my $config = Log::Handler::Plugin::YAML->get_config( $config_file ); =head1 ROUTINES =head2 get_config() Expect the config file name and returns the config as a reference. =head1 CONFIG STYLE --- file: mylog: debug_mode: 2 filename: example.log fileopen: 1 maxlevel: info minlevel: warn mode: append newline: 1 permissions: 0640 message_layout: %T %H[%P] [%L] %S: %m reopen: 1 timeformat: %b %d %H:%M:%S =head1 PREREQUISITES YAML =head1 EXPORTS No exports. =head1 REPORT BUGS Please report all bugs to . If you send me a mail then add Log::Handler into the subject. =head1 AUTHOR Jonny Schulz . =head1 COPYRIGHT Copyright (C) 2007-2009 by Jonny Schulz. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut package Log::Handler::Plugin::YAML; use strict; use warnings; use YAML; our $VERSION = '0.03'; sub get_config { my ($class, $config_file) = @_; my $config = YAML::LoadFile($config_file); return $config; } 1; Log-Handler-0.90/lib/Log/Handler/Examples.pod0000640000000000000000000002523712235521676017400 0ustar rootroot=head1 NAME Log::Handler::Examples - Examples. =head1 CREATE LOGGER Quite simple use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => \%options ); Create a application wide logger my $log = Log::Handler->create_logger("myapp"); $log->add( screen => \%options ); Once created you can use the application logger in all modules of your project: package MyApp; use Log::Handler; my $log = Log::Handler->create_logger("myapp"); $log->add( screen => \%options ); package MyApp::Admin; use Log::Handler; my $log = Log::Handler->get_logger("myapp"); $log->info("message"); =head1 ADD OUTPUTS use Log::Handler; my $log = Log::Handler->new(); $log->add( dbi => \%options ); $log->add( email => \%options ); $log->add( file => \%options ); $log->add( forward => \%options ); $log->add( screen => \%options ); $log->add( socket => \%options ); This is the same like $log->add( "Log::Handler::Output::DBI" => \%options ); $log->add( "Log::Handler::Output::Email" => \%options ); $log->add( "Log::Handler::Output::File" => \%options ); $log->add( "Log::Handler::Output::Forward" => \%options ); $log->add( "Log::Handler::Output::Screen" => \%options ); $log->add( "Log::Handler::Output::Socket" => \%options ); =head1 RELOAD THE LOGGER =head2 Quite simple use Log::Handler; my $log = Log::Handler->new(); $log->config(config => "logger.conf"); $log->reload(config => "logger.conf"); =head2 Reload on HUP use Log::Handler; my $log = Log::Handler->new(); $log->config(config => "logger.conf"); $SIG{HUP} = sub { unless ($log->reload(config => "logger.conf")) { warn "unable to reload configuration"; warn $log->errstr; } }; =head2 Validate first It's possible to make a configuration check before you reload: $log->validate(config => "logger.conf") or warn $log->errstr; =head1 LOG VIA DBI use Log::Handler; my $log = Log::Handler->new(); $log->add( dbi => { database => "database", driver => "mysql", host => "127.0.0.1", port => 3306, user => "user", password => "password", table => "messages", columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ], values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ], maxlevel => "error", minlevel => "emergency" newline => 0, message_pattern => "%L %T %D %P %H %C %S %t %m", } ); $log->error("log an error"); Or with C $log->add( dbi => { dbname => "database", driver => "Pg", host => "127.0.0.1", port => 5432, user => "user", password => "password", table => "messages", columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ], values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ], maxlevel => "error", minlevel => "emergency" newline => 0, message_pattern => "%L %T %D %P %H %C %S %t %m", } ); Or with C $log->add( dbi => { data_source => "dbi:SQLite:dbname=database.sqlite", table => "messages", columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ], values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ], maxlevel => "error", minlevel => "emergency" newline => 0, message_pattern => "%L %T %D %P %H %C %S %t %m", } ); =head1 LOG VIA EMAIL use Log::Handler; my $log = Log::Handler->new(); $log->add( email => { host => "mx.bar.example", hello => "EHLO my.domain.example", timeout => 30, from => "bar@foo.example", to => "foo@bar.example", subject => "your subject", buffer => 0, maxlevel => "emergency", minlevel => "emergency", message_pattern => '%L', } ); $log->emergency("log an emergency issue"); =head1 LOG VIA SENDMAIL use Log::Handler; my $log = Log::Handler->new(); $log->add( sendmail => { from => "bar@foo.example", to => "foo@bar.example", subject => "your subject", maxlevel => "error", minlevel => "error", message_pattern => '%L', } ); $log->emergency("message"); =head1 LOG VIA FILE use Log::Handler; my $log = Log::Handler->new(); $log->add( file => { filename => "file1.log", maxlevel => 7, minlevel => 0 } ); $log->error("log an error"); =head1 LOG VIA FORWARD use Log::Handler; my $log = Log::Handler->new(); $log->add( forward => { forward_to => \&my_func, message_pattern => [ qw/%L %T %P %H %C %S %t/ ], message_layout => "%m", maxlevel => "info", } ); $log->info("log a information"); sub my_func { my $params = shift; print Dumper($params); } =head1 LOG VIA SCREEN use Log::Handler; my $log = Log::Handler->new(); $log->add( screen => { log_to => "STDERR", maxlevel => "info", } ); $log->info("log to the screen"); =head1 LOG VIA SOCKET use Log::Handler; my $log = Log::Handler->new(); $log->add( socket => { peeraddr => "127.0.0.1", peerport => 44444, maxlevel => "info", die_on_errors => 0, } ); while ( 1 ) { $log->info("test") or warn "unable to send message: ", $log->errstr; sleep 1; } =head2 SIMPLE SOCKET SERVER (TCP) use strict; use warnings; use IO::Socket::INET; use Log::Handler::Output::File; my $sock = IO::Socket::INET->new( LocalAddr => "127.0.0.1", LocalPort => 44444, Listen => 2, ) or die $!; my $file = Log::Handler::Output::File->new( filename => "file.log", fileopen => 1, reopen => 1, ); while ( 1 ) { $file->log(message => "waiting for next connection\n"); while (my $request = $sock->accept) { my $ipaddr = sprintf("%-15s", $request->peerhost); while (my $message = <$request>) { $file->log(message => "$ipaddr - $message"); } } } =head1 DIFFERENT OUTPUTS use Log::Handler; my $log = Log::Handler->new(); $log->add( file => { filename => "common.log", maxlevel => 6, minlevel => 5, } ); $log->add( file => { filename => "error.log", maxlevel => 4, minlevel => 0, } ); $log->add( email => { host => "mx.bar.example", hello => "EHLO my.domain.example", timeout => 120, from => "bar@foo.example", to => "foo@bar.example", subject => "your subject", buffer => 0, maxlevel => 0, } ); # log to common.log $log->info("this is a info message"); # log to error.log $log->warning("this is a warning"); # log to error.log and to foo@bar.example $log->emergency("this is a emergency message"); =head1 FILTER MESSAGES my $log = Log::Handler->new(); $log->add( screen => { maxlevel => 6, filter_message => { match1 => "foo", match2 => "bar", match3 => "baz", condition => "(match1 && match2) && !match3" } } ); $log->info("foo"); $log->info("foo bar"); $log->info("foo baz"); =head2 FILTER CALLER This example shows you how it's possilbe to debug messages only from a special namespace. my $log = Log::Handler->new(); $log->add( file => { filename => "file1.log", maxlevel => "warning", } ); $log->add( screen => { maxlevel => "debug", message_layout => "message from %p - %m", filter_caller => qr/^Foo::Bar\z/, } ); $log->warning("a warning here"); package Foo::Bar; $log->info("an info here"); 1; =head2 ANOTHER FILTER filter_message => "as string" filter_message => qr/as regexp/ filter_message => sub { shift->{message} =~ /as code ref/ } # or with conditions filter_message => { match1 => "as string", match2 => qr/as regexp/, condition => "match1 || match2", } filter_caller => "as string" filter_caller => qr/as regexp/ =head1 CONFIG Examples: my $log = Log::Handler->new( config => "logger.conf" ); # or $log->add( config => "logger.conf" ); # or $log->config( config => "logger.conf" ); Example with Config::General. Script: use Log::Handler; my $log = Log::Handler->new(); $log->config( config => "logger.conf" ); Config (logger.conf): alias = common filename = example.log maxlevel = info minlevel = warn alias = error filename = example-error.log maxlevel = warn minlevel = emergency alias = debug filename = example-debug.log maxlevel = debug minlevel = debug log_to = STDERR dump = 1 maxlevel = debug minlevel = debug =head1 CHECK FOR ACTIVE LEVELS It can be very useful if you want to check if a level is active. use Log::Handler; use Data::Dumper; my $log = Log::Handler->new(); $log->add( file => { filename => "file1.log", maxlevel => 4, } ); my %hash = (foo => 1, bar => 2); Now you want to dump the hash, but not in any case. if ( $log->is_debug ) { my $dump = Dumper(\%hash); $log->debug($dump); } This would dump the hash only if the level debug is active. =head1 AUTHOR Jonny Schulz . =cut