Log::ger::Manual::FAQ - FAQ on Log::ger
version 0.040.000
By default log messages don't go anywhere (stealth/null logging). Only after you set up an output, the messages will go somewhere:
use Log::ger::Output 'Screen'; use Log::ger; log_warn("hey!");
One mistake might be that you write this instead:
use Log::ger::Output::Screen;
This does nothing; you need to do:
use Log::ger::Output 'Screen';
or:
use Log::ger::Output; Log::ger::Output->set('Screen');
Another reason why your logs are not showing might be that you use levels info, debug, or trace. By default, log level is set to warn. You need to increase log level first to show messages logged at higher level.
info
debug
trace
warn
You need to import Log::ger using:
use Log::ger;
or (for loading in runtime):
require Log::ger; Log::ger->import;
and not:
use Log::ger ();
nor:
require Log::ger;
In other words, the importing is necessary because Log::ger sets up the logger routines to the calling package via import().
import()
This will not work either:
require Log::ger; Log::ger::log_warn("blah");
because the logger routines (log_warn et al) are not defined statically in the Log::ger package but constructed dynamically for each calling package.
log_warn
Log::ger
You need to do:
BEGIN { require Log::ger; Log::ger->import; }
instead of just:
In other words, the import() needs to happen during compile-time to give perl hints that the logger statements (log_*) are subroutines.
log_*
The default in Log::ger is to use procedural style:
use Log::ger; log_warn("blah"); if (log_is_debug()) { log_debug("Format: %s %s", "blah ...", {data=>'structure'}); }
However, you can also use objects:
use Log::ger (); # don't initialize and export logger subroutines my $log = Log::ger->get_logger; $log->warn("blah"); if ($log->is_debug) { $log->debug("Format: %s %s", "blah ...", {data=>'structure'}); }
For example, in Log::Any:
my $log = Log::Any->get_logger; my $log_dump = Log::Any->get_logger(category => "dump"); # to dump contents $log->debugf("Headers is: %s", $http_res->{headers}); $log_dump->debug($http_res->{content});
in Log::ger:
# instead of installing to package, we setup objects (or hashes) for the # secondary loggers my $log_dump = Log::ger->get_logger(category => "dump"); log_debug("Headers is: %s", $http_res->{headers}); $log_dump->debug($http_res->{content});
Examples of real code that produce logs to multiple loggers: WWW::PAUSE::Simple, Finance::Bank::ID::Base.
use Log::ger; $SIG{__WARN__} = sub { log_warn(join "", @_); warn @_; }; $SIG{__DIE__} = sub { log_fatal(join "", @_); die @_; }; warn "This is a warning"; # message will also be logged die "This is an error"; # message will also be logged before script dies
or you can use Log::ger::LogException which shortens the above incantation to just:
use Log::ger::LogException;
Overriding the builtin print is a bit complex as it is not overridable like some other builtin functions (see e.g.: https://www.perlmonks.org/?node_id=300471, https://www.perlmonks.org/?node_id=542712) although it's still doable via e.g. low-level manipulation or source filter trickery.
print
A simpler alternative is capturing output to the filehandle instead (STDERR, STDOUT), e.g. using Tie::STDOUT:
use Log::ger; use Tie::STDOUT print => sub { log_info join("", @_); };
If you are actually debugging with print() instead of any logging framework, take a look at Debug::Print.
print()
You can use Tie::STDERR:
use Log::ger; use Log::ger::Output File => (path => "/tmp/log"); use Tie::STDERR sub { log_warn(join "", @_) };
Now output to stderr will be logged to file /tmp/log.
See also the FAQ on how to log warn/die.
Normally, using the procedural interface you are logging under the category of your package:
package My::App; use Log::ger; log_warn("this will be logged under category 'My::App'");
If you want to log under a different category, you can use the OO interface:
package My::App; use Log::ger; my $log_foo = Log::ger->get_logger(category => "Foo"); $log_foo->warn("this will be logged under category 'Foo'");
For example, you want category Foo to go to a separate file /tmp/foo.log while the rest go to /path/app.log, you can do something like this:
Foo
/tmp/foo.log
/path/app.log
use Log::ger::Output Composite => ( outputs => { File => [ { conf => {path=>'/path/app.log'}, category_level => { Foo => 'off' }, }, { conf => {path=>'/path/foo.log'}, level => 'off', category_level => { Foo => 'trace' }, }, ], }, );
By default, the Log::ger formatter already does sprintf-style formatting:
log_warn("Format %s %s", "blah ...", {data=>'structure'});
If there is only one argument, no formatting is done.
log_warn("blah ...");
Logging multiple arguments is not supported by the default formatter because by default Log::ger adopts sprintf style:
log_warn("blah ...", "more blah ...");
Either join the arguments first, use sprintf style, or use some of the other formatters that support this, e.g. Log::ger::Format::Join and Log::ger::Like::LogAny.
Use a formatter like Log::ger::Format::Block, or Log::ger::Format::Flogger, or develop your own formatter to do what you want.
You can also do this:
if (log_is_trace()) { log_trace("Format %s", $foo->something_that_is_expensive_to_calculate); }
The default formatter already dumps data structures:
You can use a formatter like Log::ger::Format::None which will prevent your log message from being stringified. To output this to destination, combine this with a layout plugin like Log::ger::Layout::JSON or Log::ger::Layout::LTSV. Or perhaps write your own output module that accepts raw data structure instead of formatted string and send it somewhere.
For example, a la Log::Contextual:
log_warn { 'The number of stuffs is: ' . $obj->stuffs_count };
See Log::ger::Format::Block for an example.
Use a layouter, e.g. Log::ger::Layout::Pattern.
TODO
For example, you want to use [%r] %m on the screen, but the more complete [%d] [PID %P] %m in log file. Use Log::ger::Output::Composite, e.g.:
[%r] %m
[%d] [PID %P] %m
use Log::ger::Output Composite => ( outputs => { Screen => { layout => [Pattern => {format => '[%r] %m'}], }, File => { path => '/path/to/logfile', layout => [Pattern => {format => '[%d] [PID %P] %m'}], }, }, );
For example, with the [%r] %m layout, you want a multiline log message "line1\nline2\nline3" to be laid out not as:
"line1\nline2\nline3"
[0.003] line1 line2 line3
but as:
[0.003] line1 [0.003] line2 [0.003] line3
You can use Log::ger::Layout::Pattern::Multiline instead of Log::ger::Layout::Pattern to accomplish this.
One way:
use Log::ger (); BEGIN { our %Log::ger::Levels = ( critical => 1, error => 2, warning => 3, info => 4, extra => 5, ); our %Log::ger::Level_Aliases = ( warn => 3, verbose => 4, );
Do this before initializing any package with use Log::ger. The above example will create these logger routines: log_critical, log_error, log_warning, log_info, log_extra. The aliases won't get the logging routines but Log::ger::Util::numeric_level will recognize them.
use Log::ger
log_critical
log_error
log_warning
log_info
log_extra
Log::ger::Util::numeric_level
ALternatively, you can use one of the available Log::ger::Level::Like::* (like Log::ger::Level::Like::LogAny) which basically perform something like the above so you can just say use Log::ger::Level::Like::LogAny.
Log::ger::Level::Like::*
use Log::ger::Level::Like::LogAny
Or, you can write your own Log::ger::Level::Like:: module.
Log::ger::Level::Like::
Just issue another Log::ger::Output->set() which will replace previous output plugin.
Log::ger::Output->set()
Log::ger::Output->set("Screen"); log_warn("foo!"); # goes to screen Log::ger::Output->set("SimpleFile", path=>"app.log"); log_warn("bar!"); # goes to file
Use Log::ger::Output::Composite, which can multiplex log message to multiple outputs, including multiple outputs of the same type (e.g. two or more File's).
Using Log::ger::Output::Composite's per-output level:
use Log::ger::Output 'Composite' => ( outputs => { Screen => { level => ['trace', 'debug'], }, File => { conf => { path=>'/path/to/file.log' }, level => ['warn', 'error'], }, }, );
See Log::ger::Manual::Tutorial::481_Output_Composite for a more detailed example.
Using Log::ger::Output::Composite's per-output level. Note that you can specify multiple outputs of the same kind (in this case, File):
use Log::ger::Output 'Composite' => ( outputs => { File => [ { conf => { path=>'file1.log' }, level => ['trace', 'debug'], }, { conf => { path=>'file2.log' }, level => ['warn', 'error'], }, ], ], );
Using Log::ger::Output::Composite's per-category level:
use Log::ger::Output 'Composite' => ( outputs => { Screen => {...}, File => {...}, }, category_level => { 'MyApp::SubModule1' => 'info', 'MyApp::SubModule2' => 'debug', ... }, );
or per-output, per-category level:
use Log::ger::Output 'Composite' => ( outputs => { Screen => { category_level => { 'MyApp::SubModule1' => 'info', 'MyApp::SubModule2' => 'debug', }, }, ... }, );
You need to use a file output module which supports locking, e.g. FileWriteRotate or File then enable locking so that on every log a lock is acquired first.
One nice thing about the FileWriteRotate output module is that File::Write::Rotate provides a buffer so when you temporarily fail writing (e.g. disk is momentarily full or lock momentarily cannot be acquired) log messages (up to a certain number of them) is kept at the memory buffer first.
An application might have more than one kind of logs. A common thing is for an application to have two kinds of logs: an error log and an access log (like in Apache or Squid). You can use Log::ger for both kinds of log and have them easily configurable.
To produce different kinds of logs, you can log to multiple loggers with different category. Here you have choices.
One procedural, the other OO. Let's say for the error log you use the default logger with default procedural style, and for the access log you use another logger with OO style and a specific category.
use Log::ger; my $access_log = Log::ger->get_logger(category => 'access'); log_warn "goes to error log"; $access_log->info("goes to access log");
All OO. Or you can use OO style for both:
use Log::ger (); # don't export log_* procedural routines my $error_log = Log::ger->get_logger(category => 'error'); my $access_log = Log::ger->get_logger(category => 'access'); $error_log->warn("goes to error log"); $access_log->info("goes to access log");
All procedural. You can use something like Log::ger::Plugin::Multisets to create multiple sets of logger routines, each for each category:
use Log::ger::Plugin Multisets => ( log_sub_prefixes => { log_ => 'error', access_ => 'access', }, is_sub_prefixes => { is_ => 'error', access_is => 'access', }, ); # this will create log_error, log_warn, et al to log under the category # 'error'; and access_error, access_warn, et al to log under the category # 'access'. use Log::ger; log_warn "goes to error log"; access_info "goes to access log";
To use a plugin only for the current package:
package MyPackage; use Log::ger::Plugin; Log::ger::Plugin->set_for_current_package( 'PluginName', conf1 => ..., ...); use Log::ger;
Do the same thing for format (using Log::ger::Format), layout (using Log::ger::Layout), or output (using Log::ger::Output).
I was a happy user of Log::Any from 2009-2014. Log::Any 0.15 is very lightweight and I was happily peppering 'use Log::Any' to many of my modules. Then Log::Any 1.00 was released on Christmas day 2014. David Golden took over Log::Any's maintenance from its original creator Jonathan Swartz, and decided to add Log::Any::Proxy while bringing in some other modules like Data::Dumper, base, Carp. Those modules are core modules and not particularly huge themselves, but the result is that startup overhead of the 'use Log::Any' statement increased from ~1-2ms (0.15) to ~10ms (1.00). To me this means Log::Any is no longer a tiny little module I can sneak in the use of to just about any other module without care. At that time, I was also developing a CLI framework and was interested in doing tab completion using Perl scripts, so startup overhead is a very important issue for me.
Since then, I planned to write my own replacement of Log::Any. Aside from improving the startup overhead situation, I also wanted a logging library that uses procedural style instead of OO. One of the reasons for that, aside from obvious preference, was that so I can optimize away the logging statements if unneeded (like how asserts work). Another wishlist I had for a logging library is the ease of setting and re-setting levels, outputs, etc during runtime.
A long vacation in Jun-Jul 2017 gave me the opportunity to finally write Log::ger.
By the way, the startup overhead of Log::Any (1.708 at the time of this writing) is better now. But it will probably never be as lightweight as Log::Any 0.15 again, or as lightweight as Log::ger.
My blog post in 2017, which introduces Log::ger and explains basically the same thing as above: https://perlancar.wordpress.com/2017/07/13/introducing-logger/
perlancar <perlancar@cpan.org>
This software is copyright (c) 2022, 2020, 2019, 2018, 2017 by perlancar <perlancar@cpan.org>.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install Log::ger::Manual, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Log::ger::Manual
CPAN shell
perl -MCPAN -e shell install Log::ger::Manual
For more information on module installation, please visit the detailed CPAN module installation guide.