The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Log::Any::Simple - Very thin wrapper around Log::Any, using a functional interface that dies automatically when you log above a given level

SYNOPSIS

use Log::Any::Simple ':default';

info 'Starting program...';
debug 'Printing the output of a costly function: %s', sub { costly_data() };
trace 'Printing structured data: %s', $ref_to_complex_data_structure;
fatal 'Received a %s signal', $signal;

DESCRIPTION

Disclaimer: Log::Any is already quite simple, and our module name does not imply otherwise. Maybe Log::Any::SlightlySimpler would have been a better name.

Log::Any::Simple is offering a purely functional interface to Log::Any, removing all possible clutter. The first intent, however, was to die() when logging at the fatal() level or above, so that the application using the module can control how much stack trace is printed in that case.

The main features of the module, in addition to those of Log::Any, are:

  • Purely functional interface with no object to manipulate.

  • Supports dying directly from call to the log function (by default at the fatal level and above, but this can be configured).

  • The consumer application can control the amount of stack-trace produced when a module dies with Log::Any::Simple.

  • Options to trivially control the log output, for simple programs or for tests.

  • Support for a simple command line based log output.

  • Support for lazily producing logged data.

  • Several formatting options for dumping data-structure.

Except for that stack trace control, the usage of Log::Any::Simple on the application side (log consumer), is exactly the same as the usage of Log::Any. See Log::Any::Adapter documentation, for how to consume logs in your main application and Log::Any::Test for how to test your logging statements.

Importing

You can pass the following parameters on the use Log::Any::Simple line:

  • :default will export the following names in your namespace: trace, debug, info, warning, error, and fatal.

  • :all will export logging methods for all the log levels supported by Log::Any as well as for all their aliases.

  • :die_at => level_name specifies the lowest logging level that triggers a call to die() when used. By default this is fatal (and so, the critical, alert, and emergency levels also dies). You can also pass none to disable this behavior entirely.

  • :die_silence_msg By defaults, when the library dies, the call to die() receive the entire log message that was passed to the logging function. This may results in duplicated log messages, depending on how your logging is configured.

    This parameter makes it so that die() will only receive a shorter message.

  • :die_repeats_msg This is the opposite of :die_silence_msg and restores the default behavior of passing the entire fatal message to die().

  • :category => category_name specifies the logging category to use. If not specified, this defaults to your package name.

  • :prefix => prefix_value sets a prefix that is prepended to all logging messages. This is handled by directly passing this value to the Log::Any::Proxy object used internally.

  • :dump_long will use a multi-line layout for rendering complex data-structures that are logged.

  • :dump_short will use a compact single-line layout for rendering complex data-structures that are logged. This is the default.

  • :to_stderr => level_name Activate logging for messages at the given level or above. All logs messages are prefixed with their level name and sent to STDERR. In general this is meant for tests or very simple programs. Note that this option has a global effect on the program and cannot be turned off.

  • :to_stdout => level_name is the same as :to_stderr but sending the messages to STDOUT instead of STDERR.

  • :from_argv parses @ARGV and activates logging based on its content. For now this is not configurable and will look for a single --log argument in the command line, that can take only a single log level as argument (either as two consecutive command line arguments or as --log=level). If found, log messages at that level or above will be sent to STDERR. The parsed arguments are removed from @ARGV. Note that this option has a global effect on the program and cannot be turned off.

In addition to these options, you can pass the names of any of the valid level or alias as documented at "LOG LEVELS" in Log::Any to import just these methods and you can pass the name of Log::Any::Simple public methods to import them as well, as is standard.

If you do not import the logging methods into your package, you can still call them directly from the Log::Any::Simple namespace, but this is slightly less efficient than importing them.

Logging methods

While we use the default level names for our methods (info, error, etc.), they behave more like the f variant of the "Logging" in Log::Any methods. That is, they expect any number of arguments where the first argument is a format string following the sprintf syntax and the rest are the arguments for the sprintf call.

In addition to the normal behavior the following values can be passed in the list of arguments (except in the first position):

  • A code reference, which will be called if the logging actually happens. This is useful when you want to log large or costly data-structures to avoid generating them if detailed logging is not activated by the application.

  • Any other data reference, which will be dumped through Data::Dumper. The formatting being used can be controlled through the :dump_short and :dump_long arguments passed on the use Log::Any::Simple line. Note that you can use a code reference that returns a data-reference and the data will be generated and dumped lazily (in all cases, it will be dumped lazily even if not returned by a code reference).

  • An undef value, which will be rendered as <undef>.

Although this should be uncommon, you can call the get_logger() method (which can be imported) to retrieve the underlying Log::Any logger being used internally for your package. But note that using this object bypass all the specific behavior of Log::Any::Simple. One use of this object is to call the is_xxx() method family, indicating whether a given log level is activated. However, thanks to the lazy-logging behavior of our module, the need for that should be infrequent.

Controlling stack-traces

You can use the die_with_stack_trace() method (that can be imported) to control the amount of stack-trace printed when the library dies following a call to a logging method above the :die_at level. This is meant to be called by the application consuming the logs, rather than by the module producing them (although this is possible too).

This method can be called in two ways:

die_with_stack_trace($category => $mode);
die_with_stack_trace($mode);

The first syntax sets a stack-trace mode for a specific logging category (by default the package name from which you log) and the second sets a fallback mode for categories for which you didn’t set a specific mode.

The valid values for $mode are the following:

  • none: no stack trace is printed at all, just the log message that goes to STDERR, in addition to the default logging destination.

  • short: a short stack trace is printed, with just the name of the calling method (similar to the default behavior of die()).

  • long: a long stack trace is printed, with all the chain of the calling methods (similar to the behavior of croak()).

  • undef: delete the global or per-category setting for the stack trace mode.

When neither a global nor a per-category mode is set, the default is short.

RESTRICTIONS

Importing this module more than once in a given package is not supported and can give unpredictable results.

AUTHOR

This program has been written by Mathias Kende.

COPYRIGHT AND LICENSE

Copyright 2024 Mathias Kende

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

SEE ALSO