Log::Any - Now With Structured Logging
The first trial release of Log::Any with mephinet's structured and contextual logging support has been released for feedback!
These features make it easier to log structured data which will then be picked up by log parsers. Adding a single, unblessed hashref as the last argument to a log method will write that data structure as a compact entry at the end of your log string:
use Log::Any '$LOG';
use Log::Any::Adapter 'Stdout';
$LOG->info( "Hello, World", { foo => 'bar' } );
$ perl test.pl
Hello, World {foo => "bar"}
Even better, you can add values to the logging context, which will also add those values to the log string as a compact data structure. Using local will automatically remove those values at the end of the scope.
use Log::Any '$LOG';
use Log::Any::Adapter 'Stdout';
local $LOG->context->{fizz} = "buzz";
$LOG->info( "Hello, World" );
$ perl test.pl
Hello, World {fizz => "buzz"}
And you can combine the two:
use Log::Any '$LOG';
use Log::Any::Adapter 'Stdout';
local $LOG->context->{fizz} = "buzz";
$LOG->info( "Hello, World", { foo => "bar" } );
$ perl test.pl
Hello, World {foo => "bar"} {fizz => "buzz"}
Adapters get these structures directly and can decide how to most effectively use them. For example, the Log4perl Log::Any adapter would likely map the Log::Any context to the Log4perl Mapped Diagnostic Context).
This is a trial release and the features may be changed slightly for simplicity and backwards-compatibility. Please give these new features a test and let us know how you like them by commenting here, by opening a ticket on the Log::Any ticket tracker on Github, or by e-mailing me preaction@cpan.org.
Hi, the context feature seems like it might be quite useful. (I'm not sure I see the value of passing structured data since you can already do that with debugf(), infof(), etc...)
I wonder if there's any chance of getting it to support passing a coderef, so that the content of the string emitted could be generated at calltime. For example, it might be nice to be able to create a custom caller string using something like
local $log->context->{'caller'} = sub { (caller(0))[3] ... };
.The difference between what happens with
debug(..., \%href)
anddebugf(...,\%href)
is the formatter callback and what the Adapter object receives: If the adapter implements a specific method (structured
), it gets the raw arguments unedited so that it can use its own method of structured logging (Log4perl's MDC for example). In no case withdebug()
will the formatter get called. The rules for the formatting set of functions (debugf()
and friends) have not changed: Adapters get the string after Log::Any has done all of its formatting.I'm not sure if I like the idea of putting coderefs in the context, especially for the reason you mentioned (getting a particular stack frame): How many stack frames you need to walk up to get to the right one is not part of the API of Log::Any, and changes to Log::Any's internals will break that. However, I'd be totally open to finding ways to better emit tracing information (I suspect they'd largely be part of
perl
's debugger hooks though).If there are compelling reasons to allow coderefs in the structured/context data, I'm open to it, but right now the data is just run through Data::Dumper with some terse settings. Any changes would have to be fast (so, no recursing data structures to find coderefs to execute).
Log::Any's purpose is to delegate to another logging system as unobtrusively as possible. If it's not something that can be directly mapped to another logging system, then it's likely not a good candidate for Log::Any. These new APIs are meant to improve support for advanced logging features of other systems while bringing some part of these features to systems which don't have them.
Thanks for the comprehensive reply ++
Thanks for releasing the changes as a trial! For your information: I've migrated my $work's internal Log::Any::Adapter to use structured logging and the context hash - and things are working fine!