Plack-App-MCCS

 view release on metacpan or  search on metacpan

local/lib/perl5/Plack/Middleware/StackTrace.pm  view on Meta::CPAN

package Plack::Middleware::StackTrace;
use strict;
use warnings;
use parent qw/Plack::Middleware/;
use Devel::StackTrace;
use Devel::StackTrace::AsHTML;
use Scalar::Util qw( refaddr );
use Try::Tiny;
use Plack::Util::Accessor qw( force no_print_errors );

our $StackTraceClass = "Devel::StackTrace";

# Optional since it needs PadWalker
if (try { require Devel::StackTrace::WithLexicals; Devel::StackTrace::WithLexicals->VERSION(0.08); 1 }) {
    $StackTraceClass = "Devel::StackTrace::WithLexicals";
}

sub call {
    my($self, $env) = @_;

    my ($trace, %string_traces, %ref_traces);
    local $SIG{__DIE__} = sub {
        $trace = $StackTraceClass->new(
            indent => 1, message => munge_error($_[0], [ caller ]),
            ignore_package => __PACKAGE__, no_refs => 1,
        );
        if (ref $_[0]) {
            $ref_traces{refaddr($_[0])} ||= $trace;
        }
        else {
            $string_traces{$_[0]} ||= $trace;
        }
        die @_;
    };

    my $caught;
    my $res = try {
        $self->app->($env);
    } catch {
        $caught = $_;
        [ 500, [ "Content-Type", "text/plain; charset=utf-8" ], [ no_trace_error(utf8_safe($caught)) ] ];
    };

    if ($caught) {
        # Try to find the correct trace for the caught exception
        my $caught_trace;
        if (ref $caught) {
            $caught_trace = $ref_traces{refaddr($caught)};
        }
        else {
            # This is not guaranteed to work if multiple exceptions with
            # the same message are thrown.
            $caught_trace = $string_traces{$caught};
        }
        $trace = $caught_trace if $caught_trace;
    }

    if ($trace && ($caught || ($self->force && ref $res eq 'ARRAY' && $res->[0] == 500)) ) {
        my $text = $trace->as_string;
        my $html = $trace->as_html;
        $env->{'plack.stacktrace.text'} = $text;
        $env->{'plack.stacktrace.html'} = $html;
        $env->{'psgi.errors'}->print($text) unless $self->no_print_errors;
        if (($env->{HTTP_ACCEPT} || '*/*') =~ /html/) {
            $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ utf8_safe($html) ]];
        } else {
            $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ utf8_safe($text) ]];
        }
    }

    # break $trace here since $SIG{__DIE__} holds the ref to it, and
    # $trace has refs to Standalone.pm's args ($conn etc.) and
    # prevents garbage collection to be happening.



( run in 1.485 second using v1.01-cache-2.11-cpan-5a3173703d6 )