File Coverage

blib/lib/Dist/Zilla/Role/ErrorLogger.pm
Criterion Covered Total %
statement 76 76 100.0
branch 21 24 87.5
condition 3 3 100.0
subroutine 10 10 100.0
pod 4 4 100.0
total 114 117 97.4


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