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; +} +