CGI-Application-Plugin-LogDispatch

 view release on metacpan or  search on metacpan

lib/CGI/Application/Plugin/LogDispatch.pm  view on Meta::CPAN

    no strict 'refs';
    foreach my $sym (@EXPORT) {
        *{"${callpkg}::$sym"} = \&{$sym};
    }
    $callpkg->log_config(@_) if @_;
}

sub log {
    my $self = shift;

    my ($log, $options, $frompkg) = _get_object_or_options($self);

    if (!$log) {
        # define the config hash if it doesn't exist to save some checks later
        $options = {} unless $options;

        # create Log::Dispatch object
        if ($options->{LOG_DISPATCH_OPTIONS}) {
            # use the parameters the user supplied
            $log = Log::Dispatch->new( %{ $options->{LOG_DISPATCH_OPTIONS} } );
        } else {
            $log = Log::Dispatch->new( );
        }

        if ($options->{LOG_DISPATCH_MODULES}) {
            foreach my $logger (@{ $options->{LOG_DISPATCH_MODULES} }) {
                if (!$logger->{module}) {
                    # no logger module provided
                    #  not fatal... just skip this logger
                    warn "No 'module' name provided -- skipping this logger";
                } elsif (!$logger->{module}->require) {
                    # Couldn't load the logger module
                    #  not fatal... just skip this logger
                    warn $UNIVERSAL::require::ERROR;
                } else {
                    my $module = delete $logger->{module};
                    # setup a callback to append a newline if requested
                    if ($logger->{append_newline} || $options->{APPEND_NEWLINE}) {
                        delete $logger->{append_newline} if exists $logger->{append_newline};
                        $logger->{callbacks} = [ $logger->{callbacks} ]
                            if $logger->{callbacks} &&  ref $logger->{callbacks} ne 'ARRAY';
                        push @{ $logger->{callbacks} }, \&_append_newline;
                    }
                    # add the logger to the dispatcher
                    $log->add( $module->new( %$logger ) );
                }
            }
        } else {
            # create a simple STDERR logger
            my %options = (
                                name => 'screen',
                              stderr => 1,
                           min_level => 'debug',
            );
            $options{callbacks} = \&_append_newline if $options->{APPEND_NEWLINE};
            $log->add( Log::Dispatch::Screen->new( %options ) );
        }
        _set_object($frompkg||$self, $log);

        # CAP::DevPopup support
        if (UNIVERSAL::can($self, 'devpopup')) {
            # Register our report with DevPopup
            $self->add_callback( 'devpopup_report', \&_devpopup_report );

            # Create logger to capture all log entries
            my %options = (
                'name'      => 'DevPopup',
                'min_level' => 'debug',
                'filename'  => File::Spec->devnull(),
                'callbacks' => sub {
                    my %args = @_;
                    push( @{$self->{LOG_DISPATCH_DEVPOPUP_HISTORY}}, [$args{level}, $args{message}] );
                    },
                );
            $log->add( Log::Dispatch::File->new(%options) );
        }
    }

    return $log;
}

sub log_config {
    my $self = shift;
    my $class = ref $self ? ref $self : $self;

    my $log_config;
    if (ref $self) {
        die "Calling log_config after the log object has already been created" if @_ && defined $self->{__LOG_OBJECT};
        $log_config = $self->{__LOG_CONFIG} ||= {};
    } else {
        no strict 'refs';
        die "Calling log_config after the log object has already been created" if @_ && defined ${$class.'::__LOG_OBJECT'};
        ${$class.'::__LOG_CONFIG'} ||= {};
        $log_config = ${$class.'::__LOG_CONFIG'};
    }

    if (@_) {
        my $props;
        if (ref($_[0]) eq 'HASH') {
            my $rthash = %{$_[0]};
            $props = CGI::Application->_cap_hash($_[0]);
        } else {
            $props = CGI::Application->_cap_hash({ @_ });
        }
        my %options;
        # Check for LOG_OPTIONS
        if ($props->{LOG_DISPATCH_OPTIONS}) {
            die "log_config error:  parameter LOG_DISPATCH_OPTIONS is not a hash reference"
                if ref $props->{LOG_DISPATCH_OPTIONS} ne 'HASH';
            $log_config->{LOG_DISPATCH_OPTIONS} = delete $props->{LOG_DISPATCH_OPTIONS};
        }

        # Check for LOG_DISPATCH_MODULES
        if ($props->{LOG_DISPATCH_MODULES}) {
            die "log_config error:  parameter LOG_DISPATCH_MODULES is not an array reference"
                if ref $props->{LOG_DISPATCH_MODULES} ne 'ARRAY';
            $log_config->{LOG_DISPATCH_MODULES} = delete $props->{LOG_DISPATCH_MODULES};
        }

        # Check for APPEND_NEWLINE
        if ($props->{APPEND_NEWLINE}) {
            $log_config->{APPEND_NEWLINE} = 1;
            delete $props->{APPEND_NEWLINE};

lib/CGI/Application/Plugin/LogDispatch.pm  view on Meta::CPAN

                            pre      => sub {
                              $log->debug("calling $_[0](".join(', ', @_[1..$#_]).")");
                            },
                            post     => sub {
                              no warnings qw(uninitialized);
                              $log->debug("returning from $_[0] (".join(', ', @_[1..$#_]).")");
                            }
    );
    1;
  } or do {
    $log->error("Failed to load and configure Sub::WrapPackages:  $@");
  };
}

sub _append_newline {
  my %hash = @_;
  chomp $hash{message};
  return $hash{message}.$/;
}


##
## Private methods
##
sub _set_object {
    my $self = shift;
    my $log  = shift;
    my $class = ref $self ? ref $self : $self;

    if (ref $self) {
        $self->{__LOG_OBJECT} = $log;
    } else {
        no strict 'refs';
        ${$class.'::__LOG_OBJECT'} = $log;
    }
}

sub _get_object_or_options {
    my $self = shift;
    my $class = ref $self ? ref $self : $self;

    # Handle the simple case by looking in the object first
    if (ref $self) {
        return ($self->{__LOG_OBJECT}, undef) if $self->{__LOG_OBJECT};
        return (undef, $self->{__LOG_CONFIG}) if $self->{__LOG_CONFIG};
    }

    # See if we can find them in the class hierarchy
    #  We look at each of the modules in the @ISA tree, and
    #  their parents as well until we find either a log
    #  object or a set of configuration parameters
    require Class::ISA;
    foreach my $super ($class, Class::ISA::super_path($class)) {
        no strict 'refs';
        return (${$super.'::__LOG_OBJECT'}, undef) if ${$super.'::__LOG_OBJECT'};
        return (undef, ${$super.'::__LOG_CONFIG'}, $super) if ${$super.'::__LOG_CONFIG'};
    }
    return;
}

sub _devpopup_report {
    my $self = shift;
    my $r=0;
    my $history = join $/, map {
                    $r=1-$r;
                    qq(<tr class="@{[$r?'odd':'even']}"><td valign="top">$_->[0]</td><td>$_->[1]</td></tr>)
                    }
                    @{$self->{LOG_DISPATCH_DEVPOPUP_HISTORY}};
    $self->devpopup->add_report(
        title   => 'Log Entries',
        summary => 'All entries logged via Log::Dispatch',
        report  => qq(
            <style type="text/css">
              tr.even{background-color:#eee}
            </style>
            <div style="font-size: 80%">
              <table>
                <thead><tr><th>Level</th><th>Message</th></tr></thead>
                <tbody>$history</tbody>
              </table>
            </div>
            ),
        );
}

1;
__END__

=head1 NAME

CGI::Application::Plugin::LogDispatch - Add Log::Dispatch support to CGI::Application


=head1 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');
 }


=head1 DESCRIPTION

CGI::Application::Plugin::LogDispatch adds logging support to your L<CGI::Application>
modules by providing a L<Log::Dispatch> dispatcher object that is accessible from
anywhere in the application.

If you have L<CGI::Application::Plugin::DevPopup> installed, a "Log Entries"
report is added to the popup window, containing all of the entries that were
logged during the execution of the runmode.  

=head1 METHODS

=head2 log

This method will return the current L<Log::Dispatch> dispatcher object.  The L<Log::Dispatch>
object is created on the first call to this method, and any subsequent calls will return the
same object.  This effectively creates a singleton log dispatcher for the duration of the request.
If C<log_config> has not been called before the first call to C<log>, then it will choose some
sane defaults to create the dispatcher object (the exact default values are defined below).

  # 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');


=head2 log_config

This method can be used to customize the functionality of the CGI::Application::Plugin::LogDispatch module.
Calling this method does not mean that a new L<Log::Dispatch> object will be immediately created.
The log object will not be created until the first call to $self->log.

The recommended place to call C<log_config> is in the C<cgiapp_init>
stage of L<CGI::Application>.  If this method is called after the log object
has already been accessed, then it will die with an error message.

If this method is not called at all then a reasonable set of defaults
will be used (the exact default values are defined below).

The following parameters are accepted:

=over 4

=item LOG_DISPATCH_OPTIONS

This allows you to customize how the L<Log::Dispatch> object is created by providing a hash of
options that will be passed to the L<Log::Dispatch> constructor.  Please see the documentation
for L<Log::Dispatch> for the exact syntax of the parameters.  Surprisingly enough you will usually
not need to use this option, instead look at the LOG_DISPATCH_MODULES option.

 LOG_DISPATCH_OPTIONS => {
      callbacks => sub { my %h = @_; return time().': '.$h{message}; },
 }

=item LOG_DISPATCH_MODULES

This option allows you to specify the Log::Dispatch::* modules that you wish to use to
log messages.  You can list multiple dispatch modules, each with their own set of options.  Format



( run in 1.253 second using v1.01-cache-2.11-cpan-364913b4093 )