![]() |
![]()
| ![]() |
![]()
NAMELog::Log4perl::Tiny - mimic Log::Log4perl in one single module VERSIONThis document describes Log::Log4perl::Tiny version 1.8.0. SYNOPSISuse Log::Log4perl::Tiny qw( :easy ); Log::Log4perl->easy_init({ file => '/var/log/something.log', layout => '[%d] [%-5P:%-5p] %m%n', level => $INFO, }); WARN 'something weird happened'; INFO 'just doing it'; DEBUG 'this does not get printed at $INFO level'; # LOGLEVEL isn't in Log::Log4perl, but might come handy LOGLEVEL($DEBUG); # enable debugging for small section # otherwise, "get_logger()->level($DEBUG)", see below DEBUG 'now this gets printed'; LOGLEVEL($INFO); # disable debugging again DEBUG 'skipped, again'; DEBUG 'complex evaluation value:', sub { # evaluation skipped if log level filters DEBUG out }; # Object-oriented interface is available as well my $logger = get_logger(); $logger->level($DEBUG); # enable debugging for small section $logger->debug('whatever you want'); $logger->level($INFO); # disable debugging again # All stealth loggers are available LOGCONFESS 'I cannot accept this, for a whole stack of reasons!'; # Want to change layout? $logger->layout('[%d %p] %m%n'); # or, equivalently $logger->format('[%d %p] %m%n'); # Want to send the output somewhere else? use IO::Handle; open my $fh, '>>', '/path/to/new.log'; $fh->autoflush(); $logger->fh($fh); # Want to multiplex output to different channels? $logger->fh( build_channels( fh => \*STDERR, file_create => '/var/log/lastrun.log', file_append => '/var/log/overall.log', ) ); # Want to handle the output message by yourself? my @queue; # e.g. all log messages will be put here $logger->fh(sub { push @queue, $_[0] }); # As of 1.4.0, you can set key-value pairs in the logger $logger->loglocal(foo => 'bar'); LOGLOCAL(baz => 100); # You can later retrieve the value in the format with %{key}e $logger->format("[%{foo}e] [%{baz}e] %m%n"); # You are not limited to scalars, you can use references too LOGLOCAL(baz => sub { my ($data, $op, $ekey) = @_; return join '.', @{$data->{tod}}; # epoch from gettimeofday }); LOGLOCAL(foo => sub { return rand 100 }); DESCRIPTIONYes... yet another logging module. Nothing particularly fancy nor original, too, but a single-module implementation of the features I use most from Log::Log4perl for quick things, namely:
There are many, many things that are not included; probably the most notable one is the ability to provide a configuration file. Why?I have really nothing against Log::Log4perl, to the point that one of the import options is to check whether Log::Log4perl is installed and use it if possible. I just needed to crunch the plethora of modules down to a single-file module, so that I can embed it easily in scripts I use in machines where I want to reduce my impact as much as possible. Log LevelsLog::Log4perl::Tiny implements all standard Log::Log4perl's log levels, without the possibility to change them. The correspondent values are available in the following variables (in order of increasing severity or importance):
The default log level is $INFO. In addition to the above, the following levels are defined as well:
You can import these variables using the ":levels" import facility, or you can use the directly from the Log::Log4perl::Tiny namespace. They are imported automatically if the ":easy" import option is specified. Default Log Level As of version 1.1.0 the default logging level is still $INFO like any previous version, but it is possible to modify this value to $DEAD through the ":dead_if_first" import key. This import key is useful to load Log::Log4perl in modules that you want to publish but where you don't want to force the end user to actually use it. In other terms, if you do this: package My::Module; use Log::Log4perl::Tiny qw( :easy :dead_if_first ); you will import all the functionalities associated to ":easy" but will silence the logger off unless somewhere else the module is loaded (and imported) without this option. In this way:
Easy Mode OverviewI love Log::Log4perl's easy mode because it lets you set up a sophisticated logging infrastructure with just a few keystrokes: use Log::Log4perl qw( :easy ); Log::Log4perl->easy_init({ file => '>>/var/log/something.log', layout => '[%d] [%-5P:%-5p] %m%n', level => $INFO, }); INFO 'program started, yay!'; use Data::Dumper; DEBUG 'Some stuff in main package', sub { Dumper(\%main::) }; If you want, you can replicate it with just a change in the first line: use Log::Log4perl::Tiny qw( :easy ); Log::Log4perl->easy_init({ file => '>>/var/log/something.log', layout => '[%d] [%-5P:%-5p] %m%n', level => $INFO, }); INFO 'program started, yay!'; use Data::Dumper; DEBUG 'Some stuff in main package', sub { Dumper(\%main::) }; Well... yes, I'm invading the Log::Log4perl namespace in order to reduce the needed changes as mush as possible. This is useful when I begin using Log::Log4perl and then realise I want to make a single script with all modules embedded. There is also another reason why I put easy_init() in Log::Log4perl namespace: use Log::Log4perl::Tiny qw( :full_or_fake :easy ); Log::Log4perl->easy_init({ file => '>>/var/log/something.log', layout => '[%d] [%-5P:%-5p] %m%n', level => $INFO, }); INFO 'program started, yay!'; use Data::Dumper; DEBUG 'Some stuff in main package', sub { Dumper(\%main::) }; With import option "full_or_fake", in fact, the module first tries to load Log::Log4perl in the caller's namespace with the provided options (except "full_or_fake", of course), returning immediately if it is successful; otherwise, it tries to "fake" Log::Log4perl and installs its own logging functions. In this way, if Log::Log4perl is available it will be used, but you don't have to change anything if it isn't. Easy mode tries to mimic what Log::Log4perl does, or at least the things that (from a purely subjective point of view) are most useful: easy_init() and stealth loggers. easy_init()Log::Log4perl::Tiny only supports three options from the big brother, plus its own:
If you call easy_init() with a single unblessed scalar, it is considered to be the "level" and it will be set accordingly. Otherwise, you have to pass a hash ref with the keys above. In addition to the above keys, the easy_init() method installed by Log::Log4perl::Tiny also accepts all keys defined for "new", e.g. "format" (an alias for "layout") and the different alternatives to "file" ("file_insecure", "file_create" and "file_append"). Stealth LoggersStealth loggers are functions that emit a log message at a given severity; they are installed when ":easy" mode is turned on (see "Easy Mode Overview"). They are named after the corresponding level:
Additionally, you get the following logger functions (again, these are in line with Log::Log4perl):
If you want to set the exit code for "LOGEXIT" above (and "LOGDIE" as well, in case die() does not exit by itself), you can go "the Log::Log4perl way" and set $Log::Log4perl::LOGEXIT_CODE, or set a code with logexit_code() - but you have to wait to read something about the object-oriented interface before doing this! As indicated, functions "LOGWARN", "LOGDIE", "LOGCARP", "LOGCLUCK", "LOGCROAK", and "LOGCONFESS" (as well as their lowercase counterparts called as object methods) both emit the log message on the normal output channel for Log::Log4perl::Tiny and call the respective function. This might not be what you want in the default case where the output channel is standard error, because you will end up with duplicate error messages. You can avoid the call to the canonical function setting import option ":no_extra_logdie_message", in line with what Log::Log4perl provides. There is also one additional stealth function that Log::Log4perl misses but that I think is of the outmoste importance: "LOGLEVEL", to set the log level threshold for printing. If you want to be 100% compatible with Log::Log4perl, anyway, you should rather do the following: get_logger()->level(...); # instead of LOGLEVEL(...) This function does not get imported when you specify ":easy", anyway, so you have to import it explicitly. This will help you remembering that you are deviating from Log::Log4perl. Emitting LogsTo emit a log, you can call any of the stealth logger functions or any of the corresponding log methods. All the parameters that you pass are sent to the output stream as they are, except code references that are first evaluated. This lets you embed costly evaluations (e.g. generate heavy dumps of variabls) inside subroutines, and avoid the cost of evaluation in case the log is filtered out: use Data::Dumper; LOGLEVEL($INFO); # cut DEBUG and TRACE out TRACE 'costly evaluation: ', sub { Dumper($heavy_object) }; # Dumper() is not actually called because DEBUG level is # filtered out If you use the log() method, the first parameter is the log level, then the others are interpreted as described above. Log Line LayoutThe log line layout sets the contents of a log line. The layout is configured as a "printf"-like string, with placeholder identifiers that are modeled (with simplifications) after Log::Log4perl's ones: %c Category of the logging event. %C Fully qualified package (or class) name of the caller %d Current date in yyyy/MM/dd hh:mm:ss format %D Current date in strftime's "%Y-%m-%d %H:%M:%S.$u%z" (localtime) %{type}D Current date as strftime's "%Y-%m-%d %H:%M:%S.$u%z" (type can be utc or local) %{key}e Evaluate or substitute (extension WRT Log::Log4perl) %F File where the logging event occurred %H Hostname %l Fully qualified name of the calling method followed by the callers source the file name and line number between parentheses. %L Line number within the file where the log statement was issued %m The message to be logged %M Method or function where the logging request was issued %n Newline (OS-independent) %p Priority of the logging event %P pid of the current process %r Number of milliseconds elapsed from program start to logging event %R Number of milliseconds elapsed from last logging event including a %R to current logging event %T A stack trace of functions called %% A literal percent (%) sign Notably, both %x (NDC) and %X (MDC) are missing. The functionality for the latter is partially covered by the extension %e explained below. Moreover, the extended specifier feature with additional info in braces (like %d{HH:mm}) is missing, i.e. the structure of each specifier above is fixed. (Thanks to "Log::Tiny" for the cool trick of how to handle the "printf"-like string, which is probably mutuated from "Log::Log4perl" itself according to the comments). There are also two extensions with respect to Log::Log4perl, that help partially cover the missing items explained above, as of release 1.4.0:
As of release 1.4.0 all time-expansions in a single log refer to the same time, i.e. if you specify the format string "%D %D" and you have microsecond-level resolution, the two values in output will be the same (as opposed to show two slightly different times, related to the different expansion times of the %D specifier). Wrapping Log::Log4perl::TinyAs of release 1.4.0, all expansion sequences that imply using "caller" (namely %C, %F, %l, %L, %M, and %T) will honor whatever you set for $Log::Log4perl::caller_depth or $Log::Log4perl::Tiny::caller_depth (they're aliased), defaulting to value 0. You can basically increase this value by 1 for each wrapper function that you don't want to appear from the real caller's point of view. In the following example, we have two nested wrappers, each of which takes care to increase the value by 1 to be hidden: sub my_wrapper_logger { local $Log::Log4perl::Tiny::caller_depth = $Log::Log4perl::Tiny::caller_depth + 1; # ignore my_wrapper_logger INFO(@_); } # ... somewhere else... sub wrap_wrapper { local $Log::Log4perl::Tiny::caller_depth = $Log::Log4perl::Tiny::caller_depth + 1; # ignore wrap_wrapper my_wrapper_logger(@_); } The control variable is either $Log::Log4perl::Tiny::caller_depth or $Log::Log4perl::caller_depth, as a matter of fact they are aliased (i.e. changing either one will also change the other). This is intentional to let you switch towards Log::Log4perl should you need to upgrade to it. See "Using Log::Log4perl with wrapper functions and classes" in Log::Log4perl for further information. INTERFACEYou have two interfaces at your disposal, the functional one (with all the stealth logger functions) and the object-oriented one (with explicit actions upon a logger object). Choose your preferred option. Functional InterfaceThe functional interface sports the following functions (imported automatically when ":easy" is passed as import option except for "LEVELID_FOR", "LEVELNAME_FOR" and "LOGLEVEL"):
Object-Oriented InterfaceThe functional interface is actually based upon actions on a pre-defined fixed instance of a "Log::Log4perl::Tiny" object, so you can do the same with a logger object as well:
The methods you can call upon the object mimic the functional interface, but with lowercase method names:
The main logging function is actually the following:
Additionally, you have the following accessors:
DEPENDENCIESRuns on perl 5.8.0 on with no additional runtime requirements. See cpanfile for additional requirements when testing and/or developing. In particular, developing will require Log::Log4perl to perform a comparison between the expansions of a few items related to caller(). BUGS AND LIMITATIONSPlease view/report any bugs or feature requests through Github at <https://github.com/polettix/Log-Log4perl-Tiny/issues>. SEE ALSOLog::Log4perl is one of the most useful modules I ever used, go check it! AUTHORFlavio Poletti <polettix@cpan.org> COPYRIGHT AND LICENSECopyright (C) 2010-2022 by Flavio Poletti <polettix@cpan.org>. This module is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.
|