File Coverage

blib/lib/Log/WarnDie.pm
Criterion Covered Total %
statement 65 92 70.6
branch 34 68 50.0
condition 6 21 28.5
subroutine 10 13 76.9
pod 2 2 100.0
total 117 196 59.6


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