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/\&/\&/g;
+ $text =~ s/</\</g;
+ # for use in attributes we do both just in case.
+ $text =~ s/"/"/g;
+ $text =~ s/'/'/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;
+}
+