Group
Extension

Log-ger-Manual/lib/Log/ger/Manual/FAQ.pod

package Log::ger::Manual::FAQ;

# AUTHORITY
# DATE
our $DIST = 'Log-ger-Manual'; # DIST
# VERSION

1;
# ABSTRACT: FAQ on Log::ger

__END__

=pod

=encoding UTF-8

=head1 NAME

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

=head1 VERSION

version 0.040.000

=head1 GENERAL/ETC

=head2 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
C<info>, C<debug>, or C<trace>. By default, log level is set to C<warn>. You
need to increase log level first to show messages logged at higher level.

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

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 C<import()>.

This will not work either:

 require Log::ger;
 Log::ger::log_warn("blah");

because the logger routines (C<log_warn> et al) are not defined statically in
the C<Log::ger> package but constructed dynamically for each calling package.

=head2 Why am I getting "String found where operator expected at ..., near "log_warn "foo""? Why do I need to put parentheses after the log_* statements?

You need to do:

 use Log::ger;

or:

 BEGIN {
     require Log::ger;
     Log::ger->import;
 }

instead of just:

 require Log::ger;
 Log::ger->import;

In other words, the import() needs to happen during compile-time to give perl
hints that the logger statements (C<log_*>) are subroutines.

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

=head2 How to create multiple loggers?

For example, in L<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:
L<WWW::PAUSE::Simple>, L<Finance::Bank::ID::Base>.

=head2 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 L<Log::ger::LogException> which shortens the above incantation to
just:

 use Log::ger::LogException;

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

Overriding the builtin C<print> is a bit complex as it is not overridable like
some other builtin functions (see e.g.:
L<https://www.perlmonks.org/?node_id=300471>,
L<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 L<Tie::STDOUT>:

 use Log::ger;
 use Tie::STDOUT
     print => sub {
         log_info join("", @_);
     };

If you are actually debugging with C<print()> instead of any logging framework,
take a look at L<Debug::Print>.

=head2 How to log stderr output?

You can use L<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 F</tmp/log>.

See also the FAQ on how to log warn/die.

=head1 CATEGORY

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

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

For example, you want category C<Foo> to go to a separate file C</tmp/foo.log>
while the rest go to C</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' },
             },
         ],
     },
 );

=head1 FORMAT & LAYOUT

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

=head2 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. L<Log::ger::Format::Join> and
L<Log::ger::Like::LogAny>.

=head2 How to use deferred calculation of arguments?

Use a formatter like L<Log::ger::Format::Block>, or
L<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);
 }

=head2 How to dump data structures?

The default formatter already dumps data structures:

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

=head2 How to log raw data structure?

You can use a formatter like L<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 L<Log::ger::Layout::JSON> or
L<Log::ger::Layout::LTSV>. Or perhaps write your own output module that accepts
raw data structure instead of formatted string and send it somewhere.

=head2 How to do custom formatting?

For example, a la L<Log::Contextual>:

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

See L<Log::ger::Format::Block> for an example.

=head2 How to add timestamps (and other stuffs)?

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

=head2 How to use microsecond in timestamps?

TODO

=head2 How to redact sensitive information?

TODO

=head2 How to customize layout per output?

For example, you want to use C<[%r] %m> on the screen, but the more complete
C<[%d] [PID %P] %m> in log file. Use L<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'}],
         },
     },
 );

=head2 How to split multiline log message in a layout?

For example, with the C<[%r] %m> layout, you want a multiline log message
C<"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 L<Log::ger::Layout::Pattern::Multiline> instead of
L<Log::ger::Layout::Pattern> to accomplish this.

=head1 LEVEL

=head2 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 C<use Log::ger>. The above example
will create these logger routines: C<log_critical>, C<log_error>,
C<log_warning>, C<log_info>, C<log_extra>. The aliases won't get the logging
routines but C<Log::ger::Util::numeric_level> will recognize them.

ALternatively, you can use one of the available C<Log::ger::Level::Like::*>
(like L<Log::ger::Level::Like::LogAny>) which basically perform something like
the above so you can just say C<use Log::ger::Level::Like::LogAny>.

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

=head1 OUTPUT

=head2 How to switch output?

Just issue another C<< 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

=head2 How to send logs to several outputs?

Use L<Log::ger::Output::Composite>, which can multiplex log message to multiple
outputs, including multiple outputs of the same type (e.g. two or more
L<File|Log::ger::Output::File>'s).

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

Using L<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 L<Log::ger::Manual::Tutorial::481_Output_Composite> for a more detailed
example.

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

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

=head2 How to filter by category?

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

=head1 OUTPUT: FILE

=head2 How to have different applications log to the same file?

You need to use a file output module which supports locking, e.g.
L<FileWriteRotate|Log::ger::Output::FileWriteRotate> or
L<File|Log::ger::Output::File> then enable locking so that on every log a lock
is acquired first.

One nice thing about the FileWriteRotate output module is that
L<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.

=head2 How to log different things to different files (with different levels)?

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.

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

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

B<All procedural.> You can use something like L<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";

See L<Log::ger::Manual::Tutorial::481_Output_Composite> for a more detailed
example.

=head1 TARGETS

=head2 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 L<Log::ger::Format>), layout (using
L<Log::ger::Layout>), or output (using L<Log::ger::Output>).

=head1 THE PROJECT

=head2 Why was Log::ger created?

I was a happy user of L<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 L<Data::Dumper>,
L<base>, L<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:
L<https://perlancar.wordpress.com/2017/07/13/introducing-logger/>

=head1 SEE ALSO

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

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.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.