NAME

Log::ger::Manual::ForLogAny - Log::ger for Log::Any users

VERSION

version 0.040.000

WHY LOG::ANY

Log::Any is one of the first logging libraries on CPAN which promotes separation of producers and consumers. This means, instead of having to configure logging output in your module like this:

package MyLib;
use Log::SomeLib;

my $log = Log::SomeLib->new(
    output => ...,
    level => ...,
);

sub mymethod {
    $log->warn("blah ...");
}

you just log:

package MyLibrary;
use Log::Any '$log';

sub mymethod {
    $log->warn("blah ...");
}

and the configuration of outputs and levels is done on the application side.

I loved Log::Any and was a happy user for a long time, but the increasing startup overhead annoyed me enough to start the Log::ger project (see this FAQ entry for more details: "Why was Log::ger created?" in Log::ger::Manual::FAQ).

WHY LOG::GER OVER LOG::ANY

Log::ger can be used in procedural style in addition to OO style. Aside from preference, this gives the benefit of being able to optimize away unneeded logging statements to avoid runtime overhead (see Log::ger::Plugin::OptAway). A procedural wrapper for Log::Any could also be written to accomplish the same, but this is native in Log::ger.

Log::ger has a smaller startup overhead compared to Log::Any. Log::Any used to be very light also (startup overhead under 1ms) until version 0.15.

Log::ger gives you customizable levels and routine names.

Log::ger allows you to log in a custom format, e.g. using block a la Log::Contextual, raw data structure as JSON, etc.

MIGRATING

To ease migrating, Log::ger::Like::LogAny is provided. You can change this line in your code:

use Log::Any;

into:

use Log::ger::Like::LogAny;

and this:

my $log = Log::Any->get_logger;

into:

my $log = Log::Any::Like::LogAny->get_logger;

and this:

use Log::Any '$log';

into:

use Log::ger::Like::LogAny '$log';

FAQ

In my application, I have some modules using Log::Any and some using Log::ger. I want to consume logs using Log::Any, how do I do that?

Install Log::ger::Output::LogAny then in your application:

use Log::ger::Output 'LogAny';

This will send logs produced via Log::ger to Log::Any.

In my application, I have some modules using Log::Any and some using Log::ger. I want to consume logs using Log::ger, how do I do that?

Install Log::Any::Adapter::LogGer then in your application:

use Log::Any::Adapter 'LogGer';

This will send logs produced via Log::Any to Log::ger.

How to set an output "lexically", like with the 'lexically' option in Log::Any::Adapter?

In Log::Any, this is a way to set an adapter temporarily:

{
    Log::Any::Adapter->set({lexically => \my $lex}, 'Name', ...);
    ...
} # when $lex goes out of scope, the adapter setting is removed

One way to do this in Log::ger:

my $saved = Log::ger::Util::save_hooks('create_outputter');
Log::ger::Output->set('Name', ...);
...
Log::ger::Util::restore_hooks('create_outputter', $saved);

A nicer interface may be provided in the future.

How to log structured data?

In Log::Any, you log additional data via an additional structured data after the message:

$log->info("message", {additional=>1, data=>2});

or even just log structured data:

$log->info({message=>"msg", {additional=>1, data=>2}, ['more', 'data']);

You must use an adapter that can handle structured data, i.e. one that defines structured method. This method will then receive the level, category, and the arguments as-is. Whereas an adapter that cannot handle structured data will get a single string where the structured is dumped and joined with a single space:

"message {additional=>1,data=>2}"
{message=>"msg",additional=>1,data=>2} ['more','data']"

Note that you must use the "non-f" variant of the logging methods. You cannot do this:

$log->infof("message", {additional=>1, data=>2});

as the first argument will be treated as sprintf format and the rest are arguments for sprintf.

In Log::ger, you can use the None formatter. This means your arguments will not be formatted and sent as-is to the output module:

use Log::ger::Format 'None';
log_info("message", {additional=>1, data=>2});
log_info({message=>'msg', additional=>1, data=>2}, ['more','data']);

You can then choose an output module that can handle structured data.

What about contextual logging?

Contextual logging is attaching additional information along with your message.

In Log::Any this is done by adding keys to $log->context which is a hashref:

$log->context->{progname} = "myapp";
$log->info("Starting program");

if (my $errmsg = error_condition()) {
    local $log->context->{file} = $path;
    $log->error("Can't process file: $errmsg");
}

The adapter must be able to handle structured data. The context will be passed to the adapter's structured method as if it were an additional argument to the logging function:

("Starting program", {progname=>"myapp"})
("Can't process file: some error msg", {progname=>"myapp", file=>"/some/path"})

If the adapter cannot handle structured data, the context hashref will be dumped to string:

"Starting program {progname=>"myapp"}"
"Can't process file: some error msg {progname=>'myapp',file=>'/some/path'}"

In Log::ger this is currently not yet implemented as I haven't decided how to best implement it.

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.