File Coverage

blib/lib/Log/AutoDump.pm
Criterion Covered Total %
statement 53 217 24.4
branch 0 86 0.0
condition 0 19 0.0
subroutine 18 45 40.0
pod 18 24 75.0
total 89 391 22.7


line stmt bran cond sub pod time code
1             package Log::AutoDump;
2              
3 1     1   68924 use 5.006;
  1         5  
4              
5 1     1   5 use strict;
  1         1  
  1         19  
6 1     1   4 use warnings;
  1         2  
  1         23  
7              
8 1     1   664 use Data::Dumper;
  1         7134  
  1         68  
9 1     1   489 use IO::File;
  1         8913  
  1         125  
10              
11 1     1   9 use constant FATAL => 0;
  1         2  
  1         61  
12 1     1   6 use constant ERROR => 1;
  1         2  
  1         45  
13 1     1   5 use constant WARN => 2;
  1         2  
  1         53  
14 1     1   7 use constant INFO => 3;
  1         2  
  1         66  
15 1     1   23 use constant DEBUG => 4;
  1         2  
  1         43  
16 1     1   5 use constant TRACE => 5;
  1         2  
  1         72  
17              
18             my %LEVELS = ( 0 => 'FATAL', 1 => 'ERROR', 2 => 'WARN', 3 => 'INFO', 4 => 'DEBUG', 5 => 'TRACE' );
19              
20 1     1   6 use constant DEFAULT_LEVEL => 5;
  1         2  
  1         59  
21 1     1   5 use constant DEFAULT_SORT_KEYS => 1;
  1         2  
  1         58  
22 1     1   6 use constant DEFAULT_QUOTE_KEYS => 0;
  1         2  
  1         72  
23 1     1   5 use constant DEFAULT_DEEP_COPY => 1;
  1         2  
  1         51  
24              
25 1     1   6 use constant DEFAULT_BASE_DIR => '/tmp';
  1         1  
  1         49  
26 1     1   13 use constant MAX_FRAME => 10;
  1         2  
  1         41  
27              
28 1     1   5 use constant DEFAULT_HISTORY_LENGTH => 0;
  1         2  
  1         2565  
29              
30             =head1 NAME
31              
32             Log::AutoDump - Log with automatic dumping of references and objects.
33              
34             =head1 VERSION
35              
36             Version 0.20
37              
38             =cut
39              
40             our $VERSION = '0.21';
41              
42             $VERSION = eval $VERSION;
43              
44             =head1 SYNOPSIS
45              
46             Logging as usual, but with automatic dumping of references and objects.
47              
48             use Log::AutoDump;
49              
50             my $log = Log::AutoDump->new;
51            
52             $log->msg( 4, "Logging at level 4 (debug)", $ref, $hashref );
53              
54             $log->warn( "Logging at warn level (2)", \@somelist, "Did you see that list?!" )
55            
56             =cut
57              
58             =head1 DESCRIPTION
59              
60             When logging in development, it is common to dump a reference or object.
61              
62             When working with logging systems that employ the idea of "log-levels", you can quickly end up with expensive code.
63              
64             For example...
65              
66             $log->warn( "Some object:", Dumper( $obj ), "Did you like that?" );
67              
68             If the B for the C<$log> object is set lower than B, the above log statement will never make it to any log file, or database.
69              
70             Unfortunately, you have still C an entire data-structure, just in case.
71              
72             We take the dumping process out of your hands.
73              
74             The above statement becomes...
75              
76             $log->warn( "Some object:", $obj, "Did you like that?" );
77              
78             Which is easier to read/write for a start, but will also B the C by default.
79              
80             Using L unless specified.
81              
82             You can control the C<$Data::Dumper::Maxdepth> by setting the C attribute at construction time, and/or change it later.
83              
84             my $log = Log::AutoDump->new( dump_depth => 3 );
85            
86             $log->dump_depth( 1 );
87              
88             This is useful when dealing with some references or objects that may contain things like L objects, which are themselves huge.
89              
90             =cut
91              
92              
93             =head1 METHODS
94              
95             =head2 Class Methods
96              
97             =head3 new
98              
99             Creates a new logger object.
100              
101             my $log = Log::AutoDump->new(
102             level => 3,
103             dumps => 1,
104             dump_depth => 2,
105             sort_keys => 1,
106             quote_keys => 0,
107             deep_copy => 1,
108             filename_datestamp => 1,
109             );
110              
111             =cut
112              
113             sub new
114             {
115 0     0 1   my ( $class, %args ) = @_;
116              
117 0           if ( 1 ) # possibly use db backend later
118             {
119 0   0       my $path = $ENV{LOG_AUTODUMP_BASE_DIR} || $args{ base_dir } || DEFAULT_BASE_DIR;
120              
121 0 0         $path .= '/' unless $path =~ m!/$!;
122              
123 0   0       my $filename = delete $args{filename} || $0;
124            
125 0           $filename =~ s/^\.//;
126            
127 0           $filename =~ s/[\s\/]/-/g;
128              
129 0           $filename =~ s/^-//;
130              
131 0 0 0       if ( $args{ filename_datestamp } || $args{ datestamp_filename } ) # datestamp_filename can be removed after May 2012
132             {
133 0           my ( undef, undef, undef, $day, $mon, $year, undef, undef, undef ) = localtime( time );
134              
135 0           $mon++;
136 0           $mon =~ s/^(\d)$/0$1/;
137 0           $day =~ s/^(\d)$/0$1/;
138            
139 0           my $datestamp = ( $year + 1900 ) . $mon . $day;
140            
141 0           $filename = $datestamp . '-' . $filename;
142             }
143            
144 0           $args{ filename } = $path . $filename;
145             }
146            
147             my $self = {
148             level => exists $args{ level } ? $args{ level } : DEFAULT_LEVEL,
149             history_length => exists $args{ history_length } ? $args{ history_length } : DEFAULT_HISTORY_LENGTH,
150             history => [],
151             dumps => exists $args{ dumps } ? $args{ dumps } : 1,
152             dump_depth => $args{ dump_depth } || 0,
153             sort_keys => exists $args{ sort_keys } ? $args{ sort_keys } : DEFAULT_SORT_KEYS,
154             quote_keys => exists $args{ quote_keys } ? $args{ quote_keys } : DEFAULT_QUOTE_KEYS,
155             deep_copy => exists $args{ deep_copy } ? $args{ deep_copy } : DEFAULT_DEEP_COPY,
156             filename => $args{ filename },
157 0 0 0       autoflush => exists $args{ autoflush } ? $args{ autoflush } : 1,
    0          
    0          
    0          
    0          
    0          
    0          
158             _fh => undef,
159             };
160              
161 0           my $fh = IO::File->new( ">> " . $self->{filename} );
162              
163 0           $fh->binmode( ":utf8" );
164              
165 0           $fh->autoflush( 1 );
166              
167 0           $self->{ _fh } = $fh;
168              
169 0           bless( $self, $class );
170            
171 0           return $self;
172             }
173              
174             =head2 Instance Methods
175              
176             =head3 level
177              
178             Changes the log level for the current instance.
179              
180             $log->level( 3 );
181              
182             =cut
183              
184             sub level
185             {
186 0     0 1   my ( $self, $arg ) = @_;
187 0 0         $self->{ level } = $arg if defined $arg;
188 0           return $self->{ level };
189             }
190              
191             =head3 history_length
192              
193             Controls how many historical log events to remember.
194              
195             This is the number of events, not number of statments or dumps.
196              
197             If a debug statement includes 3 messages/objects, all 3 are stored in one unit of length.
198              
199             $log->history_length( 10 );
200              
201             =cut
202              
203             sub history_length
204             {
205 0     0 1   my ( $self, $arg ) = @_;
206 0 0         $self->{ history_length } = $arg if defined $arg;
207 0           return $self->{ history_length };
208             }
209              
210             =head3 history
211              
212             The list of historical statements/objects.
213              
214             Each point in the history is an arrayref of statements/objects.
215              
216             This is only a getter, the history is accumulated internally.
217              
218             $log->history;
219              
220             =cut
221              
222             sub history
223             {
224 0     0 1   my $self = shift;
225              
226 0           return $self->{ history };
227             }
228              
229             =head3 dumps
230              
231             Controls whether references and objects are dumped or not.
232              
233             $log->dumps( 1 );
234              
235             =cut
236              
237             sub dumps
238             {
239 0     0 1   my ( $self, $arg ) = @_;
240 0 0         $self->{ dumps } = $arg if defined $arg;
241 0           return $self->{ dumps };
242             }
243              
244             =head3 dump_depth
245              
246             Sets C<$Data::Dumper::Maxdepth>.
247              
248             $log->dump_depth( 3 );
249              
250             =cut
251              
252             sub dump_depth
253             {
254 0     0 1   my ( $self, $arg ) = @_;
255 0 0         $self->{ dump_depth } = $arg if defined $arg;
256 0           return $self->{ dump_depth };
257             }
258              
259             =head3 sort_keys
260              
261             Sets C<$Data::Dumper::Sortkeys>.
262              
263             $log->sort_keys( 0 );
264              
265             =cut
266              
267             sub sort_keys
268             {
269 0     0 1   my ( $self, $arg ) = @_;
270 0 0         $self->{ sort_keys } = $arg if defined $arg;
271 0           return $self->{ sort_keys };
272             }
273              
274             =head3 quote_keys
275              
276             Sets C<$Data::Dumper::Quotekeys>.
277              
278             $log->quote_keys( 0 );
279              
280             =cut
281              
282             sub quote_keys
283             {
284 0     0 1   my ( $self, $arg ) = @_;
285 0 0         $self->{ quote_keys } = $arg if defined $arg;
286 0           return $self->{ quote_keys };
287             }
288              
289             =head3 deep_copy
290              
291             Sets C<$Data::Dumper::Deepcopy>.
292              
293             $log->deep_copy( 0 );
294              
295             =cut
296              
297             sub deep_copy
298             {
299 0     0 1   my ( $self, $arg ) = @_;
300 0 0         $self->{ deep_copy } = $arg if defined $arg;
301 0           return $self->{ deep_copy };
302             }
303              
304              
305              
306             =head3 filename
307              
308             Set the filename.
309              
310             $log->filename( 'foo.log' );
311              
312             =cut
313              
314             sub filename
315             {
316 0     0 1   my ( $self, $arg ) = @_;
317 0 0         $self->{ filename } = $arg if defined $arg;
318 0           return $self->{ filename };
319             }
320              
321             sub _fh
322             {
323 0     0     my ( $self, $arg ) = @_;
324 0 0         $self->{ _fh } = $arg if defined $arg;
325 0           return $self->{ _fh };
326             }
327              
328             =head3 autoflush
329              
330             Set the autoflush on the filehandle.
331              
332             $log->autoflush( 1 );
333              
334             =cut
335              
336             sub autoflush
337             {
338 0     0 1   my ( $self, $autoflush ) = @_;
339              
340 0 0         if ( defined $autoflush )
341             {
342 0           $self->{ autoflush } = $autoflush;
343              
344 0           $self->{ _fh }->autoflush( $self->{ autoflush } );
345             }
346              
347 0           return $self->{ autoflush };
348             }
349              
350             =head3 msg
351              
352             $log->msg(2, "Hello");
353              
354             This method expects a log level as the first argument, followed by a list of log messages/references/objects.
355              
356             This is the core method called by the following (preferred) methods, using the below mapping...
357              
358             TRACE => 5
359             DEBUG => 4
360             INFO => 3
361             WARN => 2
362             ERROR => 1
363             FATAL => 0
364              
365             =cut
366              
367             sub msg
368             {
369 0     0 1   my ( $self, $level, @things ) = @_;
370              
371 0           local $Data::Dumper::Maxdepth = $self->dump_depth;
372            
373 0           local $Data::Dumper::Sortkeys = $self->sort_keys;
374              
375 0           local $Data::Dumper::Quotekeys = $self->quote_keys;
376            
377 0           local $Data::Dumper::Deepcopy = $self->deep_copy;
378              
379 0 0         if ( $level !~ /^\d+$/ )
380             {
381             # bad log level, so push the 'level' to the 'things'
382 0           $self->msg( FATAL, "LOG LEVEL MISSING (on the next line)" );
383 0           unshift( @things, $level );
384 0           $level = FATAL;
385             }
386              
387 0 0         return $self if $level > $self->level;
388              
389 0           my $package = '-';
390 0           my $sub = '-';
391 0           my $line = 0;
392              
393 0           ( $package, undef, $line ) = caller( 1 );
394              
395 0 0         if ( caller( 2 ) )
396             {
397 0           ( undef, undef, undef, $sub ) = caller( 2 );
398             }
399              
400 0           $sub =~ s/^.*::(.*?)$/$1/;
401            
402             ###################
403             # prefix the line #
404             ###################
405            
406 0           my ( $sec, $min, $hour, $day, $mon, $year, undef, undef, undef ) = localtime( time );
407              
408 0           $mon++;
409 0           $mon =~ s/^(\d)$/0$1/;
410 0           $day =~ s/^(\d)$/0$1/;
411 0           $hour =~ s/^(\d)$/0$1/;
412 0           $min =~ s/^(\d)$/0$1/;
413 0           $sec =~ s/^(\d)$/0$1/;
414            
415 0           my $datetime = ( $year + 1900 ) . '/' . $mon . '/' . $day . ' ' . $hour . ':' . $min . ':' . $sec;
416            
417 0           my $prefix = join( ' ', $datetime, $$, $LEVELS{ $level }, $package, $sub, '(' . $line . ')' ) . ' - ';
418              
419 0           my $msg = '';
420              
421 0           foreach my $thing ( @things )
422             {
423 0 0         if ( my $label = ref $thing )
424             {
425 0 0 0       if ( $self->dumps || $level == 0 )
426             {
427 0 0         $Data::Dumper::Maxdepth = 9 if $level == 0;
428              
429 0           my $dumped = Data::Dumper->Dump( [ $thing ], [ $label ] );
430              
431 0           my @lines = split( "\n", $dumped );
432              
433 0           $msg .= join( "\n", map { $prefix . $_ } @lines );
  0            
434              
435 0           $Data::Dumper::Maxdepth = $self->dump_depth;
436             }
437             else
438             {
439 0           $msg .= $prefix . "NOT DUMPING [ " . $label . " ]";
440             }
441             }
442             else
443             {
444 0 0         if ( defined $thing )
445             {
446 0           $msg .= $prefix . $thing;
447             }
448             else
449             {
450 0           $msg .= $prefix . '<< UNDEFINED LOG STATEMENT >>';
451             }
452             }
453            
454 0 0         $msg .= "\n" if $msg !~ /\n$/;
455             }
456              
457             # we have to make a local copy of the fh for some reason :-/
458              
459 0           my $fh = $self->_fh;
460              
461 0           print $fh $msg;
462              
463 0           return $self;
464             }
465              
466             sub _flush
467             {
468 0     0     my $self = shift;
469              
470 0           my $fh = $self->_fh;
471              
472 0           $fh->flush;
473              
474 0           return $self;
475             }
476              
477             sub _add_to_history
478             {
479 0     0     my ( $self, @things ) = @_;
480              
481 0 0         if ( scalar @{ $self->{ history } } >= $self->{ history_length } )
  0            
482             {
483 0           shift @{ $self->{ history } };
  0            
484             }
485              
486 0 0         if ( scalar @{ $self->{ history } } < $self->{ history_length } )
  0            
487             {
488 0           push @{ $self->{ history } }, \@things;
  0            
489             }
490              
491 0           return $self;
492             }
493              
494             =head4 trace
495              
496             $log->trace( "Trace some info" );
497              
498             A C statement is generally used for extremely low level logging, calling methods, getting into methods, etc.
499              
500             =cut
501              
502             sub trace
503             {
504 0     0 1   my $self = shift;
505 0 0         $self->msg( TRACE, @_ ) if $self->is_trace;
506 0           $self->_add_to_history( @_ );
507 0           return $self;
508             }
509              
510             sub is_trace
511             {
512 0     0 0   my $self = shift;
513 0 0         return 1 if $self->level >= TRACE;
514 0           return 0;
515             }
516              
517             =head4 debug
518              
519             $log->debug( "Debug some info" );
520              
521             =cut
522              
523             sub debug
524             {
525 0     0 1   my $self = shift;
526              
527 0           my @lines = @_;
528              
529 0           for ( my $i = 0; $i < scalar( @lines ); $i ++ )
530             {
531 0 0         next if ref $lines[ $i ];
532              
533 0 0 0       if ( $lines[ $i ] =~ /^#/ && $lines[ $i ] =~ /#$/ && $lines[ $i ] !~ /^#+$/ )
      0        
534             {
535 0           splice( @lines, $i, 0, '#' x length( $_[ 0 ] ) );
536 0           splice( @lines, $i + 2, 0, '#' x length( $_[ 0 ] ) );
537              
538 0           $i += 2;
539             }
540             }
541              
542 0 0         $self->msg( DEBUG, @lines ) if $self->is_debug;
543 0           $self->_add_to_history( @lines );
544              
545 0           return $self;
546             }
547              
548             sub is_debug
549             {
550 0     0 0   my $self = shift;
551 0 0         return 1 if $self->level >= DEBUG;
552 0           return 0;
553             }
554              
555              
556             =head4 info
557              
558             $log->info( "Info about something" );
559              
560             =cut
561              
562             sub info
563             {
564 0     0 1   my $self = shift;
565 0 0         $self->msg( INFO, @_ ) if $self->is_info;
566 0           $self->_add_to_history( @_ );
567 0           return $self;
568             }
569              
570             sub is_info
571             {
572 0     0 0   my $self = shift;
573 0 0         return 1 if $self->level >= INFO;
574 0           return 0;
575             }
576              
577             =head4 warn
578              
579             $log->warn( "Something not quite right here" );
580              
581             =cut
582              
583             sub warn
584             {
585 0     0 1   my $self = shift;
586 0 0         $self->msg( WARN, @_ ) if $self->is_warn;
587 0           $self->_add_to_history( @_ );
588 0           return $self;
589             }
590              
591             sub is_warn
592             {
593 0     0 0   my $self = shift;
594 0 0         return 1 if $self->level >= WARN;
595 0           return 0;
596             }
597              
598             =head4 error
599              
600             $log->error( "Something went wrong" );
601              
602             =cut
603              
604             sub error
605             {
606 0     0 1   my $self = shift;
607 0 0         $self->msg( ERROR, @_ ) if $self->is_error;
608 0           $self->_add_to_history( @_ );
609 0           return $self;
610             }
611              
612             sub is_error
613             {
614 0     0 0   my $self = shift;
615 0 0         return 1 if $self->level >= ERROR;
616 0           return 0;
617             }
618              
619             =head4 fatal
620              
621             $log->fatal( "Looks like we died" );
622              
623             =cut
624              
625             sub fatal
626             {
627 0     0 1   my $self = shift;
628 0 0         $self->msg( FATAL, @_ ) if $self->is_fatal;
629 0           $self->_add_to_history( @_ );
630 0           return $self;
631             }
632              
633             sub is_fatal
634             {
635 0     0 0   my $self = shift;
636 0 0         return 1 if $self->level >= FATAL;
637 0           return 0;
638             }
639              
640              
641             =head1 TODO
642              
643             simple scripts (the caller stack)
644              
645             extend to use variations of Data::Dumper
646              
647              
648              
649              
650             =head1 AUTHOR
651              
652             Rob Brown, C<< >>
653              
654             =head1 BUGS
655              
656             Please report any bugs or feature requests to C, or through
657             the web interface at L. I will be notified, and then you will
658             automatically be notified of progress on your bug as I make changes.
659              
660             =head1 SUPPORT
661              
662             You can find documentation for this module with the perldoc command.
663              
664             perldoc Log::AutoDump
665              
666              
667             You can also look for information at:
668              
669             =over 4
670              
671             =item * RT: CPAN's request tracker (report bugs here)
672              
673             L
674              
675             =item * AnnoCPAN: Annotated CPAN documentation
676              
677             L
678              
679             =item * CPAN Ratings
680              
681             L
682              
683             =item * Search CPAN
684              
685             L
686              
687             =back
688              
689             =head1 LICENSE AND COPYRIGHT
690              
691             Copyright 2012 Rob Brown.
692              
693             This program is free software; you can redistribute it and/or modify it
694             under the terms of either: the GNU General Public License as published
695             by the Free Software Foundation; or the Artistic License.
696              
697             See http://dev.perl.org/licenses/ for more information.
698              
699              
700             =cut
701              
702             1; # End of Log::AutoDump