Eidolon::Debug - Eidolon debugging facility.


Eidolon documentation Contained in the Eidolon distribution.

Index


Code Index:

NAME

Top

Eidolon::Debug - Eidolon debugging facility.

SYNOPSIS

Top

In CGI/FCGI gateway of your application (index.cgi/index.fcgi) write:

    use Eidolon::Debug;

DESCRIPTION

Top

The Eidolon::Debug package provides an easy way to avoid a confusing Internal Server Error web server message. It sends HTTP header before displaying an error, so you don't need to dig web-server's log to find the cause of the error anymore. Obviously, it will do nothing if error is in your web-server configuration, so if Internal Server Error message still remains, check your web-server configuration. Also, this package displays a stack trace when application dies. It is very useful in application development, so Eidolon::Debug is included in applications by default.

This package doesn't depend on any other Eidolon package, so you can use it outside Eidolon applications too.

While used, Eidolon::Debug hooks global die and warn subroutines, so be careful using other packages, that modify or depend on $SIG{"__DIE__"} and $SIG{"__WARN__"} handlers.

METHODS

Top

start_console()

Start a javascript debugging console. Prints a minimal HTTP header and javascript code, so further error and warning messages could be displayed in nice-looking form.

get_stack()

Get subroutine call stack. Returns reference to array of hashrefs, each hashref stands for one level of the call stack. This hashref contains the following data:

* package

Package name, where error has been occured.

* file

File name, where error has been occured.

* line

Line number, which caused program to die.

* sub

Subroutine name, where error has been occured.

warn($message)

Custom warning handler. $message - warning message to be displayed.

die($message)

Custom error handler. $message - error message to be displayed.

SEE ALSO

Top

Eidolon, Eidolon::Application

LICENSE

Top

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

Top

Anton Belousov, <abel@cpan.org>

COPYRIGHT

Top


Eidolon documentation Contained in the Eidolon distribution.

package Eidolon::Debug;
# ==============================================================================
#
#   Eidolon
#   Copyright (c) 2009, Atma 7
#   ---
#   Eidolon/Debug.pm - debugging facility
#
# ==============================================================================

use warnings;
use strict;

our $VERSION         = "0.02"; # 2009-05-12 05:50:18
my  $console_started = 0;

# ------------------------------------------------------------------------------
# BEGIN()
# package initialization
# ------------------------------------------------------------------------------
BEGIN 
{
    $SIG{"__WARN__"} = \&warn;
    $SIG{"__DIE__"}  = \&die;
}

# ------------------------------------------------------------------------------
# start_console()
# start debug console
# ------------------------------------------------------------------------------
sub start_console
{
    my $script;

    # print HTTP header
    print "Content-Type: text/html; charset=UTF-8\n\n";

    {
        local $/;
        $script = <DATA>;
    }

    print $script;
    $console_started = 1;
}

# ------------------------------------------------------------------------------
# \@ get_stack()
# get call stack
# ------------------------------------------------------------------------------
sub get_stack
{
    my (@stack, $package, $file, $line, $sub, $level);

    # we don't need this function in stack
    $level = 1;

    # walk stack
    while (($package, $file, $line, $sub) = caller($level)) 
    {
        push @stack, 
        {
            "package" => $package,
            "file"    => $file,
            "line"    => $line,
            "sub"     => $sub
        };

        $level++;
    }

    return \@stack;
}

# ------------------------------------------------------------------------------
# print_stack(@$stack)
# print call stack
# ------------------------------------------------------------------------------
sub print_stack
{
    my ($stack, $level, $sublen, $sub);

    $stack = shift;
    $sublen = 0;
  
    # count fields width
    $sublen = length($_->{"sub"}) > $sublen ? length($_->{"sub"}) : $sublen foreach (@$stack);

    # print stack
    foreach (reverse @$stack) 
    {
        printf
        (
            "{ line: '%05d', sub: '%-${sublen}s', file: '%s' },",
            $_->{"line"},
            $sub ? $sub : "main",
            $_->{"file"}
        );

        $sub = $_->{"sub"};
    }
}

# ------------------------------------------------------------------------------
# warn($message)
# warning handler
# ------------------------------------------------------------------------------
sub warn
{
    my ($message, $stack, $phase);

    $message = shift;
    $phase = defined $^S ? "Runtime" : "Compile";

    start_console unless $console_started;

    $message =~ s/[\r\n]//g;
    $message =~ s/'/\\'/g;

    printf "<script>eidolonDebug.addWarning('$phase warning', '$message');</script>";
}

# ------------------------------------------------------------------------------
# die($message)
# die handler
# ------------------------------------------------------------------------------
sub die
{
    my ($message, $stack, $phase);

    $message = shift;
    $phase = defined $^S ? "Runtime" : "Compile";

    # call original die if called from eval block
    CORE::die($message) if (defined $^S && $^S == 1);

    start_console unless $console_started;

    $message =~ s/[\r\n]//g;
    $message =~ s/'/\\'/g;

    print "<script>eidolonDebug.addError('$phase error', '$message', [";

    # don't print stack on compile errors
    if (defined $^S) 
    {
        print_stack(get_stack);
    }

    print "]);</script>";

    exit;
}

1;

__DATA__
<body>
<div id="eidolon-console" style="background-color: #F0F0F0; border: 1px solid #909090; position: absolute; top: 10px; left: 10px; width: 800px; font-family: Verdana, Tahoma; font-size: 12px; text-align: left;"></div>
<script>
    EidolonConsole = function ()
    {
        this.errors   = [];
        this.warnings = [];
        this.details  = 0;
    }

    EidolonConsole.prototype.addError = function (title, message, stack)
    {
        this.errors.push( { "title": title, "message": message, "stack": stack } );
        this.redraw();
    }

    EidolonConsole.prototype.addWarning = function (title, message)
    {
        this.warnings.push( { "title": title, "message": message } );
        this.redraw();
    }

    EidolonConsole.prototype.redraw = function ()
    {
        var obj, i, html, item, k, frame;

        obj = document.getElementById("eidolon-console");
        html = '<div id="eidolon-title" style="background-color: #7B84B0; color: white; padding: 10px; cursor: pointer;" onclick="eidolonDebug.toggleDetails();"><b>Eidolon::Debug</b> - ' + 
            this.errors.length + ' errors, ' + this.warnings.length + ' warnings</div>';

        html += '<div id="eidolon-details" style="display: none; padding: 10px; color: #606060;"></div>';

        obj.innerHTML = html;
        obj = document.getElementById("eidolon-details");
        html = "";

        for (i = 0; i < this.errors.length; i++)
        {
            item = this.errors[i];
            html += '<div style="border-left: 4px solid red; padding: 0 10px 0 10px; margin-bottom: 10px;"><b>' + item.title + ":</b> " + item.message + 
                    '<div style="font-size: 12px;"><pre>';
            
            for (k = 0; k < item.stack.length; k++)
            {
                frame = item.stack[k];

                if (frame)
                    html += frame.line + " " + frame.sub + " " + frame.file + "\n";
            }

            html += "</pre></div></div>";
        }

        for (i = 0; i < this.warnings.length; i++)
        {
            item = this.warnings[i];
            html += '<div style="border-left: 4px solid #7B84B0; padding: 0 10px 0 10px; margin-bottom: 10px;"><b>' + item.title + ":</b> " + item.message + "</div>";
        }

        obj.innerHTML = html;
    }

    EidolonConsole.prototype.toggleDetails = function ()
    {
        var obj = document.getElementById("eidolon-details");

        if (this.details)
        {
            this.details = 0;
            obj.style.display = "none";
        }
        else
        {
            this.details = 1;
            obj.style.display = "block";
        }
    }

    var eidolonDebug = new EidolonConsole();
</script>