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

NAME

Log::ger::Manual::FAQ - FAQ on Log::ger

VERSION

version 0.028.005

GENERAL

Why am I not seeing the logs?

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.

Why am I getting error "Undefined subroutine &main::log_warn called at ..."?

You need to import Log::ger using:

 use Log::ger;

and not:

 require Log::ger;
 use Log::ger ();

because Log::ger sets up the logger routines to the calling package via 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.

How to use OO style?

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'});
 }

How to create multiple loggers?

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});

How to log Perl warning/die message? (How to trap warn/die? How to override warn/die?)

 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;

How to log stdout output? (How to trap print? How to override print?)

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.

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.

How to log stderr output?

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.

OUTPUT

How to switch output?

Just issue another Log::ger::Output->set() which will replace previous output plugin.

 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

How to send logs to several outputs?

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).

How to send trace/debug messages to screen, but warnings/errors to file?

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'],
         },
     },
 );

How to send trace/debug messages to a file, but warnings/errors to another file?

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'],
             },
         ],
     ],
 );

How to filter by category?

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',
             },
         },
         ...
     },
 );

OUTPUT: FILE

How to have different applications log to the same file?

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.

LEVEL

How to use custom levels?

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 logging 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.

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.

Or, you can write your own Log::ger::Level::Like:: module.

CATEGORY

How to log under a different category than the current package?

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'");

How to direct log messages under a certain category to a separate output?

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:

 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' },
             },
         ],
     },
 );

FORMAT & LAYOUT

How to do sprintf-style formatting?

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 ...");

Why doesn't Log::ger log multiple arguments?

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::Like::LogAny.

How to use deferred calculation of arguments?

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);
 }

How to dump data structures?

The default formatter already dumps data structures:

 log_warn("Format %s %s", "blah ...", {data=>'structure'});

How to log raw data structure?

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.

How to do custom formatting?

For example, a la Log::Contextual:

 log_warn { 'The number of stuffs is: ' . $obj->stuffs_count };

See Log::ger::Format::Block for an example.

How to add timestamps (and other stuffs)?

Use a layouter, e.g. Log::ger::Layout::Pattern.

How to use microsecond in timestamps?

TODO

How to redact sensitive information?

TODO

How to customize layout per output?

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.:

 use Log::ger::Output Composite => (
     outputs => {
         Screen => {
             layout => [Pattern => {format => '[%r] %m'}],
         },
         File => {
             path => '/path/to/logfile',
             layout => [Pattern => {format => '[%d] [PID %P] %m'}],
         },
     },
 );

How to split multiline log message in a layout?

For example, with the [%r] %m layout, you want a multiline log message "line1\nline2\nline3" to be laid out not as:

 [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.

TARGETS

How to customize format/layout, output, plugin on a per-target basis?

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).

SEE ALSO

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019, 2018, 2017 by 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.