File Coverage

blib/lib/Log/WarnDie.pm
Criterion Covered Total %
statement 45 82 54.8
branch 12 60 20.0
condition 4 21 19.0
subroutine 9 13 69.2
pod 2 2 100.0
total 72 178 40.4


line stmt bran cond sub pod time code
1             package Log::WarnDie;
2              
3 4     4   505084 use warnings;
  4         7  
  4         116  
4 4     4   19 use strict;
  4         5  
  4         71  
5              
6             # Make sure we have the modules that we need
7              
8 4     4   1161 use IO::Handle ();
  4         16659  
  4         96  
9 4     4   25 use Scalar::Util qw(blessed);
  4         9  
  4         2644  
10              
11             # The logging dispatcher that should be used
12             # The (original) error output handle
13             # Reference to the previous parameters sent
14              
15             our $DISPATCHER;
16             our $FILTER;
17             our $STDERR;
18             our $LAST;
19              
20             # Old settings of standard Perl logging mechanisms
21              
22             our $WARN;
23             our $DIE;
24              
25             =head1 NAME
26              
27             Log::WarnDie - Log standard Perl warnings and errors on a log handler
28              
29             =head1 VERSION
30              
31             Version 0.09
32              
33             =head1 SYNOPSIS
34              
35             use Log::WarnDie; # install to be used later
36             use Log::Dispatch;
37              
38             my $dispatcher = Log::Dispatch->new(); # can be any dispatcher!
39             $dispatcher->add( Log::Dispatch::Foo->new( # whatever output you like
40             name => 'foo',
41             min_level => 'info',
42             ) );
43              
44             use Log::WarnDie $dispatcher; # activate later
45              
46             Log::WarnDie->dispatcher( $dispatcher ); # same
47              
48             warn "This is a warning"; # now also dispatched
49             die "Sorry it didn't work out"; # now also dispatched
50              
51             no Log::WarnDie; # deactivate later
52              
53             Log::WarnDie->dispatcher( undef ); # same
54              
55             Log::WarnDie->filter(\&filter);
56             warn "This is a warning"; # no longer dispatched
57             die "Sorry it didn't work out"; # no longer dispatched
58              
59             # Filter out File::stat noise
60             sub filter {
61             return ($_[0] !~ /^S_IFFIFO is not a valid Fcntl macro/);
62             }
63              
64             =head1 DESCRIPTION
65              
66             The "Log::WarnDie" module offers a logging alternative for standard
67             Perl core functions. This allows you to use the features of e.g.
68             L<Log::Dispatch>, L<Log::Any> or L<Log::Log4perl> B<without> having to make extensive
69             changes to your source code.
70              
71             When loaded, it installs a __WARN__ and __DIE__ handler and intercepts any
72             output to STDERR. It also takes over the messaging functions of L<Carp>.
73             Without being further activated, the standard Perl logging functions continue
74             to be executed: e.g. if you expect warnings to appear on STDERR, they will.
75              
76             Then, when necessary, you can activate actual logging through e.g.
77             Log::Dispatch by installing a log dispatcher. From then on, any warn, die,
78             carp, croak, cluck, confess or print to the STDERR handle, will be logged
79             using the Log::Dispatch logging dispatcher. Logging can be disabled and
80             enabled at any time for critical sections of code.
81              
82             =cut
83              
84             our $VERSION = '0.09';
85              
86             =head1 SUBROUTINES/METHODS
87              
88             =cut
89              
90             #---------------------------------------------------------------------------
91              
92             # Tie subroutines need to be known at compile time, hence there here, near
93             # the start of code rather than near the end where these would normally live.
94              
95             #---------------------------------------------------------------------------
96             # TIEHANDLE
97             #
98             # Called whenever a dispatcher is activated
99             #
100             # IN: 1 class with which to bless
101             # OUT: 1 blessed object
102              
103 4     4   13 sub TIEHANDLE { bless \"$_[0]",$_[0] } #TIEHANDLE
104              
105             #---------------------------------------------------------------------------
106             # PRINT
107             #
108             # Called whenever something is printed on STDERR
109             #
110             # IN: 1 blessed object returned by TIEHANDLE
111             # 2..N whatever was needed to be printed
112              
113             sub PRINT {
114              
115             # Lose the object
116             # If there is a dispatcher
117             # Put it in the log handler if not the same as last time
118             # Reset the flag
119             # Make sure it appears on the original STDERR as well
120              
121 1     1   8 shift;
122 1 50       4 if($FILTER) {
123 0 0       0 return unless($FILTER->(@_));
124             }
125 1 50       3 if ($DISPATCHER) {
126 1 0 33     8 $DISPATCHER->error( @_ )
      33        
127             unless $LAST and @$LAST == @_ and join( '',@$LAST ) eq join( '',@_ );
128 1         175 undef $LAST;
129             }
130 1 50       7 if($STDERR) {
131 1         14 print $STDERR @_;
132             }
133             } #PRINT
134              
135             #---------------------------------------------------------------------------
136             # PRINTF
137             #
138             # Called whenever something is printed on STDERR using printf
139             #
140             # IN: 1 blessed object returned by TIEHANDLE
141             # 2..N whatever was needed to be printed
142              
143             sub PRINTF {
144              
145             # Lose the object
146             # If there is a dispatcher
147             # Put it in the log handler if not the same as last time
148             # Reset the flag
149             # Make sure it appears on the original STDERR as well
150              
151 1     1   7 shift;
152 1 50       3 if($FILTER) {
153 0 0       0 return unless($FILTER->(@_));
154             }
155 1 50       3 if ($DISPATCHER) {
156 1 0 33     6 $DISPATCHER->error( @_ )
      33        
157             unless $LAST and @$LAST == @_ and join( '',@$LAST ) eq join( '',@_ );
158 1         119 undef $LAST;
159             }
160 1 50       4 if($STDERR) {
161 1         37 printf $STDERR @_;
162             }
163             } #PRINTF
164              
165             #---------------------------------------------------------------------------
166             # CLOSE
167             #
168             # Called whenever something tries to close STDERR
169             #
170             # IN: 1 blessed object returned by TIEHANDLE
171             # 2..N whatever was needed to be printed
172              
173             sub CLOSE {
174              
175             # Lose the object
176             # If there is a dispatcher
177             # Put it in the log handler if not the same as last time
178             # Reset the flag
179             # Make sure it appears on the original STDERR as well
180              
181 0     0   0 my $keep = $STDERR;
182 0         0 $STDERR = undef;
183 0         0 close $keep; # So that the return status can be checked
184             } #CLOSE
185              
186             #---------------------------------------------------------------------------
187             # OPEN
188             #
189             # Called whenever something tries to (re)open STDERR
190             #
191             # IN: 1 blessed object returned by TIEHANDLE
192             # 2..N whatever was needed to be printed
193              
194             sub OPEN {
195              
196             # Lose the object
197             # If there is a dispatcher
198             # Put it in the log handler if not the same as last time
199             # Reset the flag
200             # Make sure it appears on the original STDERR as well
201              
202 0     0   0 shift;
203 0         0 my $arg1 = shift;
204 0         0 my $arg2 = shift;
205              
206 0         0 open($STDERR, $arg1, $arg2);
207             } #OPEN
208             #---------------------------------------------------------------------------
209             # At compile time
210             # Create new handle
211             # Make sure it's the same as the current STDERR
212             # Make sure the original STDERR is now handled by our sub
213              
214             BEGIN {
215 4     4   22 $STDERR = new IO::Handle;
216 4 50       87 $STDERR->fdopen( fileno( STDERR ),"w" )
217             or die "Could not open STDERR 2nd time: $!\n";
218 4         175 tie *STDERR,__PACKAGE__;
219              
220             # Save current __WARN__ setting
221             # Replace it with a sub that
222             # If there is a dispatcher
223             # Remembers the last parameters
224             # Dispatches a warning message
225             # Executes the standard system warn() or whatever was there before
226              
227 4         8 $WARN = $SIG{__WARN__};
228             $SIG{__WARN__} = sub {
229 0 0       0 if($FILTER) {
230 0 0       0 unless($FILTER->(@_)) {
231 0 0       0 $WARN ? $WARN->( @_ ) : CORE::warn( @_ );
232 0         0 return;
233             }
234             }
235 0 0       0 if ($DISPATCHER) {
236 0         0 $LAST = \@_;
237 0 0       0 if(ref($DISPATCHER) =~ /^Log::Log4perl/) {
238 0         0 $DISPATCHER->warn( @_ );
239             } else {
240 0         0 $DISPATCHER->warning( @_ );
241             }
242             }
243 0 0       0 $WARN ? $WARN->( @_ ) : CORE::warn( @_ );
244 4         24 };
245              
246             # Save current __DIE__ setting
247             # Replace it with a sub that
248             # If there is a dispatcher
249             # Remembers the last parameters
250             # Dispatches a critical message
251             # Executes the standard system die() or whatever was there before
252              
253 4         10 $DIE = $SIG{__DIE__};
254             $SIG{__DIE__} = sub {
255             # File::stat goes to long efforts to not display the Fcntl message - then we go and display it,
256             # so let's not do that
257             # TODO: would be better to set a list of messages to be filtered out
258 0 0 0     0 if ($DISPATCHER && ($_[0] !~ /^S_IFFIFO is not a valid Fcntl macro/)) {
259 0 0       0 if($FILTER) {
260 0 0       0 unless($FILTER->(@_)) {
261 0 0       0 if($DIE) {
262 0         0 $DIE->(@_);
263             } else {
264 0 0 0     0 return unless((defined $^S) && ($^S == 0)); # Ignore errors in eval
265 0         0 CORE::die(@_);
266             }
267             }
268             }
269 0         0 $LAST = \@_;
270 0 0       0 if(ref($DISPATCHER) =~ /^Log::Log4perl/) {
271 0         0 $DISPATCHER->fatal( @_ );
272             } else {
273 0         0 $DISPATCHER->critical( @_ );
274             }
275             }
276             # Handle http://stackoverflow.com/questions/8078220/custom-error-handling-is-catching-errors-that-normally-are-not-displayed
277             # $DIE ? $DIE->( @_ ) : CORE::die( @_ );
278 0 0       0 if($DIE) {
279 0         0 $DIE->(@_);
280             } else {
281 0 0 0     0 return unless((defined $^S) && ($^S == 0)); # Ignore errors in eval
282 0         0 CORE::die(@_);
283             }
284 4         15 };
285              
286             # Make sure we won't be listed ourselves by Carp::
287              
288 4         775 $Carp::Internal{__PACKAGE__} = 1;
289             } #BEGIN
290              
291             # Satisfy require
292              
293             1;
294              
295             #---------------------------------------------------------------------------
296              
297             # Class methods
298              
299             #---------------------------------------------------------------------------
300              
301             =head2 dispatcher
302              
303             Class method to set and/or return the current dispatcher
304              
305             # IN: 1 class (ignored)
306             # 2 new dispatcher (optional)
307             # OUT: 1 current dispatcher
308              
309             =cut
310              
311             sub dispatcher {
312              
313             # Return the current dispatcher if no changes needed
314             # Set the new dispatcher
315              
316 4 100   4 1 1631 return $DISPATCHER unless @_ > 1;
317 1         3 $DISPATCHER = $_[1];
318              
319             # If there is a dispatcher now
320             # If the dispatcher is a Log::Dispatch er
321             # Make sure all of standard Log::Dispatch stuff becomes invisible for Carp::
322             # If there are outputs already
323             # Make sure all of the output objects become invisible for Carp::
324              
325 1 50       3 if ($DISPATCHER) {
326 1 50       5 if ($DISPATCHER->isa( 'Log::Dispatch' )) {
327             $Carp::Internal{$_} = 1
328 1         6 foreach 'Log::Dispatch','Log::Dispatch::Output';
329 1 50       4 if (my $outputs = $DISPATCHER->{'outputs'}) {
330             $Carp::Internal{$_} = 1
331 1         2 foreach map {blessed $_} values %{$outputs};
  1         7  
  1         4  
332             }
333             }
334             }
335              
336             # Return the current dispatcher
337              
338 1         2 $DISPATCHER;
339             } #dispatcher
340              
341             =head2 filter
342              
343             Class method to set and/or get the current output filter
344              
345             The given callback function should return 1 to output the given message, or 0
346             to drop it.
347             Useful for noisy messages such as File::stat giving S_IFFIFO is not a valid Fcntl macro.
348              
349             =cut
350              
351             sub filter {
352 0 0   0 1   return $FILTER unless @_ > 1;
353 0           $FILTER = $_[1];
354             }
355              
356              
357             #---------------------------------------------------------------------------
358              
359             # Perl standard features
360              
361             #---------------------------------------------------------------------------
362             # import
363             #
364             # Called whenever a -use- is done.
365             #
366             # IN: 1 class (ignored)
367             # 2 new dispatcher (optional)
368              
369             *import = \&dispatcher;
370              
371             #---------------------------------------------------------------------------
372             # unimport
373             #
374             # Called whenever a -use- is done.
375             #
376             # IN: 1 class (ignored)
377              
378 0     0     sub unimport { import( undef ) } #unimport
379              
380             #---------------------------------------------------------------------------
381              
382             __END__
383              
384             =head1 LOG LEVELS
385              
386             The following log levels are used:
387              
388             =head2 warning
389              
390             Any C<warn>, C<Carp::carp> or C<Carp::cluck> will generate a "warning" level
391             message.
392              
393             =head2 error
394              
395             Any direct output to STDERR will generate an "error" level message.
396              
397             =head2 critical
398              
399             Any C<die>, C<Carp::croak> or C<Carp::confess> will generate a "critical"
400             level message.
401              
402             =head1 REQUIRED MODULES
403              
404             Scalar::Util (1.08)
405              
406             =head1 CAVEATS
407              
408             The following caveats may apply to your situation.
409              
410             =head2 Associated modules
411              
412             Although a module such as L<Log::Dispatch> is B<not> listed as a prerequisite,
413             the real use of this module only comes into view when such a module B<is>
414             installed. Please note that for testing this module, you will need the
415             L<Log::Dispatch::Buffer> module to also be available.
416              
417             This module has been tested with
418             L<Log::Dispatch>, L<Log::Any> and L<Log::Log4perl>.
419             In principle any object which recognises C<warning>, C<error> and C<critical> should work.
420              
421             =head2 eval
422              
423             In the current implementation of Perl, a __DIE__ handler is B<also> called
424             inside an eval. Whereas a normal C<die> would just exit the eval, the __DIE__
425             handler _will_ get called inside the eval. Which may or may not be what you
426             want. To prevent the __DIE__ handler to be called inside eval's, add the
427             following line to the eval block or string being evaluated:
428              
429             local $SIG{__DIE__} = undef;
430              
431             This disables the __DIE__ handler within the evalled block or string, and
432             will automatically enable it again upon exit of the evalled block or string.
433             Unfortunately there is no automatic way to do that for you.
434              
435             =head1 AUTHOR
436              
437             Elizabeth Mattijsen, <liz@dijkmat.nl>
438              
439             Maintained by Nigel Horne, C<< <njh at bandsman.co.uk> >>
440              
441             =head1 BUGS
442              
443             Please report any bugs or feature requests to C<bug-log-warndie at rt.cpan.org>,
444             or through the web interface at
445             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Log-WarnDie>.
446             I will be notified, and then you'll
447             automatically be notified of progress on your bug as I make changes.
448              
449             =head1 COPYRIGHT
450              
451             Copyright (c) 2004, 2007 Elizabeth Mattijsen <liz@dijkmat.nl>. All rights
452             reserved. This program is free software; you can redistribute it and/or
453             modify it under the same terms as Perl itself.
454              
455             Portions of versions 0.06 onwards, Copyright 2017 Nigel Horne
456              
457             =cut