File Coverage

blib/lib/Catalyst/Plugin/Log/Dispatch.pm
Criterion Covered Total %
statement 39 192 20.3
branch 3 82 3.6
condition 0 29 0.0
subroutine 13 27 48.1
pod 0 1 0.0
total 55 331 16.6


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Log::Dispatch;
2              
3 1     1   45034 use warnings;
  1         2  
  1         37  
4 1     1   6 use strict;
  1         1  
  1         63  
5              
6             our $VERSION = '0.121';
7              
8             #use base 'Catalyst::Base';
9 1     1   6 use vars qw/$HasTimePiece $HasTimeHiRes/;
  1         7  
  1         124  
10 1     1   1053 use UNIVERSAL::require;
  1         8946  
  1         12  
11              
12             BEGIN {
13 1 50   1   131 Log::Dispatch::Config->use or warn "$@\nIt moves without using Log::Dispatch::Config.\n";
14 1 50       213 $HasTimeHiRes = 1 if( Time::HiRes->use(qw/tv_interval/) );
15 1 50       19 $HasTimePiece = 1 if( Time::Piece->use );
16             };
17             $Catalyst::Plugin::Log::Dispatch::CallerDepth = 0;
18              
19 1     1   2507 use IO::Handle;
  1         19182  
  1         1150  
20              
21              
22             # Module implementation here
23              
24             sub setup {
25 0 0   0 0   if( $Catalyst::VERSION >= 5.8 ) {
26 0 0         MRO::Compat->use or die "can not use MRO::Compat : $@\n";
27             }
28             else {
29 0 0         NEXT->use or die "can not use NEXT : $@\n";
30             }
31 0           my $c = shift;
32 0           my $old_log = undef;
33 0 0 0       if ( $c->log and ref( $c->log ) eq 'Catalyst::Log' ) {
34 0           $old_log = $c->log;
35             }
36 0           $c->log( Catalyst::Plugin::Log::Dispatch::Backend->new );
37            
38             #Make it an array with one element if its a hashref
39 0 0         if (ref ( $c->config->{'Log::Dispatch'} ) eq 'HASH') {
40 0           $c->config->{'Log::Dispatch'} = [ $c->config->{'Log::Dispatch'} ];
41             }
42            
43 0 0         unless ( ref( $c->config->{'Log::Dispatch'} ) eq 'ARRAY' ) {
44 0           push(
45 0           @{ $c->config->{'Log::Dispatch'} },
46             { class => 'STDOUT',
47             name => 'default',
48             min_level => 'debug',
49             format => '[%p] %m%n'
50             }
51             );
52              
53             }
54 0           foreach my $tlogc ( @{ $c->config->{'Log::Dispatch'} } ) {
  0            
55 0           my %logc = %{$tlogc};
  0            
56 0 0 0       if ( $logc{'class'} eq 'STDOUT' or $logc{'class'} eq 'STDERR' ) {
57 0           my $io = IO::Handle->new;
58 0           $io->fdopen( fileno( $logc{'class'} ), 'w' );
59 0           $logc{'class'} = 'Handle';
60 0           $logc{'handle'} = $io;
61             }
62 0           my $class = sprintf( "Log::Dispatch::%s", $logc{'class'} );
63 0           delete $logc{'class'};
64 0 0         $logc{'callbacks'} = [$logc{'callbacks'}] if(ref($logc{'callbacks'}) eq 'CODE');
65            
66 0 0 0       if(exists $logc{'format'} and defined $Log::Dispatch::Config::CallerDepth ) {
67 0           my $callbacks = Log::Dispatch::Config->format_to_cb($logc{'format'},0);
68 0 0         if(defined $callbacks) {
69 0 0         $logc{'callbacks'} = [] unless($logc{'callbacks'});
70 0           push(@{$logc{'callbacks'}}, $callbacks);
  0            
71             }
72             }
73 0 0 0       if( exists $logc{'format_o'} and length( $logc{'format_o'} ) ) {
    0          
74 0           my $callbacks = Catalyst::Plugin::Log::Dispatch->_format_to_cb_o($logc{'format_o'},0);
75 0 0         if(defined $callbacks) {
76 0 0         $logc{'callbacks'} = [] unless($logc{'callbacks'});
77 0           push(@{$logc{'callbacks'}}, $callbacks);
  0            
78             }
79             }
80             elsif(!$logc{'callbacks'}) {
81 0     0     $logc{'callbacks'} = sub { my %p = @_; return "$p{message}\n"; };
  0            
  0            
82             }
83 0 0         $class->use or die "$@";
84 0           my $logb = $class->new(%logc);
85 0   0       $logb->{rtf} = $logc{real_time_flush} || 0;
86 0           $c->log->add( $logb );
87             }
88            
89 0 0 0       if ($old_log && defined __log_dispatch_get_body( $old_log ) ) {
90 0           my @old_logs;
91 0           foreach my $line ( split /\n/, __log_dispatch_get_body( $old_log ) ) {
92 0 0         if ( $line =~ /^\[(\w+)] (.+)$/ ) {
    0          
93 0           push( @old_logs, { level => $1, msg => [$2] } );
94             }
95             elsif( $line =~ /^\[(\w{3} \w{3}[ ]{1,2}\d{1,2}[ ]{1,2}\d{1,2}:\d{2}:\d{2} \d{4})\] \[catalyst\] \[(\w+)\] (.+)$/ ) {
96 0           push( @old_logs, { level => $2, msg => [$3] } );
97             }
98             else {
99 0           push( @{ $old_logs[-1]->{'msg'} }, $line );
  0            
100             }
101             }
102 0           foreach my $line (@old_logs) {
103 0           my $level = $line->{'level'};
104 0           $c->log->$level( join( "\n", @{ $line->{'msg'} } ) );
  0            
105             }
106             }
107 0 0         if( $Catalyst::VERSION >= 5.8 ) {
108 0           return $c->maybe::next::method( @_ );
109             }
110             else {
111 0           $c->NEXT::setup(@_);
112             }
113             }
114              
115              
116             sub __log_dispatch_get_body {
117 0     0     my $log = shift;
118 0 0         return $Catalyst::VERSION >= 5.8 ? $log->_body : $log->body;
119             }
120 1     1   2527 use Data::Dumper;
  1         25978  
  1         935  
121             # copy and paste from Log::Dispatch::Config
122             # please teach a cool method.
123             sub _format_to_cb_o {
124 0     0     my($class, $format, $stack) = @_;
125 0 0         return undef unless defined $format;
126            
127             # caller() called only when necessary
128 0           my $needs_caller = $format =~ /%[FLP]/;
129 0 0         if( $HasTimeHiRes ) {
130             return sub {
131 0     0     my %p = @_;
132 0           $p{p} = delete $p{level};
133 0           $p{m} = delete $p{message};
134 0           $p{n} = "\n";
135 0           $p{'%'} = '%';
136 0           $p{i} = $$;
137 0 0         if ($needs_caller) {
138 0           my $depth = 0;
139 0           $depth++ while caller($depth) =~ /^Catalyst::Plugin::Log::Dispatch/;
140 0           $depth += $Catalyst::Plugin::Log::Dispatch::CallerDepth;
141 0           @p{qw(P F L)} = caller($depth);
142             }
143            
144 0           my ($t,$ms) = Time::HiRes::gettimeofday();
145 0           $ms = sprintf('%06d', $ms);
146 0           my $log = $format;
147 0           $log =~ s{
148             (%d(?:{(.*?)})?)| # $1: datetime $2: datetime fmt
149             (%MS)| # $3: milli second
150             (?:%([%pmFLPni])) # $4: others
151             }{
152 0 0 0       if ($1 && $2) {
    0          
    0          
    0          
153 0           _strftime_o($2,$t);
154             }
155             elsif ($1) {
156 0           scalar localtime;
157             }
158             elsif ($3) {
159 0           $ms;
160             }
161             elsif ($4) {
162 0           $p{$4};
163             }
164             }egx;
165 0           return $log;
166 0           };
167             }
168             else {
169             return sub {
170 0     0     my %p = @_;
171 0           $p{p} = delete $p{level};
172 0           $p{m} = delete $p{message};
173 0           $p{n} = "\n";
174 0           $p{'%'} = '%';
175 0           $p{i} = $$;
176 0 0         if ($needs_caller) {
177 0           my $depth = 0;
178 0           $depth++ while caller($depth) =~ /^Catalyst::Plugin::Log::Dispatch/;
179 0           $depth += $Catalyst::Plugin::Log::Dispatch::CallerDepth;
180 0           @p{qw(P F L)} = caller($depth);
181             }
182            
183 0           my $log = $format;
184 0           $log =~ s{
185             (%d(?:{(.*?)})?)| # $1: datetime $2: datetime fmt
186             (?:%([%pmFLPn])) # $3: others
187             }{
188 0 0 0       if ($1 && $2) {
    0          
    0          
189 0           _strftime_o($2);
190             }
191             elsif ($1) {
192 0           scalar localtime;
193             }
194             elsif ($3) {
195 0           $p{$3};
196             }
197             }egx;
198 0           return $log;
199 0           };
200             }
201             }
202              
203             sub _strftime_o {
204 0     0     my $fmt = shift;
205 0   0       my $time = shift || time;
206 0 0         if ($HasTimePiece) {
207 0           return Time::Piece->new($time)->strftime($fmt);
208             } else {
209 0           require POSIX;
210 0           return POSIX::strftime($fmt, localtime($time));
211             }
212             }
213              
214              
215             1;
216              
217             package Catalyst::Plugin::Log::Dispatch::Backend;
218              
219 1     1   10 use strict;
  1         2  
  1         50  
220              
221 1     1   5 use base qw/Log::Dispatch Class::Accessor::Fast/;
  1         2  
  1         2594  
222              
223 1     1   39482 use Time::HiRes qw/gettimeofday/;
  1         4  
  1         11  
224 1     1   10524 use Data::Dump;
  1         8217  
  1         111  
225 1     1   11 use Data::Dumper;
  1         2  
  1         80  
226              
227             {
228             foreach my $l (qw/debug info warn error fatal/) {
229             my $name = $l;
230             $name = 'warning' if ( $name eq 'warn' );
231             $name = 'critical' if ( $name eq 'fatal' );
232              
233 1     1   5 no strict 'refs';
  1         1  
  1         679  
234             *{"is_${l}"} = sub {
235 0     0     my $self = shift;
236 0           return $self->level_is_valid($name);
237             };
238              
239             *{"$l"} = sub {
240 0     0     my $self = shift;
241 0           my %p = (level => $name,
242             message => "@_");
243 0           local $Log::Dispatch::Config::CallerDepth += 1;
244 0           local $Catalyst::Plugin::Log::Dispatch::CallerDepth += 3;
245 0 0         if( keys( %{ $self->{outputs} } ) ) {
  0            
246 0           foreach (keys %{ $self->{outputs} }) {
  0            
247 0           my %h = %p;
248 0           $h{name} = $_;
249 0 0         if( $self->{outputs}->{$_}->{rtf} ) {
250 0           $self->{outputs}->{$_}->log(%h);
251             }
252             else {
253 0 0         $h{message} = $self->{outputs}->{$_}->_apply_callbacks(%h)
254             if($self->{outputs}->{$_}->{callbacks});
255 0           push(@{$self->_body}, \%h);
  0            
256             }
257             }
258             }
259             else {
260 0           push(@{$self->_body}, \%p);
  0            
261             }
262             };
263             }
264             }
265              
266             sub new {
267 0     0     my $pkg = shift;
268 0           my $this = $pkg->SUPER::new(@_);
269 0           $this->mk_accessors(qw/abort _body/);
270 0           $this->_body([]);
271 0           return $this;
272             }
273              
274              
275             sub dumper {
276 0     0     my $self = shift;
277 0           return $self->debug( Data::Dumper::Dumper(@_) );
278             }
279              
280             sub _dump {
281 0     0     my $self = shift;
282 0           return $self->debug( Data::Dump::dump(@_) );
283             }
284              
285             sub level_is_valid {
286 0     0     my $self = shift;
287 0 0         return 0 if ( $self->abort );
288 0           return $self->SUPER::level_is_valid(@_);
289             }
290              
291             sub _flush {
292 0     0     my $self = shift;
293 0 0 0       if ( $self->abort || !(scalar @{$self->_body})) {
  0            
294 0           $self->abort(undef);
295             }
296             else {
297 0           foreach my $p (@{$self->_body}) {
  0            
298 0           local $self->{outputs}->{$p->{name}}->{callbacks} = undef;
299 0           $self->{outputs}->{$p->{name}}->log(%{$p});
  0            
300             }
301             }
302 0           $self->_body([]);
303             }
304              
305              
306             1; # Magic true value required at end of module
307             __END__
308              
309              
310             =head1 NAME
311              
312             Catalyst::Plugin::Log::Dispatch - Log module of Catalyst that uses Log::Dispatch
313              
314              
315             =head1 VERSION
316              
317             This document describes Catalyst::Plugin::Log::Dispatch version 2.15
318              
319              
320             =head1 SYNOPSIS
321              
322             package MyApp;
323              
324             use Catalyst qw/Log::Dispatch/;
325              
326             configuration in source code
327              
328             MyApp->config->{ Log::Dispatch } = [
329             {
330             class => 'File',
331             name => 'file',
332             min_level => 'debug',
333             filename => MyApp->path_to('debug.log'),
334             format => '[%p] %m %n',
335             }];
336              
337             in myapp.yml
338              
339             Log::Dispatch:
340             - class: File
341             name: file
342             min_level: debug
343             filename: __path_to(debug.log)__
344             mode: append
345             format: '[%p] %m %n'
346              
347             If you use L<Catalyst::Plugin::ConfigLoader>,
348             please load this module after L<Catalyst::Plugin::ConfigLoader>.
349              
350             =head1 DESCRIPTION
351              
352             Catalyst::Plugin::Log::Dispatch is a plugin to use Log::Dispatch from Catalyst.
353              
354             =head1 CONFIGURATION
355              
356             It is same as the configuration of Log::Dispatch excluding "class" and "format".
357              
358             =over
359              
360             =item class
361              
362             The class name to Log::Dispatch::* object.
363             Please specify the name just after "Log::Dispatch::" of the class name.
364              
365             =item format
366              
367             It is the same as the format option of Log::Dispatch::Config.
368              
369             =back
370              
371             =head1 DEPENDENCIES
372              
373             L<Catalyst>, L<Log::Dispatch>, L<Log::Dispatch::Config>
374              
375             =head1 AUTHOR
376              
377             Shota Takayama C<< <shot[at]bindstorm.jp> >>
378              
379              
380             =head1 LICENCE AND COPYRIGHT
381              
382             Copyright (c) 2006, Shota Takayama C<< <shot[at]bindstorm.jp> >>. All rights reserved.
383              
384             This module is free software; you can redistribute it and/or
385             modify it under the same terms as Perl itself. See L<perlartistic>.
386              
387             =cut
388