File Coverage

blib/lib/Log/AutoDump.pm
Criterion Covered Total %
statement 56 224 25.0
branch 0 90 0.0
condition 0 19 0.0
subroutine 19 47 40.4
pod 19 25 76.0
total 94 405 23.2


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