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::ger::Manual::Tutorial::481_Output_Composite - More on the Composite output

VERSION

version 0.040.000

DESCRIPTION

EXAMPLE 1: access log and error log

A very common thing to have in an application is two kids 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.

Producing

You have two choices. First, you use the default logger with default procedural style for the error log, and another logger with OO style for the access log:

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

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

Or you can use procedural style for both using Log::ger::Plugin::Multisets:

 use Log::ger::Plugin Multisets => (
     log_sub_prefixes => {
         # prefix  => init args
         log_      => {category=>'error' }, # or undef, to use the default init args (including category)
         access_   => {category=>'access'},
     },
     is_sub_prefixes => {
         # prefix   => category
         is_        => {category=>'error' },
         access_is_ => {category=>'access'},
     },
 );
 use Log::ger;

 access_info "goes to access log";
 access_warn "goes to access log";
 log_warn    "goes to error log";
 log_debug   "goes to error log";
 ...

The example below will use the first choice.

Usually, for the access log you will need to log additional pieces of information like the current user (and/or IP, and/or process ID, etc). A simple format for the access log is JSON, where each JSON object (hash) is printed as a single line and contains the necessary information pieces in hash keys. So let's create a wrapper to supply this information in a more convenient manner:

 use Log::ger::Format 'None';

 sub access_log {
     my ($self, $action, $object, $note) = @_;
     $access_log->info({
         time   => time(),
         user   => $self->user,
         ip     => $self->user_ip,
         pid    => $pid,

         action => $action,
         object => $object,
         note   => $note,
     });
 }

 sub handle_request {
     my $self = shift;
     ...
     if ($is_success) {
         $self->access_log($action => {file=>$file});
     } else {
         log_error ...;
     }
 }

Consuming

To display the logs, you can use Log::ger::Output::Composite, for example with this configuration:

 use Log::ger::Output Composite => (
     outputs => {
         Screen => {
             category_level => {access=>'off'}, # we don't show access log to screen
         },
         File => [
             # error log file
             {
                 conf => {path=>'/path/to/app-error.log'},
                 category_level => {access=>'off'}, # we don't show access log to error log file
             },
             # access log file
             {
                 conf => {path=>'/path/to/app-access.log'},
                 level => 'off',
                 category_level => {access=>'info'}, # we only show access log to access log file
             },
         ],
     },
 );

See also a real-world example in WWW::PAUSE::Simple and App::pause.

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.