File Coverage

blib/lib/Bio/Phylo/Util/Logger.pm
Criterion Covered Total %
statement 73 131 55.7
branch 23 64 35.9
condition 5 18 27.7
subroutine 16 26 61.5
pod 16 17 94.1
total 133 256 51.9


line stmt bran cond sub pod time code
1             package Bio::Phylo::Util::Logger;
2 57     57   379 use strict;
  57         114  
  57         1602  
3 57     57   275 use base 'Exporter';
  57         101  
  57         5248  
4 57     57   20886 use Term::ANSIColor;
  57         377279  
  57         4247  
5 57     57   462 use Bio::Phylo::Util::Exceptions 'throw';
  57         105  
  57         2360  
6 57     57   3291 use Bio::Phylo::Util::CONSTANT qw'/looks_like/';
  57         111  
  57         18229  
7              
8             our ( %VERBOSITY, $PREFIX, %STYLE );
9             our $STYLE = 'detailed';
10             our $COLORED = 1; # new default: we use colors
11             our $TRACEBACK = 0;
12             our @EXPORT_OK = qw(DEBUG INFO WARN ERROR FATAL VERBOSE);
13             our %EXPORT_TAGS = ( 'simple' => [@EXPORT_OK], 'levels' => [@EXPORT_OK] );
14             our %COLORS = (
15             'DEBUG' => 'blue',
16             'INFO' => 'green',
17             'WARN' => 'yellow',
18             'ERROR' => 'bold red',
19             'FATAL' => 'red',
20             );
21              
22             BEGIN {
23            
24             # compute the path to the root of Bio::Phylo,
25             # use that as the default prefix
26 57     57   219 my $package = __PACKAGE__;
27 57         105 my $file = __FILE__;
28 57         293 $package =~ s/::/\//g;
29 57         154 $package .= '.pm';
30 57         833 $file =~ s/\Q$package\E$//;
31 57         151 $PREFIX = $file;
32            
33             # set verbosity to 2, i.e. warn
34 57   50     452 $VERBOSITY{'*'} = $ENV{'BIO_PHYLO_VERBOSITY'} || 2;
35            
36             # define verbosity styles
37 57         73855 %STYLE = (
38             'simple' => '${level}: $message',
39             'detailed' => '$level $sub [$file $line] - $message',
40             );
41             }
42              
43             {
44             my %levels = ( FATAL => 0, ERROR => 1, WARN => 2, INFO => 3, DEBUG => 4 );
45             my @listeners = ( sub {
46             my ( $string, $level ) = @_;
47             if ( $COLORED and -t STDERR ) {
48             print STDERR colored( $string, $COLORS{$level} );
49             }
50             else {
51             print STDERR $string;
52             }
53             } ); # default
54              
55             # dummy constructor that dispatches to VERBOSE(),
56             # then returns the package name
57             sub new {
58 200     200 1 1509 my $class = shift;
59 200 50       1452 $class->VERBOSE(@_) if @_;
60 200         559 return $class;
61             }
62              
63             # set additional listeners
64             sub set_listeners {
65 2     2 1 20 my ( $class, @args ) = @_;
66 2         3 for my $arg (@args) {
67 2 100       8 if ( looks_like_instance $arg, 'CODE' ) {
68 1         5 push @listeners, $arg;
69             }
70             else {
71 1         6 throw 'BadArgs' => "$arg not a CODE reference";
72             }
73             }
74 1         5 return $class;
75             }
76            
77             # this is never called directly. rather, messages are dispatched here
78             # by the DEBUG() ... FATAL() subs below
79             sub LOG ($$) {
80 235631     235631 0 340650 my ( $message, $level ) = @_;
81            
82             # probe the call stack
83 235631         806040 my ( $pack2, $file2, $line2, $sub ) = caller( $TRACEBACK + 2 );
84 235631         674861 my ( $pack1, $file, $line, $sub1 ) = caller( $TRACEBACK + 1 );
85            
86             # cascade verbosity from global to local
87 235631         372867 my $verbosity = $VERBOSITY{'*'}; # global
88 235631 50       374847 $verbosity = $VERBOSITY{$pack1} if exists $VERBOSITY{$pack1}; # package
89 235631 50 33     570240 $verbosity = $VERBOSITY{$sub} if $sub and exists $VERBOSITY{$sub}; # sub
90            
91             # verbosity is higher than the current caller, proceed
92 235631 50       450728 if ( $verbosity >= $levels{$level} ) {
93              
94             # strip the prefix from the calling file's path
95 0 0       0 if ( index($file, $PREFIX) == 0 ) {
96 0         0 $file =~ s/^\Q$PREFIX\E//;
97             }
98            
99             # select one of the templates
100 0         0 my $string;
101 0         0 my $s = $STYLE{$STYLE};
102 0         0 $string = eval "qq[$s\n]";
103            
104             # dispatch to the listeners
105 0         0 $_->( $string, $level, $sub, $file, $line, $message ) for @listeners;
106             }
107             }
108            
109             # these subs both return their verbosity constants and, if
110             # provided with a message, dispatch the message to LOG()
111 0 0   0 1 0 sub FATAL (;$) { LOG $_[0], 'FATAL' if $_[0]; $levels{'FATAL'} }
  0         0  
112 0 0   0 1 0 sub ERROR (;$) { LOG $_[0], 'ERROR' if $_[0]; $levels{'ERROR'} }
  0         0  
113 4 50   4 1 13 sub WARN (;$) { LOG $_[0], 'WARN' if $_[0]; $levels{'WARN'} }
  4         5  
114 70999 50   70999 1 170026 sub INFO (;$) { LOG $_[0], 'INFO' if $_[0]; $levels{'INFO'} }
  70999         89478  
115 164638 100   164638 1 361634 sub DEBUG (;$) { LOG $_[0], 'DEBUG' if $_[0]; $levels{'DEBUG'} }
  164638         196203  
116              
117             sub PREFIX {
118 0     0 1 0 my ( $class, $prefix ) = @_;
119 0 0       0 $PREFIX = $prefix if $prefix;
120 0         0 return $PREFIX;
121             }
122              
123             sub VERBOSE {
124 14 100 66 14 1 116 shift if ref $_[0] or $_[0] eq __PACKAGE__;
125 14 50       53 if (@_) {
126 14         58 my %opt = looks_like_hash @_;
127 14         40 my $level = $opt{'-level'};
128            
129             # verbosity is specified
130 14 50       50 if ( defined $level ) {
131              
132             # check validity
133 14 50 25     109 if ( $level > 4 xor $level < 0 ) {
134 0         0 throw 'OutOfBounds' => "'-level' can be between 0 and 4, not $level";
135             }
136            
137             # verbosity is specified for one or more packages
138 14 50       72 if ( my $class = $opt{'-class'} ) {
    50          
139 0 0       0 if ( ref $class eq 'ARRAY' ) {
140 0         0 for my $c ( @{ $class } ) {
  0         0  
141 0         0 $VERBOSITY{$c} = $level;
142 0         0 INFO "Changed verbosity for class $c to $level";
143             }
144             }
145             else {
146 0         0 $VERBOSITY{$class} = $level;
147 0         0 INFO "Changed verbosity for class $class to $level";
148             }
149             }
150            
151             # verbosity is specified for one or more methods
152             elsif ( my $method = $opt{'-method'} ) {
153 0 0       0 if ( ref $method eq 'ARRAY' ) {
154 0         0 for my $m ( @{ $method } ) {
  0         0  
155 0         0 $VERBOSITY{$m} = $level;
156 0         0 INFO "Changed verbosity for method $m to $level";
157             }
158             }
159             else {
160 0         0 $VERBOSITY{$method} = $level;
161 0         0 INFO "Changed verbosity for method $method to $level";
162             }
163             }
164            
165             # verbosity is set globally
166             else {
167 14         41 $VERBOSITY{'*'} = $level;
168 14         68 INFO "Changed global verbosity to $VERBOSITY{'*'}";
169             }
170             }
171            
172             # log to a file
173 14 50       60 if ( $opt{'-file'} ) {
174 0 0       0 open my $fh, '>>', $opt{'-file'} or throw 'FileError' => $!;
175 0     0   0 __PACKAGE__->set_listeners(sub { print $fh shift });
  0         0  
176             }
177            
178             # log to a handle
179 14 50       75 if ( $opt{'-handle'} ) {
180 0         0 my $fh = $opt{'-handle'};
181 0     0   0 __PACKAGE__->set_listeners(sub { print $fh shift });
  0         0  
182             }
183            
184             # log to listeners
185 14 50       51 if ( $opt{'-listeners'} ) {
186 0         0 __PACKAGE__->set_listeners(@{$opt{'-listeners'}});
  0         0  
187             }
188            
189             # update the prefix
190 14 50       46 if ( $opt{'-prefix'} ) {
191 0         0 __PACKAGE__->PREFIX($opt{'-prefix'});
192             }
193            
194             # set logstyle
195 14 50       47 if ( $opt{'-style'} ) {
196 0         0 my $s = lc $opt{'-style'};
197 0 0       0 if ( exists $STYLE{$s} ) {
198 0         0 $STYLE = $s;
199             }
200             }
201            
202             # turn colors on/off. default is on.
203 14 50       60 $COLORED = !!$opt{'-colors'} if defined $opt{'-colors'};
204             }
205 14         42 return $VERBOSITY{'*'};
206             }
207            
208             # Change the terminal to a predefined color. For example to make sure that
209             # an entire exception (or part of it) is marked up as FATAL, or so that the
210             # output from an external command is marked up as DEBUG.
211             sub start_color {
212 0     0 1 0 my ( $self, $level, $handle ) = @_;
213 0 0       0 $handle = \*STDERR if not $handle;
214 0 0 0     0 if ( $COLORED and -t $handle ) {
215 0         0 print $handle color $COLORS{$level};
216             }
217 0         0 return $COLORS{$level};
218             }
219            
220             sub stop_color {
221 0     0 1 0 my ( $self, $handle ) = @_;
222 0 0       0 $handle = \*STDERR if not $handle;
223 0 0 0     0 if ( $COLORED and -t $handle ) {
224 0         0 print $handle color 'reset';
225             }
226 0         0 return $self;
227             }
228            
229             # aliases for singleton methods
230             sub fatal {
231 0     0 1 0 my $self = shift;
232 0         0 $TRACEBACK++;
233 0         0 FATAL shift;
234 0         0 $TRACEBACK--;
235             }
236             sub error {
237 0     0 1 0 my $self = shift;
238 0         0 $TRACEBACK++;
239 0         0 ERROR shift;
240 0         0 $TRACEBACK--;
241             }
242             sub warn {
243 4     4 1 11 my $self = shift;
244 4         6 $TRACEBACK++;
245 4         9 WARN shift;
246 4         8 $TRACEBACK--;
247             }
248             sub info {
249 68987     68987 1 93445 my $self = shift;
250 68987         81064 $TRACEBACK++;
251 68987         121545 INFO shift;
252 68987         104033 $TRACEBACK--;
253             }
254             sub debug {
255 164638     164638 1 215731 my $self = shift;
256 164638         180722 $TRACEBACK++;
257 164638         273057 DEBUG shift;
258 164638         247813 $TRACEBACK--;
259             }
260            
261             # empty destructor so we don't go up inheritance tree at the end
262       0     sub DESTROY {}
263             }
264             1;
265              
266             =head1 NAME
267              
268             Bio::Phylo::Util::Logger - Logger of internal messages of several severity
269             levels
270              
271             =head1 SYNOPSIS
272              
273             use strict;
274             use Bio::Phylo::Util::Logger ':levels'; # import level constants
275             use Bio::Phylo::IO 'parse';
276             use Bio::Phylo::Factory;
277            
278             # Set the verbosity level of the tree class.
279             # "DEBUG" is the most verbose level. All log messages
280             # emanating from the tree class will be
281             # transmitted. For this to work the level constants
282             # have to have been imported!
283             use Bio::Phylo::Forest::Tree 'verbose' => DEBUG; # note: DEBUG is not quoted!
284            
285             # Create a file handle for logger to write to.
286             # This is not necessary, by default the logger
287             # writes to STDERR, but sometimes you will want
288             # to write to a file, as per this example.
289             open my $fh, '>', 'parsing.log' or die $!;
290            
291             # Create a logger object.
292             my $fac = Bio::Phylo::Factory->new;
293             my $logger = $fac->create_logger;
294            
295             # Set the verbosity level of the set_name
296             # method in the base class. Messages coming
297             # from this method will be transmitted.
298             $logger->VERBOSE(
299             '-level' => DEBUG, # note, not quoted, this is a constant!
300             '-method' => 'Bio::Phylo::set_name', # quoted, otherwise bareword error!
301             );
302            
303             # 'Listeners' are subroutine references that
304             # are executed when a message is transmitted.
305             # The first argument passed to these subroutines
306             # is the log message. This particular listener
307             # will write the message to the 'parsing.log'
308             # file, if the $fh file handle is still open.
309             $logger->set_listeners(
310             sub {
311             my ($msg) = @_;
312             if ( $fh->opened ) {
313             print $fh $msg;
314             }
315             }
316             );
317              
318             # Now parse a tree, and see what is logged.
319             my $tree = parse(
320             '-format' => 'newick',
321             '-string' => do { local $/; <DATA> },
322             )->first;
323              
324             # Cleanly close the log handle.
325             close $fh;
326            
327             __DATA__
328             ((((A,B),C),D),E);
329              
330             The example above will write something like the following to the log file:
331              
332             INFO Bio::Phylo::Forest::Tree::new [Bio/Phylo/Forest/Tree.pm, 99] - constructor called for 'Bio::Phylo::Forest::Tree'
333             INFO Bio::Phylo::set_name [Bio/Phylo.pm, 281] - setting name 'A'
334             INFO Bio::Phylo::set_name [Bio/Phylo.pm, 281] - setting name 'B'
335             INFO Bio::Phylo::set_name [Bio/Phylo.pm, 281] - setting name 'C'
336             INFO Bio::Phylo::set_name [Bio/Phylo.pm, 281] - setting name 'D'
337             INFO Bio::Phylo::set_name [Bio/Phylo.pm, 281] - setting name 'E'
338              
339             =head1 DESCRIPTION
340              
341             This class defines a logger, a utility object for logging messages.
342             The other objects in Bio::Phylo use this logger to give detailed feedback
343             about what they are doing at per-class, per-method, user-configurable log levels
344             (DEBUG, INFO, WARN, ERROR and FATAL). These log levels are constants that are
345             optionally exported by this class by passing the ':levels' argument to your
346             'use' statement, like so:
347              
348             use Bio::Phylo::Util::Logger ':levels';
349              
350             If for some reason you don't want this behaviour (i.e. because there is
351             something else by these same names in your namespace) you must use the fully
352             qualified names for these levels, i.e. Bio::Phylo::Util::Logger::DEBUG and
353             so on.
354              
355             The least verbose is level FATAL, in which case only 'fatal' messages are shown.
356             The most verbose level, DEBUG, shows debugging messages, including from internal
357             methods (i.e. ones that start with underscores, and special 'ALLCAPS' perl
358             methods like DESTROY or TIEARRAY). For example, to monitor what the root class
359             is doing, you would say:
360              
361             $logger->( -class => 'Bio::Phylo', -level => DEBUG )
362              
363             To define global verbosity you can omit the -class argument. To set verbosity
364             at a more granular level, you can use the -method argument, which takes a
365             fully qualified method name such as 'Bio::Phylo::set_name', such that messages
366             originating from within that method's body get a different verbosity level.
367              
368             =head1 METHODS
369              
370             =head2 CONSTRUCTOR
371              
372             =over
373              
374             =item new()
375              
376             Constructor for Logger.
377              
378             Type : Constructor
379             Title : new
380             Usage : my $logger = Bio::Phylo::Util::Logger->new;
381             Function: Instantiates a logger
382             Returns : a Bio::Phylo::Util::Logger object
383             Args : -level => Bio::Phylo::Util::Logger::INFO (DEBUG/INFO/WARN/ERROR/FATAL)
384             -class => a package (or array ref) for which to set verbosity (optional)
385             -method => a sub name (or array ref) for which to set verbosity (optional)
386             -file => a file to which to append logging messages
387             -listeners => array ref of subs that handle logging messages
388             -prefix => a path fragment to strip from the paths in logging messages
389            
390              
391             =back
392              
393             =head2 VERBOSITY LEVELS
394              
395             =over
396              
397             =item FATAL
398              
399             Rarely happens, usually an exception is thrown instead.
400              
401             =item ERROR
402              
403             If this happens, something is seriously wrong that needs to be addressed.
404              
405             =item WARN
406              
407             If this happens, something is seriously wrong that needs to be addressed.
408              
409             =item INFO
410              
411             If something weird is happening, turn up verbosity to this level as it might
412             explain some of the assumptions the code is making.
413              
414             =item DEBUG
415              
416             This is very verbose, probably only useful if you write core Bio::Phylo code.
417              
418             =back
419              
420             =head2 LOGGING METHODS
421              
422             =over
423              
424             =item debug()
425              
426             Prints argument debugging message, depending on verbosity.
427              
428             Type : logging method
429             Title : debug
430             Usage : $logger->debug( "debugging message" );
431             Function: prints debugging message, depending on verbosity
432             Returns : invocant
433             Args : logging message
434              
435             =item info()
436              
437             Prints argument informational message, depending on verbosity.
438              
439             Type : logging method
440             Title : info
441             Usage : $logger->info( "info message" );
442             Function: prints info message, depending on verbosity
443             Returns : invocant
444             Args : logging message
445              
446             =item warn()
447              
448             Prints argument warning message, depending on verbosity.
449              
450             Type : logging method
451             Title : warn
452             Usage : $logger->warn( "warning message" );
453             Function: prints warning message, depending on verbosity
454             Returns : invocant
455             Args : logging message
456              
457             =item error()
458              
459             Prints argument error message, depending on verbosity.
460              
461             Type : logging method
462             Title : error
463             Usage : $logger->error( "error message" );
464             Function: prints error message, depending on verbosity
465             Returns : invocant
466             Args : logging message
467              
468             =item fatal()
469              
470             Prints argument fatal message, depending on verbosity.
471              
472             Type : logging method
473             Title : fatal
474             Usage : $logger->fatal( "fatal message" );
475             Function: prints fatal message, depending on verbosity
476             Returns : invocant
477             Args : logging message
478              
479             =item set_listeners()
480              
481             Adds listeners to send log messages to.
482              
483             Type : Mutator
484             Title : set_listeners()
485             Usage : $logger->set_listeners( sub { warn shift } )
486             Function: Sets additional listeners to log to (e.g. a file)
487             Returns : invocant
488             Args : One or more code references
489             Comments: On execution of the listeners, the @_ arguments are:
490             $log_string, # the formatted log string
491             $level, # log level, i.e DEBUG, INFO, WARN, ERROR or FATAL
492             $subroutine, # the calling subroutine
493             $filename, # filename where log method was called
494             $line, # line where log method was called
495             $msg # the unformatted message
496              
497             =item start_color()
498              
499             Changes color of output stream to that of specified logging level. This so that for
500             example all errors are automatically marked up as 'FATAL', or all output generated
501             by an external program is marked up as 'DEBUG'
502              
503             Type : Mutator
504             Title : start_color()
505             Usage : $logger->start_color( 'DEBUG', \*STDOUT )
506             Function: Changes color of output stream
507             Returns : color name
508             Args : Log level whose color to use,
509             (optional) which stream to change, default is STDERR
510              
511             =item stop_color()
512              
513             Resets the color initiated by start_color()
514              
515             Type : Mutator
516             Title : stop_color()
517             Usage : $logger->stop_color( \*STDOUT )
518             Function: Changes color of output stream
519             Returns : color name
520             Args : (Optional) which stream to reset, default is STDERR
521              
522              
523             =item PREFIX()
524              
525             Getter and setter of path prefix to strip from source file paths in messages.
526             By default, messages will have a field such as C<[$PREFIX/Bio/Phylo.pm, 280]>,
527             which indicates the message was sent from line 280 in file Bio/Phylo.pm inside
528             path $PREFIX. This is done so that your log won't be cluttered with
529             unnecessarily long paths. To find out what C<$PREFIX> is set to, call the
530             PREFIX() method on the logger, and to change it provide a path argument
531             relative to which the paths to source files will be constructed.
532              
533             Type : Mutator/Accessor
534             Title : PREFIX()
535             Usage : $logger->PREFIX( '/path/to/bio/phylo' )
536             Function: Sets/gets $PREFIX
537             Returns : Verbose level
538             Args : Optional: a path
539             Comments:
540              
541             =item VERBOSE()
542              
543             Setter for the verbose level. This comes in five levels:
544              
545             FATAL = only fatal messages (though, when something fatal happens, you'll most
546             likely get an exception object),
547            
548             ERROR = errors (hopefully recoverable),
549            
550             WARN = warnings (recoverable),
551            
552             INFO = info (useful diagnostics),
553            
554             DEBUG = debug (almost every method call)
555              
556             Without additional arguments, i.e. by just calling VERBOSE( -level => $level ),
557             you set the global verbosity level. By default this is 2. By increasing this
558             level, the number of messages quickly becomes too great to make sense out of.
559             To focus on a particular class, you can add the -class => 'Some::Class'
560             (where 'Some::Class' stands for any of the class names in the Bio::Phylo
561             release) argument, which means that messages originating from that class will
562             have a different (presumably higher) verbosity level than the global level.
563             By adding the -method => 'Fully::Qualified::method_name' (say,
564             'Bio::Phylo::set_name'), you can change the verbosity of a specific method. When
565             evaluating whether or not to transmit a message, the method-specific verbosity
566             level takes precedence over the class-specific level, which takes precedence
567             over the global level.
568              
569             Type : Mutator
570             Title : VERBOSE()
571             Usage : $logger->VERBOSE( -level => $level )
572             Function: Sets/gets verbose level
573             Returns : Verbose level
574             Args : -level => 4 # or lower
575            
576             # optional, or any other class
577             -class => 'Bio::Phylo'
578            
579             # optional, fully qualified method name
580             -method' => 'Bio::Phylo::set_name'
581              
582             =back
583              
584             =head1 SEE ALSO
585              
586             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
587             for any user or developer questions and discussions.
588              
589             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
590              
591             =head1 CITATION
592              
593             If you use Bio::Phylo in published research, please cite it:
594              
595             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
596             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
597             I<BMC Bioinformatics> B<12>:63.
598             L<http://dx.doi.org/10.1186/1471-2105-12-63>
599              
600             =cut