CGI::A...ispatch(3pm)

πŸ“¦
version 1.02 Β· 4 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– CGI::Application::Plugin::LogDispatch β€” the perl manpage, but make it make sense πŸ’–

CGI::A...ispatch(3pm) Β· User Contributed Perl Documentation Β· CGI::Application::Plugin::LogDispatch(3pm)

okay bestie sit DOWN because this one is actually so useful. it's the manual page for CGI::Application::Plugin::LogDispatch, and it's literally just "hey, want logging in your CGI::Application app? here, have a whole Log::Dispatch dispatcher, no big deal." πŸŽ€


✨ NAME ✨

CGI::Application::Plugin::LogDispatch β€” adds Log::Dispatch support to CGI::Application. that's it. that's the whole job. and it ate. πŸ’…


πŸ¦‹ SYNOPSIS πŸ¦‹

package My::App;

use CGI::Application::Plugin::LogDispatch;

sub cgiapp_init {
  my $self = shift;

  # calling log_config is optional as
  # some simple defaults will be used
  $self->log_config(
    LOG_DISPATCH_MODULES => [
      {    module => 'Log::Dispatch::File',
             name => 'debug',
         filename => '/tmp/debug.log',
        min_level => 'debug',
      },
    ]
  );
}

sub myrunmode {
  my $self = shift;

  $self->log->info('Information message');
  $self->log->debug('Debug message');
}

- or as a class based singleton -

package My::App;

use CGI::Application::Plugin::LogDispatch (
  LOG_DISPATCH_MODULES => [
    {    module => 'Log::Dispatch::File',
           name => 'debug',
       filename => '/tmp/debug.log',
      min_level => 'debug',
    },
  ]
);

My::App->log->info('Information message');

sub myrunmode {
  my $self = shift;

  $self->log->info('This also works');
}

two ways to do the thing: per-instance config in cgiapp_init, or full-on singleton mode at use time. pick your fighter. πŸ₯Š


🌸 DESCRIPTION 🌸

CGI::Application::Plugin::LogDispatch adds logging support to your CGI::Application modules by handing you a Log::Dispatch dispatcher object that you can grab from anywhere in the application. literally anywhere. no cap. ✨

and if you've got CGI::Application::Plugin::DevPopup installed? bestie. a "Log Entries" report gets added to the popup window, containing every single entry that got logged while the runmode was executing. it's giving ✨observability✨ and i'm obsessed.


πŸ‘‘ METHODS πŸ‘‘

πŸŽ€ log

this method returns the current Log::Dispatch dispatcher object. the Log::Dispatch object gets created on the first call to this method, and every call after that returns the same object. so basically it creates a singleton log dispatcher for the duration of the request. iconic behavior honestly.

and if log_config hasn't been called before that first call to log? no worries, it'll just pick some sane defaults to build the dispatcher object with (the exact default values are defined below, keep scrolling 🫢).

# retrieve the log object
my $log = $self->log;
$log->warning("something's not right!");
$log->emergency("It's all gone pear shaped!");

- or -

# use the log object directly
$self->log->debug(Data::Dumper::Dumper(\%hash));

- or -

# if you configured it as a singleton
My::App->log->debug('This works too');

πŸ¦‹ log_config

this method is how you customize the functionality of the CGI::Application::Plugin::LogDispatch module. important: calling it does not mean a new Log::Dispatch object gets created right away. the log object doesn't get created until the first call to $self->log. it's patient like that. πŸ’…

the recommended place to call log_config is in the cgiapp_init stage of CGI::Application. and heads up β€” if you call this method after the log object has already been accessed, it will die with an error message. so like. do it early bestie. 😭

and if you never call this method at all? a reasonable set of defaults gets used (the exact default values are defined below, again, scroll down 🌈).

the following parameters are accepted:

  • LOG_DISPATCH_OPTIONS β€” lets you customize how the Log::Dispatch object gets created by giving a hash of options that get passed straight to the Log::Dispatch constructor. go read the docs for Log::Dispatch for the exact syntax of the parameters. and surprisingly enough, you will usually not need this option β€” instead go look at the LOG_DISPATCH_MODULES option. fr fr.
 LOG_DISPATCH_OPTIONS => {
      callbacks => sub { my %h = @_; return time().': '.$h{message}; },
 }
  • LOG_DISPATCH_MODULES β€” this option is where you say which Log::Dispatch::* modules you want to use to log messages. you can list multiple dispatch modules, each with its own little set of options. format the options as an array of hashes, where each hash holds the options for the Log::Dispatch:: module you're configuring, and also include a 'module' parameter with the name of the dispatch module. example is right below, don't worry. you can also add an 'append_newline' option to automatically append a newline to each log entry for that specific dispatch module (you don't need this option if you already specified the APPEND_NEWLINE option listed below, because that one adds a newline for all dispatch modules).
 LOG_DISPATCH_MODULES => [
   {         module => 'Log::Dispatch::File',
               name => 'messages',
           filename => '/tmp/messages.log',
          min_level => 'info',
     append_newline => 1
   },
   {         module => 'Log::Dispatch::Email::MailSend',
               name => 'email',
                 to => [ qw(foo@bar.com bar@baz.org ) ],
             subject => 'Oh No!!!!!!!!!!',
          min_level => 'emerg'
   }
 ]
  • APPEND_NEWLINE β€” by default Log::Dispatch does not append a newline to the end of log messages. set this option to a true value and a newline character gets automatically added to the end of the log message. simple as. ✨
 APPEND_NEWLINE => 1
  • LOG_METHOD_EXECUTION (EXPERIMENTAL) β€” this option lets you log the execution path of your program. set LOG_METHOD_EXECUTION to a list of all the modules you want logged. it will automatically send a debug message at the start and at the end of every method/function that gets called in the modules you listed. the parameters passed and the return value also get logged. this is useful for tracing program flow in the logfile without having to resort to the debugger. honestly kind of a vibe. πŸ”₯
 LOG_METHOD_EXECUTION => [qw(__PACKAGE__ CGI::Application CGI)],
WARNING: this hasn't been heavily tested, although it seems to work fine for me. also, a closure is created around the log object, so some care may need to be taken when using this in a persistent environment like mod_perl. this feature depends on the Sub::WrapPackages module.

🫢 DEFAULT OPTIONS 🫢

the following example shows what options get set by default (ie this is what you'd get if you do not call log_config). a single Log::Dispatch::Screen module that writes error messages to STDERR with a minimum log level of debug.

$self->log_config(
  LOG_DISPATCH_MODULES => [
    {        module => 'Log::Dispatch::Screen',
               name => 'screen',
             stderr => 1,
          min_level => 'debug',
     append_newline => 1
    }
  ],
);

here's a more customized example that uses two file appenders and an email gateway. here all debug messages get sent to /tmp/debug.log, and all messages above get sent to /tmp/messages.log. also, any emerg messages get emailed to foo@bar.com and bar@baz.org. πŸ’Œ

$self->log_config(
  LOG_DISPATCH_MODULES => [
    {    module => 'Log::Dispatch::File',
           name => 'debug',
       filename => '/tmp/debug.log',
      min_level => 'debug',
      max_level => 'debug'
    },
    {    module => 'Log::Dispatch::File',
           name => 'messages',
       filename => '/tmp/messages.log',
      min_level => 'info'
    },
    {    module => 'Log::Dispatch::Email::MailSend',
           name => 'email',
             to => [ qw(foo@bar.com bar@baz.org ) ],
         subject => 'Oh No!!!!!!!!!!',
      min_level => 'emerg'
    }
  ],
  APPEND_NEWLINE => 1,
);

🌈 EXAMPLE 🌈

in a CGI::Application module:

# configure the log modules once during the init stage
sub cgiapp_init {
  my $self = shift;

  # Configure the session
  $self->log_config(
    LOG_DISPATCH_MODULES => [
      {    module => 'Log::Dispatch::File',
             name => 'messages',
         filename => '/tmp/messages.log',
        min_level => 'error'
      },
      {    module => 'Log::Dispatch::Email::MailSend',
             name => 'email',
               to => [ qw(foo@bar.com bar@baz.org ) ],
           subject => 'Oh No!!!!!!!!!!',
        min_level => 'emerg'
      }
    ],
    APPEND_NEWLINE => 1,
  );

}

sub cgiapp_prerun {
  my $self = shift;

  $self->log->debug("Current runmode:  ".$self->get_current_runmode);
}

sub my_runmode {
  my $self = shift;
  my $log  = $self->log;

  if ($ENV{'REMOTE_USER'}) {
    $log->info("user ".$ENV{'REMOTE_USER'});
  }

  # etc...
}

look at that. logging in the init stage, logging in prerun, logging in the runmode. it's giving organized. it's giving main character energy. πŸ‘‘


πŸ’œ SINGLETON SUPPORT πŸ’œ

this module can be used as a singleton object. what that means: when the object gets created, it stays accessible for the duration of the process. super useful in persistent environments like mod_perl and PersistentPerl, because the object only has to be created one time and then stays in memory across multiple requests. it's also handy if you want to set up a DIE handler or a WARN handler, since you won't have access to the $self object in those. 🫢

to use this module as a singleton you need to provide all the configuration parameters as options to the use statement. the use statement accepts all the same parameters that the log_config method accepts, so go read the documentation above for more details.

when creating the singleton, the log object gets saved in the namespace of the module that created it. the singleton will also be inherited by any subclasses of this module. ✨

NOTE: singleton support requires the Class::ISA module, which is not installed automatically by this module. go install it yourself bestie, i believe in you πŸ’…

okay that's part 1!! there's more coming, don't go anywhere β€” the page keeps going and so do we πŸ¦‹βœ¨

✨ SINGLETON EXAMPLE ✨

okay so picture this: you want one logger for your whole app, set up once, and then every subclass just... uses it. no re-configuring, no drama. that's the singleton vibe and it's giving main character energy πŸ‘‘

package My::App;

use base qw(CGI::Application);
use CGI::Application::Plugin::LogDispatch(
    LOG_DISPATCH_MODULES => [
      {    module => 'Log::Dispatch::File',
             name => 'messages',
         filename => '/tmp/messages.log',
        min_level => 'error'
      },
    ],
    APPEND_NEWLINE => 1,
  );

}

sub cgiapp_prerun {
  my $self = shift;

  $self->log->debug("Current runmode:  ".$self->get_current_runmode);
}

sub my_runmode {
  my $self = shift;
  my $log  = $self->log;

  if ($ENV{'REMOTE_USER'}) {
    $log->info("user ".$ENV{'REMOTE_USER'});
  }

  # etc...
}

package My::App::Subclass;

use base qw(My::App);

# Setup a die handler that uses the logger
$SIG{__DIE__} = sub { My::App::Subclass->log->emerg(@_); CORE::die(@_); };

sub my_other_runmode {
  my $self = shift;

  $self->log->info("This will log to the logger configured in My::App");
}

see what's happening? bestie sets up the logger once in My::App, and then My::App::Subclass just inherits it and calls $self->log like it owns the place. which, honestly, it kind of does πŸ’…

and the $SIG{__DIE__} handler? that's the glow-up where even your fatal errors get logged with emerg before the real CORE::die(@_) does its thing. so nothing sneaks past you. not a single crumb πŸ“


πŸ’€ BUGS πŸ’€

found something broken? not working like it should? pls don't suffer in silence bestie, tell someone 😭

report any bugs or feature requests to bug-cgi-application-plugin-logdispatch@rt.cpan.org, or slide through the web interface at http://rt.cpan.org.

and here's the cute part: I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. so it's a whole little loop of accountability. we love to see it 🫢


🌈 SEE ALSO 🌈

go read these when you're done here, they're the extended universe:

  • CGI::Application
  • Log::Dispatch
  • Log::Dispatch::Screen
  • Sub::WrapPackages
  • perl(1)

πŸ‘‘ AUTHOR πŸ‘‘

Cees Hek <ceeshek@gmail.com> β€” the icon, the moment, the whole entire blueprint πŸ’œ


πŸŽ€ LICENSE πŸŽ€

Copyright (C) 2004 Cees Hek <ceeshek@gmail.com>

This library is free software. You can modify and or distribute it under the same terms as Perl itself.


aaaand that's the whole page bestie!! ✨ this was perl v5.34.0, dated 2022-06-09, and it lives under CGI::A...ispatch(3pm) (yes the name got squished, it's fine, she's still gorgeous πŸ’…). go forth and log everything πŸŒΈπŸ¦‹πŸ’–

πŸ“¦ where this page comes from