Dist::Zilla::Role::ErrorLogger(3pm)

NAME
Dist::Zilla::Role::ErrorLogger - Have error logging capabilities in your Dist::Zilla plugin

VERSION
Version v0.9.0, released on 2015-10-26 21:04 UTC.

WHAT?
"Dist-Zilla-Role-ErrorLogger" is a "Dist::Zilla" role. It provides "log_error", "abort", and "abort_if_errors" methods to consuming plugins.

This is "Dist::Zilla::Role::ErrorLogger" role documentation. Read this if you want to have error logging capabilities in your Dist::Zilla plugin.

SYNOPSIS
    package Dist::Zilla::Plugin::YourPlugin;
    use Moose;
    use namespace::autoclean;
    with 'Dist::Zilla::Role::Plugin';
    with 'Dist::Zilla::Role::ErrorLogger';

    sub method {
        my $self = shift( @_ );

        if ( $cond ) { $self->log_error( 'error message' ); };

        do_something or $self->log_error( 'another error message' );

        while ( $cond ) {
            do_something_else or $self->log_error( 'error message' ) and next;
            ...;
        };

        $self->log_errors_in_file(
            $file,
            1 => 'error message',           # Error at file line 1.
            5 => 'another error message',   # Error at file line 5.
        );

        $self->abort_if_errors( 'errors found' );
    };

    __PACKAGE__->meta->make_immutable;
    1;

DESCRIPTION
The role extends standard "Dist::Zilla" logging capabilities with few methods a bit more convenient for reporting (multiple) errors than brutal "log_fatal". See "WHY?" for more details.

The role requires "log" method in the consumer.

OBJECT ATTRIBUTES

  error_count
    $int = $self->error_count;

"Int", read-only. Number of logged errors (i. e. number of made "log_error" calls).

OBJECT METHODS

  log_error
    $self->log_error( @items );
    $self->log_error( \%args, @items );

This method calls "log" method, passing all the arguments, and increments value of "error_count" attribute. The method returns true value, so can be used in following constructs:

    while ( ... ) {
        do_something or $self->log_error( 'message' ) and next;
        ...
    };

  abort
    $self->abort( @items );
    $self->abort( \%args, @items );

This is an attempt to workaround "log_fatal" drawback <https://github.com/rjbs/Dist-Zilla/issues/397>: in contrast to "log_fatal", "abort" guarantees the message (which can be quite long) appears on the screen only once.

The method log the message (via "log"), then flush "STDOUT", then throws an exception of "Dist::Zilla::Role::ErrorLogger::Exception::Abort" class (which being stringified gives short message "Aborting...\n").

  abort_if_error

  abort_if_errors
    $self->abort_if_errors( @items );
    $self->abort_if_errors( \%args, @items );

If there was any errors (i. e. "error_count" is greater than zero), the logs all the arguments and aborts execution. Both actions (logging and aborting) are implemented by calling "abort".

"abort_if_error" is an alias for "abort_if_errors".

  log_errors_in_file
The method intended to report errors against a file. It prints file name (and colon after it), then prints line-numbered file content annotated by error messages. The method does not print entire file content, but only error lines with surrounding context (2 lines above and below each error line).

    $self->log_errors_in_file(
        $file,
        $linenum1 => $message1,
        $linenum2 => $message2,
        $linenum3 => [ $message3a, $message3b, ... ],
        ...
    );

$file should be a "Dist::Zilla" file (e. g. "Dist::Zilla::File::OnDisk", "Dist::Zilla::File::InMemory", or does role "Dist::Zilla::Role::File").

Errors are specified by pairs "$linenum => $message", where $linenum is a number of problem line (one-based), and $message is an error message ("Str") or array of messages ("ArrayRef[Str]"). Order of errors does not matter usually. However, if errors are associated with the same line (the same line number may appear multiple times), they will be printed in order of appearance.

Zero or negative line numbers, or line numbers beyond the last line are invalid. Messages associated with invalid line numbers are reported in unspecified way.

Normally, the method prints all the information by calling "log_error" method and returns a positive integer. However, If any invalid line numbers are specified, the method returns negative integer. If no errors are specified, the method prints "No errors found at file." by calling "log" (not "log_error"!) and returns zero.

TODO: Example.

WHY?
"Dist::Zilla" limits logging capabilities with 3 logging levels available in plugins through "log_debug", "log", and "log_fatal" methods. Debug level messages are turned off by default, the first fatal message terminates "Dist::Zilla". This is simple, but sometimes you may want to report all the errors, instead of stopping at the first found one. In such a case "log_fatal" cannot be used, obviously. There are few alternatives:

Collect error messages in an array, then report all the errors with single "log_fatal" call:

    my @errors;
    ...
    push( @errors, ... );
    ...
    if ( @errors ) {
        $self->log_fatal( join( "\n", @errors ) );
    };

This works, but current implementation of "log_fatal" has a disadvantage: it prints the message twice, so output looks ugly. (See message handling in log_fatal is suboptimal <https://github.com/rjbs/Dist-Zilla/issues/397>.)

Another approach is reporting each error immediately with "log", counting number of reported errors, and calling "log_fatal" once at the end:

    my $error_count = 0;
    ...
    $self->log( 'error' );
    ++ $error_count;
    ...
    if ( $error_count ) {
        $self->log_fatal( 'Aborting...' );
    };

This works, but incrementing the counter after each "log" call is boring and error-prone. "Dist-Zilla-Role-ErrorLogger" role automates it, making plugin code shorter and more readable:

    with 'Dist-Zilla-Role-ErrorLogger';
    ...
    $self->log_error( 'error' );
    ...
    $self->abort_if_errors();

NOTES
All the methods defined in the role log items through the "log" method. "Dist::Zilla" takes this method from "Log::Dispatchouli", the latter uses "String::Flogger" to process the messages. It means you can use "String::Flogger" tricks, e. g.:

    $self->log_error( [ 'oops at %s line %d', $file, $line ] );
        #   [] are shorter than sprintf.

Also note how "Log::Dispatchouli" describes the "log" method:

    $logger->log( @messages );

and says:

    Each message is flogged individually, then joined with spaces.

So beware. A call

    $self->log_error( 'error 1', 'error 2' );

logs one message "error 1 error 2", not two messages "error 1" and "error 2", and bumps "error_count" by 1, not 2.

SEE ALSO

Dist::Zilla
Dist::Zilla::Role
Dist::Zilla::Plugin
Log::Dispatchouli
String::Flogger

AUTHOR
Van de Bugger <van.de.bugger@gmail.com>

COPYRIGHT AND LICENSE
Copyright (C) 2015 Van de Bugger

License GPLv3+: The GNU General Public License version 3 or later <http://www.gnu.org/licenses/gpl-3.0.txt>.

This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.