File Coverage

blib/lib/Dist/Zilla/Role/ErrorLogger.pm
Criterion Covered Total %
statement 88 88 100.0
branch 21 24 87.5
condition 3 3 100.0
subroutine 15 15 100.0
pod 4 4 100.0
total 131 134 97.7


line stmt bran cond sub pod time code
1             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2             #
3             # file: Dist/Zilla/Role/ErrorLogger.pm
4             #
5             # Copyright © 2015 Van de Bugger
6             #
7             # This file is part of perl-Dist-Zilla-Role-ErrorLogger.
8             #
9             # perl-Dist-Zilla-Role-ErrorLogger is free software: you can redistribute it and/or modify it
10             # under the terms of the GNU General Public License as published by the Free Software Foundation,
11             # either version 3 of the License, or (at your option) any later version.
12             #
13             # perl-Dist-Zilla-Role-ErrorLogger is distributed in the hope that it will be useful, but WITHOUT
14             # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15             # PURPOSE. See the GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License along with
18             # perl-Dist-Zilla-Role-ErrorLogger. If not, see <http://www.gnu.org/licenses/>.
19             #
20             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21              
22             #pod =for :this This is C<Dist::Zilla::Role::ErrorLogger> role documentation. Read this if you want to
23             #pod have error logging capabilities in your Dist::Zilla plugin.
24             #pod
25             #pod =for test_synopsis
26             #pod my ( $cond, $file );
27             #pod sub Dist::Zilla::Plugin::YourPlugin::do_something;
28             #pod sub Dist::Zilla::Plugin::YourPlugin::do_something_else;
29             #pod
30             #pod =head1 SYNOPSIS
31             #pod
32             #pod package Dist::Zilla::Plugin::YourPlugin;
33             #pod use Moose;
34             #pod use namespace::autoclean;
35             #pod with 'Dist::Zilla::Role::Plugin';
36             #pod with 'Dist::Zilla::Role::ErrorLogger';
37             #pod
38             #pod sub method {
39             #pod my $self = shift( @_ );
40             #pod
41             #pod if ( $cond ) { $self->log_error( 'error message' ); };
42             #pod
43             #pod do_something or $self->log_error( 'another error message' );
44             #pod
45             #pod while ( $cond ) {
46             #pod do_something_else or $self->log_error( 'error message' ) and next;
47             #pod ...;
48             #pod };
49             #pod
50             #pod $self->log_errors_in_file(
51             #pod $file,
52             #pod 1 => 'error message', # Error at file line 1.
53             #pod 5 => 'another error message', # Error at file line 5.
54             #pod );
55             #pod
56             #pod $self->abort_if_errors( 'errors found' );
57             #pod };
58             #pod
59             #pod __PACKAGE__->meta->make_immutable;
60             #pod 1;
61             #pod
62             #pod =cut
63              
64             # --------------------------------------------------------------------------------------------------
65              
66             #pod =head1 DESCRIPTION
67             #pod
68             #pod The role extends standard C<Dist::Zilla> logging capabilities with few methods a bit more
69             #pod convenient for reporting (multiple) errors than brutal C<log_fatal>. See L</"WHY?"> for more
70             #pod details.
71             #pod
72             #pod The role requires C<log> method in the consumer.
73             #pod
74             #pod =cut
75              
76             # --------------------------------------------------------------------------------------------------
77              
78             package Dist::Zilla::Role::ErrorLogger;
79              
80 1     1   2768626 use Moose::Role;
  1         3  
  1         10  
81 1     1   5172 use namespace::autoclean;
  1         3  
  1         9  
82              
83             # ABSTRACT: Have error logging capabilities in your Dist::Zilla plugin
84             our $VERSION = 'v0.9.0'; # VERSION
85              
86             requires qw{ log };
87              
88 1     1   94 use List::Util qw{ min max };
  1         2  
  1         1013  
89              
90             # --------------------------------------------------------------------------------------------------
91              
92             #pod =attr error_count
93             #pod
94             #pod $int = $self->error_count;
95             #pod
96             #pod C<Int>, read-only. Number of logged errors (i. e. number of made C<log_error> calls).
97             #pod
98             #pod =cut
99              
100             has error_count => (
101             is => 'ro',
102             isa => 'Int',
103             default => 0,
104             init_arg => undef,
105             );
106              
107             # --------------------------------------------------------------------------------------------------
108              
109             #pod =method log_error
110             #pod
111             #pod $self->log_error( @items );
112             #pod $self->log_error( \%args, @items );
113             #pod
114             #pod This method calls C<log> method, passing all the arguments, and increments value of C<error_count>
115             #pod attribute. The method returns true value, so can be used in following constructs:
116             #pod
117             #pod while ( ... ) {
118             #pod do_something or $self->log_error( 'message' ) and next;
119             #pod ...
120             #pod };
121             #pod
122             #pod =cut
123              
124             sub log_error { ## no critic ( RequireArgUnpacking )
125 46     46 1 92407 my $self = shift( @_ );
126             # If the first argument is a hashref, it is treated as extra arguments to logging, not as
127             # message, see <https://metacpan.org/pod/Log::Dispatchouli#log>. These extra arguments
128             # include `level` argument. It seems natural that error messages have `'error'` level.
129             # However, such messages do not appear in `dzil` output. So, do not try to set level, just
130             # pass all the arguments to `log`.
131 46         157 $self->log( @_ );
132 46         13733 ++ $self->{ error_count };
133 46         173 return $self->{ error_count };
134             };
135              
136             # --------------------------------------------------------------------------------------------------
137              
138             #pod =method abort
139             #pod
140             #pod $self->abort( @items );
141             #pod $self->abort( \%args, @items );
142             #pod
143             #pod This is an attempt to workaround L<C<log_fatal>
144             #pod drawback|https://github.com/rjbs/Dist-Zilla/issues/397>: in contrast to C<log_fatal>, C<abort>
145             #pod guarantees the message (which can be quite long) appears on the screen only once.
146             #pod
147             #pod The method log the message (via C<log>), then flush C<STDOUT>, then throws an exception of
148             #pod C<Dist::Zilla::Role::ErrorLogger::Exception::Abort> class (which being stringified gives short
149             #pod message C<"Aborting...\n">).
150             #pod
151             #pod =cut
152              
153             sub abort { ## no critic ( RequireArgUnpacking, RequireFinalReturn )
154 7     7 1 157711 my $self = shift( @_ );
155 7 100       34 if ( @_ ) {
156 2         11 $self->log( @_ );
157             };
158 7         624 STDOUT->flush();
159 7         80 Dist::Zilla::Role::ErrorLogger::Exception::Abort->throw();
160             };
161              
162             # --------------------------------------------------------------------------------------------------
163              
164             #pod =method abort_if_error
165             #pod
166             #pod =method abort_if_errors
167             #pod
168             #pod $self->abort_if_errors( @items );
169             #pod $self->abort_if_errors( \%args, @items );
170             #pod
171             #pod If there was any errors (i. e. C<error_count> is greater than zero), the logs all the arguments and
172             #pod aborts execution. Both actions (logging and aborting) are implemented by calling C<abort>.
173             #pod
174             #pod C<abort_if_error> is an alias for C<abort_if_errors>.
175             #pod
176             #pod =cut
177              
178             sub abort_if_errors { ## no critic ( RequireArgUnpacking )
179 7     7 1 47502 my $self = shift( @_ );
180 7 100       295 if ( $self->error_count ) {
181 5         18 $self->abort( @_ );
182             };
183 2         6 return;
184             };
185              
186             *abort_if_error = \&abort_if_errors;
187              
188             # --------------------------------------------------------------------------------------------------
189              
190             #pod =method log_errors_in_file
191             #pod
192             #pod The method intended to report errors against a file. It prints file name (and colon after it), then
193             #pod prints line-numbered file content annotated by error messages. The method does not print entire
194             #pod file content, but only error lines with surrounding context (2 lines above and below each error
195             #pod line).
196             #pod
197             #pod $self->log_errors_in_file(
198             #pod $file,
199             #pod $linenum1 => $message1,
200             #pod $linenum2 => $message2,
201             #pod $linenum3 => [ $message3a, $message3b, ... ],
202             #pod ...
203             #pod );
204             #pod
205             #pod C<$file> should be a C<Dist::Zilla> file (e. g. C<Dist::Zilla::File::OnDisk>,
206             #pod C<Dist::Zilla::File::InMemory>, or does role C<Dist::Zilla::Role::File>).
207             #pod
208             #pod Errors are specified by pairs C<< $linenum => $message >>, where C<$linenum> is a number of problem
209             #pod line (one-based), and C<$message> is an error message (C<Str>) or array of messages
210             #pod (C<ArrayRef[Str]>). Order of errors does not matter usually. However, if errors are associated with
211             #pod the same line (the same line number may appear multiple times), they will be printed in order of
212             #pod appearance.
213             #pod
214             #pod Zero or negative line numbers, or line numbers beyond the last line are invalid. Messages
215             #pod associated with invalid line numbers are reported in unspecified way.
216             #pod
217             #pod Normally, the method prints all the information by calling C<log_error> method and returns a
218             #pod positive integer. However, If any invalid line numbers are specified, the method returns negative
219             #pod integer. If no errors are specified, the method prints "No errors found at I<file>." by calling
220             #pod C<log> (not C<log_error>!) and returns zero.
221             #pod
222             #pod TODO: Example.
223             #pod
224             #pod =cut
225              
226             sub log_errors_in_file {
227              
228 4     4 1 207566 my ( $self, $file, @errors ) = @_;
229              
230             # Corner case: no errors specified.
231 4 100       23 if ( not @errors ) {
232 1         9 $self->log( [ 'No errors at %s.', $file->name ] );
233 1         396 return 0;
234             };
235              
236             # TODO: Chop too long lines?
237 3         18 my $text = [ split( "\n", $file->content ) ];
238              
239             # Parse `@errors`.
240 3         306 my %errors; # Key is line number, value is arrayref to error messages.
241             my %invalid; # The same but for invalid line numbers.
242 3         12 while ( @errors ) {
243 10         25 my ( $n, $msg ) = splice( @errors, 0, 2 );
244 10 100 100     54 if ( 1 <= $n and $n <= @$text ) {
245 6         9 my $ctx = 2; # TODO: Parametrize it?
246 6         57 for my $k ( max( $n - $ctx, 1 ) .. min( $n + $ctx, @$text + 0 ) ) {
247 27 100       64 if ( not $errors{ $k } ) {
248 19         49 $errors{ $k } = [];
249             };
250             };
251 6 100       12 push( @{ $errors{ $n } }, ref( $msg ) ? @$msg : $msg );
  6         32  
252             } else {
253 4 50       6 push( @{ $invalid{ $n } }, ref( $msg ) ? @$msg : $msg );
  4         23  
254             };
255             };
256              
257 3         6 my $t = ' ' x 4; # Indent for text lines.
258              
259 3 50       9 if ( %errors ) {
260              
261 3         9 my $w = length( 0 + @$text ); # Width of linenumber column.
262 3         12 my $e = $t . ( ' ' x ( $w + 2 ) ); # Indent for error messages.
263 3         6 my $last = 0; # Number of the last printed text line.
264              
265             my $log_line = sub { # Log text line number and content.
266 20     20   25 my ( $n ) = @_;
267 20 50       58 my $line = $n <= @$text ? $text->[ $n - 1 ] : '';
268 20         38 chomp( $line );
269 20         69 $self->log_error( [ '%s%0*d: %s', $t, $w, $n, $line ] );
270 3         16 };
271             my $log_messages = sub { # Log error messahes.
272 19     19   32 my ( $n ) = @_;
273 19         23 $self->log_error( [ '%s^^^ %s ^^^', $e, $_ ] ) for @{ $errors{ $n } };
  19         65  
274 3         12 };
275             my $log_skipped = sub { # Log number of skipped lines.
276 22     22   33 my ( $n ) = @_;
277 22 100       62 if ( $n > $last + 1 ) { # There are skipped lines.
278 6         10 my $count = $n - $last - 1; # Number of skipped lines.
279 6 100       13 if ( $count == 1 ) {
280 1         5 $log_line->( $n - 1 ); # There is no sense to skip one line.
281             } else {
282 5         18 $self->log_error( [ '%s... skipped %d lines ...', $e, $count ] );
283             };
284             };
285 3         13 };
286              
287             # Do actual logging.
288 3         19 $self->log_error( [ '%s:', $file->name ] );
289 3         20 for my $n ( sort( { $a <=> $b } keys( %errors ) ) ) {
  31         51  
290 19         35 $log_skipped->( $n );
291 19         37 $log_line->( $n );
292 19         48 $log_messages->( $n );
293 19         36 $last = $n;
294             };
295 3         9 $log_skipped->( @$text + 1 );
296              
297             };
298              
299 3 100       10 if ( %invalid ) {
300 1         4 $self->log_error( 'Following errors are reported against non-existing lines of the file:' );
301 1         4 for my $n ( sort( { $a <=> $b } keys( %invalid ) ) ) {
  4         10  
302             $self->log_error( [ '%s%s at %s line %d.', $t, $_, $file->name, $n ] )
303 4         5 for @{ $invalid{ $n } };
  4         21  
304             };
305 1         7 return -1;
306             };
307              
308 2         14 return 1;
309              
310             };
311              
312             # --------------------------------------------------------------------------------------------------
313              
314             ## no critic ( ProhibitMultiplePackages )
315              
316             package Dist::Zilla::Role::ErrorLogger::Exception::Abort;
317              
318 1     1   6 use strict;
  1         2  
  1         21  
319 1     1   6 use warnings;
  1         2  
  1         71  
320              
321             ## no critic ( ProhibitReusedNames )
322             # ABSTRACT: Exception class which C<ErrorLogger> throws to abort C<Dist::Zilla>
323             our $VERSION = 'v0.9.0'; # VERSION
324             ## critic ( ProhibitReusedNames )
325              
326 1     1   6 use overload '""' => sub { return "Aborting...\n"; };
  1     14   2  
  1         11  
  14         3046  
327              
328             sub throw {
329 7     7   16 my ( $class ) = @_;
330 7         82 die bless( {} => $class ); ## no critic ( RequireCarping )
331             };
332              
333             # --------------------------------------------------------------------------------------------------
334              
335             1;
336              
337             # --------------------------------------------------------------------------------------------------
338              
339             #pod =head1 NOTES
340             #pod
341             #pod All the methods defined in the role log items through the C<log> method. C<Dist::Zilla> takes this
342             #pod method from C<Log::Dispatchouli>, the latter uses C<String::Flogger> to process the messages. It
343             #pod means you can use C<String::Flogger> tricks, e. g.:
344             #pod
345             #pod $self->log_error( [ 'oops at %s line %d', $file, $line ] );
346             #pod # [] are shorter than sprintf.
347             #pod
348             #pod Also note how C<Log::Dispatchouli> describes the C<log> method:
349             #pod
350             #pod $logger->log( @messages );
351             #pod
352             #pod and says:
353             #pod
354             #pod Each message is flogged individually, then joined with spaces.
355             #pod
356             #pod So beware. A call
357             #pod
358             #pod $self->log_error( 'error 1', 'error 2' );
359             #pod
360             #pod logs I<one> message "error 1 error 2", I<not> I<two> messages "error 1" and "error 2", and bumps
361             #pod C<error_count> by 1, not 2.
362             #pod
363             #pod =head1 SEE ALSO
364             #pod
365             #pod =for :list
366             #pod = L<Dist::Zilla>
367             #pod = L<Dist::Zilla::Role>
368             #pod = L<Dist::Zilla::Plugin>
369             #pod = L<Log::Dispatchouli>
370             #pod = L<String::Flogger>
371             #pod
372             #pod =head1 COPYRIGHT AND LICENSE
373             #pod
374             #pod Copyright (C) 2015 Van de Bugger
375             #pod
376             #pod License GPLv3+: The GNU General Public License version 3 or later
377             #pod <http://www.gnu.org/licenses/gpl-3.0.txt>.
378             #pod
379             #pod This is free software: you are free to change and redistribute it. There is
380             #pod NO WARRANTY, to the extent permitted by law.
381             #pod
382             #pod
383             #pod =cut
384              
385             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
386             #
387             # file: doc/what.pod
388             #
389             # This file is part of perl-Dist-Zilla-Role-ErrorLogger.
390             #
391             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
392              
393             #pod =encoding UTF-8
394             #pod
395             #pod =head1 WHAT?
396             #pod
397             #pod C<Dist-Zilla-Role-ErrorLogger> is a C<Dist::Zilla> role. It provides C<log_error>, C<abort>, and
398             #pod C<abort_if_errors> methods to consuming plugins.
399             #pod
400             #pod =cut
401              
402             # end of file #
403             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
404             #
405             # file: doc/why.pod
406             #
407             # This file is part of perl-Dist-Zilla-Role-ErrorLogger.
408             #
409             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
410              
411             #pod =encoding UTF-8
412             #pod
413             #pod =head1 WHY?
414             #pod
415             #pod C<Dist::Zilla> limits logging capabilities with 3 logging levels available in plugins through
416             #pod C<log_debug>, C<log>, and C<log_fatal> methods. Debug level messages are turned off by default, the
417             #pod first fatal message terminates C<Dist::Zilla>. This is simple, but sometimes you may want to report
418             #pod all the errors, instead of stopping at the first found one. In such a case C<log_fatal> cannot be
419             #pod used, obviously. There are few alternatives:
420             #pod
421             #pod Collect error messages in an array, then report all the errors with single C<log_fatal> call:
422             #pod
423             #pod my @errors;
424             #pod ...
425             #pod push( @errors, ... );
426             #pod ...
427             #pod if ( @errors ) {
428             #pod $self->log_fatal( join( "\n", @errors ) );
429             #pod };
430             #pod
431             #pod This works, but current implementation of C<log_fatal> has a disadvantage: it prints the message
432             #pod twice, so output looks ugly. (See L<message handling in log_fatal is
433             #pod suboptimal|https://github.com/rjbs/Dist-Zilla/issues/397>.)
434             #pod
435             #pod Another approach is reporting each error immediately with C<log>, counting number of reported
436             #pod errors, and calling C<log_fatal> once at the end:
437             #pod
438             #pod my $error_count = 0;
439             #pod ...
440             #pod $self->log( 'error' );
441             #pod ++ $error_count;
442             #pod ...
443             #pod if ( $error_count ) {
444             #pod $self->log_fatal( 'Aborting...' );
445             #pod };
446             #pod
447             #pod This works, but incrementing the counter after each C<log> call is boring and error-prone.
448             #pod C<Dist-Zilla-Role-ErrorLogger> role automates it, making plugin code shorter and more readable:
449             #pod
450             #pod with 'Dist-Zilla-Role-ErrorLogger';
451             #pod ...
452             #pod $self->log_error( 'error' );
453             #pod ...
454             #pod $self->abort_if_errors();
455             #pod
456             #pod =cut
457              
458             # end of file #
459              
460              
461             # end of file #
462              
463             __END__
464              
465             =pod
466              
467             =encoding UTF-8
468              
469             =head1 NAME
470              
471             Dist::Zilla::Role::ErrorLogger - Have error logging capabilities in your Dist::Zilla plugin
472              
473             =head1 VERSION
474              
475             Version v0.9.0, released on 2015-10-26 21:04 UTC.
476              
477             =head1 WHAT?
478              
479             C<Dist-Zilla-Role-ErrorLogger> is a C<Dist::Zilla> role. It provides C<log_error>, C<abort>, and
480             C<abort_if_errors> methods to consuming plugins.
481              
482             This is C<Dist::Zilla::Role::ErrorLogger> role documentation. Read this if you want to
483             have error logging capabilities in your Dist::Zilla plugin.
484              
485             =head1 SYNOPSIS
486              
487             package Dist::Zilla::Plugin::YourPlugin;
488             use Moose;
489             use namespace::autoclean;
490             with 'Dist::Zilla::Role::Plugin';
491             with 'Dist::Zilla::Role::ErrorLogger';
492              
493             sub method {
494             my $self = shift( @_ );
495              
496             if ( $cond ) { $self->log_error( 'error message' ); };
497              
498             do_something or $self->log_error( 'another error message' );
499              
500             while ( $cond ) {
501             do_something_else or $self->log_error( 'error message' ) and next;
502             ...;
503             };
504              
505             $self->log_errors_in_file(
506             $file,
507             1 => 'error message', # Error at file line 1.
508             5 => 'another error message', # Error at file line 5.
509             );
510              
511             $self->abort_if_errors( 'errors found' );
512             };
513              
514             __PACKAGE__->meta->make_immutable;
515             1;
516              
517             =head1 DESCRIPTION
518              
519             The role extends standard C<Dist::Zilla> logging capabilities with few methods a bit more
520             convenient for reporting (multiple) errors than brutal C<log_fatal>. See L</"WHY?"> for more
521             details.
522              
523             The role requires C<log> method in the consumer.
524              
525             =head1 OBJECT ATTRIBUTES
526              
527             =head2 error_count
528              
529             $int = $self->error_count;
530              
531             C<Int>, read-only. Number of logged errors (i. e. number of made C<log_error> calls).
532              
533             =head1 OBJECT METHODS
534              
535             =head2 log_error
536              
537             $self->log_error( @items );
538             $self->log_error( \%args, @items );
539              
540             This method calls C<log> method, passing all the arguments, and increments value of C<error_count>
541             attribute. The method returns true value, so can be used in following constructs:
542              
543             while ( ... ) {
544             do_something or $self->log_error( 'message' ) and next;
545             ...
546             };
547              
548             =head2 abort
549              
550             $self->abort( @items );
551             $self->abort( \%args, @items );
552              
553             This is an attempt to workaround L<C<log_fatal>
554             drawback|https://github.com/rjbs/Dist-Zilla/issues/397>: in contrast to C<log_fatal>, C<abort>
555             guarantees the message (which can be quite long) appears on the screen only once.
556              
557             The method log the message (via C<log>), then flush C<STDOUT>, then throws an exception of
558             C<Dist::Zilla::Role::ErrorLogger::Exception::Abort> class (which being stringified gives short
559             message C<"Aborting...\n">).
560              
561             =head2 abort_if_error
562              
563             =head2 abort_if_errors
564              
565             $self->abort_if_errors( @items );
566             $self->abort_if_errors( \%args, @items );
567              
568             If there was any errors (i. e. C<error_count> is greater than zero), the logs all the arguments and
569             aborts execution. Both actions (logging and aborting) are implemented by calling C<abort>.
570              
571             C<abort_if_error> is an alias for C<abort_if_errors>.
572              
573             =head2 log_errors_in_file
574              
575             The method intended to report errors against a file. It prints file name (and colon after it), then
576             prints line-numbered file content annotated by error messages. The method does not print entire
577             file content, but only error lines with surrounding context (2 lines above and below each error
578             line).
579              
580             $self->log_errors_in_file(
581             $file,
582             $linenum1 => $message1,
583             $linenum2 => $message2,
584             $linenum3 => [ $message3a, $message3b, ... ],
585             ...
586             );
587              
588             C<$file> should be a C<Dist::Zilla> file (e. g. C<Dist::Zilla::File::OnDisk>,
589             C<Dist::Zilla::File::InMemory>, or does role C<Dist::Zilla::Role::File>).
590              
591             Errors are specified by pairs C<< $linenum => $message >>, where C<$linenum> is a number of problem
592             line (one-based), and C<$message> is an error message (C<Str>) or array of messages
593             (C<ArrayRef[Str]>). Order of errors does not matter usually. However, if errors are associated with
594             the same line (the same line number may appear multiple times), they will be printed in order of
595             appearance.
596              
597             Zero or negative line numbers, or line numbers beyond the last line are invalid. Messages
598             associated with invalid line numbers are reported in unspecified way.
599              
600             Normally, the method prints all the information by calling C<log_error> method and returns a
601             positive integer. However, If any invalid line numbers are specified, the method returns negative
602             integer. If no errors are specified, the method prints "No errors found at I<file>." by calling
603             C<log> (not C<log_error>!) and returns zero.
604              
605             TODO: Example.
606              
607             =head1 WHY?
608              
609             C<Dist::Zilla> limits logging capabilities with 3 logging levels available in plugins through
610             C<log_debug>, C<log>, and C<log_fatal> methods. Debug level messages are turned off by default, the
611             first fatal message terminates C<Dist::Zilla>. This is simple, but sometimes you may want to report
612             all the errors, instead of stopping at the first found one. In such a case C<log_fatal> cannot be
613             used, obviously. There are few alternatives:
614              
615             Collect error messages in an array, then report all the errors with single C<log_fatal> call:
616              
617             my @errors;
618             ...
619             push( @errors, ... );
620             ...
621             if ( @errors ) {
622             $self->log_fatal( join( "\n", @errors ) );
623             };
624              
625             This works, but current implementation of C<log_fatal> has a disadvantage: it prints the message
626             twice, so output looks ugly. (See L<message handling in log_fatal is
627             suboptimal|https://github.com/rjbs/Dist-Zilla/issues/397>.)
628              
629             Another approach is reporting each error immediately with C<log>, counting number of reported
630             errors, and calling C<log_fatal> once at the end:
631              
632             my $error_count = 0;
633             ...
634             $self->log( 'error' );
635             ++ $error_count;
636             ...
637             if ( $error_count ) {
638             $self->log_fatal( 'Aborting...' );
639             };
640              
641             This works, but incrementing the counter after each C<log> call is boring and error-prone.
642             C<Dist-Zilla-Role-ErrorLogger> role automates it, making plugin code shorter and more readable:
643              
644             with 'Dist-Zilla-Role-ErrorLogger';
645             ...
646             $self->log_error( 'error' );
647             ...
648             $self->abort_if_errors();
649              
650             =for test_synopsis my ( $cond, $file );
651             sub Dist::Zilla::Plugin::YourPlugin::do_something;
652             sub Dist::Zilla::Plugin::YourPlugin::do_something_else;
653              
654             =head1 NOTES
655              
656             All the methods defined in the role log items through the C<log> method. C<Dist::Zilla> takes this
657             method from C<Log::Dispatchouli>, the latter uses C<String::Flogger> to process the messages. It
658             means you can use C<String::Flogger> tricks, e. g.:
659              
660             $self->log_error( [ 'oops at %s line %d', $file, $line ] );
661             # [] are shorter than sprintf.
662              
663             Also note how C<Log::Dispatchouli> describes the C<log> method:
664              
665             $logger->log( @messages );
666              
667             and says:
668              
669             Each message is flogged individually, then joined with spaces.
670              
671             So beware. A call
672              
673             $self->log_error( 'error 1', 'error 2' );
674              
675             logs I<one> message "error 1 error 2", I<not> I<two> messages "error 1" and "error 2", and bumps
676             C<error_count> by 1, not 2.
677              
678             =head1 SEE ALSO
679              
680             =over 4
681              
682             =item L<Dist::Zilla>
683              
684             =item L<Dist::Zilla::Role>
685              
686             =item L<Dist::Zilla::Plugin>
687              
688             =item L<Log::Dispatchouli>
689              
690             =item L<String::Flogger>
691              
692             =back
693              
694             =head1 AUTHOR
695              
696             Van de Bugger <van.de.bugger@gmail.com>
697              
698             =head1 COPYRIGHT AND LICENSE
699              
700             Copyright (C) 2015 Van de Bugger
701              
702             License GPLv3+: The GNU General Public License version 3 or later
703             <http://www.gnu.org/licenses/gpl-3.0.txt>.
704              
705             This is free software: you are free to change and redistribute it. There is
706             NO WARRANTY, to the extent permitted by law.
707              
708             =cut