Revision: 51
Author:   matt
Date:     2006-08-06 23:26:07 +0000 (Sun, 06 Aug 2006)

Log Message:
-----------
Error XML with stacktrace now supported (via plugin)

Modified Paths:
--------------
    trunk/etc/axkit.conf
    trunk/lib/AxKit2/Client.pm
    trunk/lib/AxKit2/Utils.pm

Added Paths:
-----------
    trunk/demo/error.xsl
    trunk/plugins/error_xml

Added: trunk/demo/error.xsl
===================================================================
--- trunk/demo/error.xsl        2006-08-05 23:30:40 UTC (rev 50)
+++ trunk/demo/error.xsl        2006-08-06 23:26:07 UTC (rev 51)
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    version="1.0"
+>
+
+<xsl:output method="html"/>
+
+<xsl:template match="bt">
+<tr>
+    <xsl:if test="position() mod 2">
+        <xsl:attribute name="bgcolor">#eeeeee</xsl:attribute>
+    </xsl:if>
+    <td>
+        <xsl:value-of select="@level"/>
+    </td>
+    <xsl:apply-templates/>
+</tr>
+</xsl:template>
+
+<xsl:template match="package|file|line|subroutine">
+    <td>
+        <xsl:apply-templates/>
+    </td>
+</xsl:template>
+
+<xsl:template match="/">
+<html>
+<head>
+<title>Server Error</title>
+<style type="text/css">
+h2, h3, h4, p, i, td, th
+       {
+               font-family: Verdana, Helvetica, sans-serif;
+       }
+      th
+        {
+          color: white;
+        }
+    </style>
+</head>
+<body bgcolor="white">
+<h2>Server Error</h2>
+<p>
+The following error occurred: <xsl:value-of select="/error/msg"/>
+</p>
+
+<h3>Stack Trace:</h3>
+<table border="0" cellpadding="3" cellspacing="0">
+
+<tr bgcolor="blue"><th>Level</th><th>Package</th><th>File</th><th>Line 
#</th><th>Subroutine</th></tr>
+
+    <xsl:apply-templates select="/error/stack_trace/*"/>
+
+</table>
+</body>
+</html>
+
+</xsl:template>
+
+</xsl:stylesheet>

Modified: trunk/etc/axkit.conf
===================================================================
--- trunk/etc/axkit.conf        2006-08-05 23:30:40 UTC (rev 50)
+++ trunk/etc/axkit.conf        2006-08-06 23:26:07 UTC (rev 51)
@@ -6,8 +6,12 @@
 # ten megabytes (ish)
 CacheSize    10000000
 
+Plugin  error_xml
+ErrorStylesheet demo/error.xsl
+StackTrace On
+
 <Server>
-
+    
     Port   8000
     DocumentRoot   /Users/matt/Perl/xml-axkit/demo/xslt
     StylesheetRoot /Users/matt/Perl/xml-axkit/demo/xslt
@@ -58,10 +62,10 @@
         DocumentRoot demo/gallery
         Plugin dir_to_xml
         Plugin demo/gallery
-               ProofSheetColumns       5
-               ImagesPerProofSheet     20
-               GallerySizes            "133 640 800 1024 full"
-               GalleryThumbQuality     preview
+        ProofSheetColumns    3
+        ImagesPerProofSheet  15
+        GallerySizes         "205 800 640 1024 full"
+        GalleryThumbQuality  preview
     </Location>
     
 </Server>

Modified: trunk/lib/AxKit2/Client.pm
===================================================================
--- trunk/lib/AxKit2/Client.pm  2006-08-05 23:30:40 UTC (rev 50)
+++ trunk/lib/AxKit2/Client.pm  2006-08-06 23:26:07 UTC (rev 51)
@@ -69,7 +69,11 @@
         for my $h ($plug->hooks($hook)) {
             $self->log(LOGDEBUG, "$plugin running hook $hook") unless $hook eq 
'logging';
             eval { @r = $plug->$h($self, $conf, @_) };
-            $@ and $self->log(LOGERROR, "FATAL PLUGIN ERROR: $@"), return 
SERVER_ERROR;
+            if ($@) {
+                my $err = $@;
+                $self->log(LOGERROR, "FATAL PLUGIN ERROR: $err");
+                return SERVER_ERROR, $err;
+            }
             next unless @r;
             last MAINLOOP unless $r[0] == DECLINED;
         }
@@ -126,7 +130,7 @@
 sub hook_error {
     my $self = shift;
     $self->headers_out->code(SERVER_ERROR);
-    my ($ret) = $self->run_hooks('error');
+    my ($ret) = $self->run_hooks('error', @_);
     if ($ret != OK) {
         $self->headers_out->header('Content-Type' => 'text/html; 
charset=UTF-8');
         $self->send_http_headers;
@@ -161,7 +165,7 @@
         return 1; # stop
     }
     elsif ($ret == SERVER_ERROR) {
-        $self->hook_error();
+        $self->hook_error($out);
         return 1; # stop
     }
     else {
@@ -179,7 +183,7 @@
         return 1;
     }
     elsif ($ret == SERVER_ERROR) {
-        $self->hook_error();
+        $self->hook_error($out);
         return 1; # stop
     }
     else {

Modified: trunk/lib/AxKit2/Utils.pm
===================================================================
--- trunk/lib/AxKit2/Utils.pm   2006-08-05 23:30:40 UTC (rev 50)
+++ trunk/lib/AxKit2/Utils.pm   2006-08-06 23:26:07 UTC (rev 51)
@@ -5,7 +5,7 @@
 
 use base 'Exporter';
 
-our @EXPORT_OK = qw(uri_encode uri_decode http_date);
+our @EXPORT_OK = qw(uri_encode uri_decode http_date xml_escape);
 
 sub uri_encode {
     my $uri = shift;
@@ -84,4 +84,14 @@
                 $day, $mday, $month, $year+1900, $hour, $min, $sec);
 }
 
+sub xml_escape {
+    my $text = shift;
+    $text =~ s/\&/\&amp;/g;
+    $text =~ s/</\&lt;/g;
+    # for use in attributes we do both just in case.
+    $text =~ s/"/&quot;/g;
+    $text =~ s/'/&apos;/g;
+    return $text;
+}
+
 1;
\ No newline at end of file

Added: trunk/plugins/error_xml
===================================================================
--- trunk/plugins/error_xml     2006-08-05 23:30:40 UTC (rev 50)
+++ trunk/plugins/error_xml     2006-08-06 23:26:07 UTC (rev 51)
@@ -0,0 +1,136 @@
+#!/usr/bin/perl -w
+
+sub init {
+    my $self = shift;
+    
+    $self->register_config('ErrorStylesheet', sub { $self->stylesheet(@_) });
+    $self->register_config('StackTrace', sub { $self->stacktrace(@_) });
+    
+    $SIG{__DIE__} = sub { die AxKit2::StructuredError->new($_[0]) };
+}
+
+sub stylesheet {
+    my ($self, $conf) = (shift, shift);
+    
+    my $key = $self->plugin_name . '::stylesheet';
+    @_ and $conf->notes($key, shift);
+    $conf->notes($key);
+}
+
+sub stacktrace {
+    my ($self, $conf) = (shift, shift);
+    
+    my $key = $self->plugin_name . '::stacktrace';
+    @_ and $conf->notes($key, shift);
+    $conf->notes($key);
+}
+
+sub str_to_bool {
+    my $str = shift;
+    $str =~ /^(y(?:es)?|1|on)$/i and return 1;
+    $str =~ /^(no?|0|off)$/i and return 0;
+    die "Unkown boolean value: $str";
+}
+
+sub hook_error {
+    my $self = shift;
+    my $error = shift;
+    
+    $self->log(LOGDEBUG, "Turning error into XML");
+    
+    my $with_trace = $self->stacktrace($self->config);
+    if ($with_trace) {
+        $with_trace = str_to_bool($with_trace);
+    }
+    my $xml = $error->to_xml($with_trace);
+    
+    my $stylesheet = $self->stylesheet($self->config);
+    if (!$stylesheet) {
+        $self->log(LOGERROR, "Need an ErrorStylesheet to transform the XML");
+        $self->log(LOGERROR, $xml);
+        return DECLINED;
+    }
+    
+    my $input = AxKit2::Processor->new($self, 
$self->client->headers_in->filename);
+    $input->dom($xml);
+    
+    my $out = $input->transform(XSLT($stylesheet));
+    $out->output($self->client);
+    
+    return OK;
+}
+
+package AxKit2::StructuredError;
+
+use AxKit2::Utils qw(xml_escape);
+
+use overload
+    '""'   => \&to_string,
+    'bool' => sub { 1 };
+
+sub new {
+    my $class = shift;
+    my $err = shift;
+    return bless { error => $err, stacktrace => _stack_trace() }, $class;
+}
+
+sub to_string {
+    my $self = shift;
+    $self->{error};
+}
+
+sub _stack_trace {
+    my @stack;
+    my $pos = 2;
+    while (1) {
+        # $package, $filename, $line, $subroutine, $hasargs,
+        # $wantarray, $evaltext, $is_require, $hints, $bitmask
+        my @caller = caller($pos++);
+        last unless @caller;
+        push @stack, [EMAIL PROTECTED];
+    }
+    return [EMAIL PROTECTED];
+}
+
+# <error>
+#      <file>filename</file>
+#      <msg>error message</msg>
+#      <stack_trace>
+#              <bt level="0">
+#                      <file>filename</file>
+#                      <line>line number</line>
+#              </bt>
+#              <bt level="2">
+#              <!--etc-->
+#              </bt>
+#      </stack_trace>
+# </error>
+
+sub to_xml {
+    my $self = shift;
+    my $with_stack = shift;
+    
+    my $stack = $self->{stacktrace};
+    my $msg = $self->{error};
+    my $xml = "<error>\n<file>" . xml_escape($stack->[0]->[1]) . "</file>\n";
+    $xml   .= "<msg>" . xml_escape($msg) . "</msg>\n";
+    
+    if ($with_stack) {
+        $xml .= "<stack_trace>\n";
+        my $level = 0;
+        for my $stack_data (@$stack) {
+            $xml .= "<bt level='$level'>\n";
+            $xml .= "  <package>" . xml_escape($stack_data->[0]) . 
"</package>\n";
+            $xml .= "  <file>" . xml_escape($stack_data->[1]) . "</file>\n";
+            $xml .= "  <line>" . xml_escape($stack_data->[2]) . "</line>\n";
+            $xml .= "  <subroutine>" . xml_escape($stack_data->[3]) . 
"(...)</subroutine>\n";
+            $xml .= "</bt>\n";
+            $level++;
+        }
+        $xml .= "</stack_trace>\n";
+    }
+    
+    $xml .= "</error>\n";
+    return $xml;
+}
+


Reply via email to