NAME

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

VERSION

version 0.040.000

GENERAL/ETC

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;

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

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.

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 (log_*) are subroutines.

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

Examples of real code that produce logs to multiple loggers: WWW::PAUSE::Simple, Finance::Bank::ID::Base.

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.

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::Format::Join and 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.

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

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.

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

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

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.

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.

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

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

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

THE PROJECT

Why was Log::ger created?

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/

SEE ALSO

AUTHOR

perlancar <perlancar@cpan.org>

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.