Return to David's home page http://holgerdanske.com/node/454
NAME
Dpchrist::ExceptionTrace - save exception and trace payloads to list
SYNOPSIS
See example/ ExceptionTrace.pl in distribution tree:
#! /usr/bin/perl
use strict;
use warnings;
use Dpchrist::ExceptionTrace qw( :all );
### Application will use hash reference payloads with -label field.
### -label namespace is partitioned for exception handling strategy.
sub foo($)
{
trace {
-label => 'subroutine_entry',
-args => [@_],
-caller => [caller],
};
my $i = shift;
my $retval;
### try:
eval {
### generate some exceptions:
$retval = 1 / $i; # Illegal division by zero... $i == 0
if ($i == 1) { # old-fashioned die()
die 'die within eval within foo';
}
if ($i == 2) { # old-fashioned rethrow
eval { die 'die within eval within eval within foo' };
die if $@;
}
if ($i == 3) {
throw { # application-specific exception
-label => 'application_one',
-s => 'can put more stuff into payload',
-ra => ['this example uses hashrefs'],
-rh => {'with a -label' => 'field'},
-rc => sub { print "hello, world!\n"},
};
}
if ($i == 4) {
throw { # application-specific exception
-label => 'application_two',
};
}
};
### catch:
my $e = $@;
chomp $e;
if ($e) {
if (isa_exception $e) {
my ($payload) = $e->payload;
my $label = $payload->{-label};
trace {
-label => 'caught_class_exception',
-payload => $payload,
};
### use -label namespace to process exceptions from most
### specific to least specific, etc.:
if ($label =~ /^application/) {
### try to fix problem, otherwise propagate:
throw();
}
else {
### propagate everything else
throw();
}
}
else { # not Dpchrist::ExceptionTrace
trace {
-label => 'caught_non_class_exception',
-eval_error => $e,
};
if ($e =~ /division by zero/) {
### try to fix, otherwise convert and propagate:
throw {
-label => 'division_by_zero',
-eval_error => $e,
};
}
### convert and propagate everything else:
else {
throw {
-label => 'other',
-eval_error => $e,
};
}
}
}
### else - no exceptions
trace {-label => 'subroutine_return', -retval => $retval};
return $retval;
}
### main
{
trace {-label => 'program_start', -ARGV => [@ARGV]};
my $exitval = -1;
my $i = 0;
while (1) {
### try:
eval {
my $foo = foo($i);
if (0 < $foo) {
$exitval = 0;
goto EXIT;
}
};
### catch:
my $e = $@;
chomp $e;
if ($e) {
if (isa_exception $e) {
my ($payload) = $e->payload;
my $label = $payload->{-label};
trace {
-label => 'caught_class_exception',
-payload => $payload,
};
if ($label =~ /application/) {
### handle application exceptions:
trace {
-label => $label . '_handled',
-payload => $payload,
};
}
else {
### handle other class exceptions:
trace {
-label => $label . '_handled',
-payload => $payload,
};
}
}
else { # not Dpchrist::ExceptionTrace
trace {
-label => 'caught_non_class_exception',
-eval_error => $e,
};
### handle non-class exceptions:
trace {
-label => 'other_handled',
-eval_error => $e,
};
}
}
### else - no exceptions
$i++;
}
EXIT:
trace {
-label => 'normal_program_exit',
-exitval => $exitval,
};
print Dpchrist::ExceptionTrace->list->as_string;
exit $exitval;
}
Sample run:
2009-11-21 20:32:23 dpchrist@vmlamp ~/Dpchrist-ExceptionTrace/example
$ perl -I../lib ExceptionTrace.pl
$self = bless( {
'-data_dumper_indent' => 1,
'-data_dumper_sortkeys' => 1,
'-list' => [
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 123,
'-package' => 'main',
'-payload' => [
{
'-ARGV' => [],
'-label' => 'program_start'
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 123 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
0
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 90,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'Illegal division by zero at ExceptionTrace.pl line 29.',
'-label' => 'caught_non_class_exception'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 90 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-line' => 95,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'Illegal division by zero at ExceptionTrace.pl line 29.',
'-label' => 'division_by_zero'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 95 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 155,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[3]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 155 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 173,
'-package' => 'main',
'-payload' => [
{
'-label' => 'division_by_zero_handled',
'-payload' => $self->{'-list'}[3]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 173 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
1
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 90,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'die within eval within foo at ExceptionTrace.pl line 32.',
'-label' => 'caught_non_class_exception'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 90 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-line' => 107,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'die within eval within foo at ExceptionTrace.pl line 32.',
'-label' => 'other'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 107 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 155,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[8]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 155 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 173,
'-package' => 'main',
'-payload' => [
{
'-label' => 'other_handled',
'-payload' => $self->{'-list'}[8]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 173 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
2
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 90,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'die within eval within eval within foo at ExceptionTrace.pl line 36.
...propagated at ExceptionTrace.pl line 37.',
'-label' => 'caught_non_class_exception'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 90 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-line' => 107,
'-package' => 'main',
'-payload' => [
{
'-eval_error' => 'die within eval within eval within foo at ExceptionTrace.pl line 36.
...propagated at ExceptionTrace.pl line 37.',
'-label' => 'other'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 107 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 155,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[13]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 155 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 173,
'-package' => 'main',
'-payload' => [
{
'-label' => 'other_handled',
'-payload' => $self->{'-list'}[13]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 173 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
3
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-line' => 40,
'-package' => 'main',
'-payload' => [
{
'-label' => 'application_one',
'-ra' => [
'this example uses hashrefs'
],
'-rc' => sub { "DUMMY" },
'-rh' => {
'with a -label' => 'field'
},
'-s' => 'can put more stuff into payload'
}
],
'-subroutine' => '(eval)',
'-tag' => 'ExceptionTrace.pl 40 (eval) '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 69,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[17]{'-payload'}[0]
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 69 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-is_rethrow' => 1,
'-last_exception' => $self->{'-list'}[17],
'-line' => 77,
'-package' => 'main',
'-payload' => $self->{'-list'}[17]{'-payload'},
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 77 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 155,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[17]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 155 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 160,
'-package' => 'main',
'-payload' => [
{
'-label' => 'application_one_handled',
'-payload' => $self->{'-list'}[17]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 160 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
4
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-line' => 50,
'-package' => 'main',
'-payload' => [
{
'-label' => 'application_two'
}
],
'-subroutine' => '(eval)',
'-tag' => 'ExceptionTrace.pl 50 (eval) '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 69,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[23]{'-payload'}[0]
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 69 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_exception' => 1,
'-is_rethrow' => 1,
'-last_exception' => $self->{'-list'}[23],
'-line' => 77,
'-package' => 'main',
'-payload' => $self->{'-list'}[23]{'-payload'},
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 77 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 155,
'-package' => 'main',
'-payload' => [
{
'-label' => 'caught_class_exception',
'-payload' => $self->{'-list'}[23]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 155 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 160,
'-package' => 'main',
'-payload' => [
{
'-label' => 'application_two_handled',
'-payload' => $self->{'-list'}[23]{'-payload'}[0]
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 160 '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 13,
'-package' => 'main',
'-payload' => [
{
'-args' => [
5
],
'-caller' => [
'main',
'ExceptionTrace.pl',
134
],
'-label' => 'subroutine_entry'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 13 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 116,
'-package' => 'main',
'-payload' => [
{
'-label' => 'subroutine_return',
'-retval' => '0.2'
}
],
'-subroutine' => 'main::foo',
'-tag' => 'ExceptionTrace.pl 116 foo() '
}, 'Dpchrist::ExceptionTrace' ),
bless( {
'-filename' => 'ExceptionTrace.pl',
'-is_trace' => 1,
'-line' => 201,
'-package' => 'main',
'-payload' => [
{
'-exitval' => 0,
'-label' => 'normal_program_exit'
}
],
'-subroutine' => '',
'-tag' => 'ExceptionTrace.pl 201 '
}, 'Dpchrist::ExceptionTrace' )
],
'-maxlen' => 100
}, 'Dpchrist::ExceptionTrace::List' );
DESCRIPTION
Dpchrist::ExceptionTrace is a Perl module for exceptions (e.g.
'eval' and 'die') and program tracing (e.g. debugging print
statements).
Each call to throw() or trace() constructs one
Dpchrist::ExceptionTrace object, and appends that object to a
Dpchrist::ExceptionTrace::List (singleton).
The list can be accessed via the list() class method:
my $list = Dpchrist::ExceptionTrace->list;
The only public interface to the list is the as_string() object
method:
print $list->as_string;
STRUCTURE
Each Dpchrist::ExceptionTrace object has the following attributes:
-filename => SCALAR
The __FILE__ string where throw() or trace() was called.
-line => SCALAR
The __LINE__ number where throw() or trace() was called.
-is_exception => BOOL
Optional. Set to true when object created by throw().
-is_rethrow => BOOL
Optional. Set to true when object created by throw() with no
arguments.
-is_trace => BOOL
Optional. Set to true when object created by trace().
-package => SCALAR
The __PACKAGE__ string where throw() or trace() was called.
-payload => ARRAYREF
A reference to the argument array passed to throw() or trace().
-subroutine => SCALAR
The __SUB__ string where throw() or trace() was called.
-tag => SCALAR
The __TAG__ string where throw() or trace() was called.
CLASS METHODS
_new($)
Dpchrist::ExceptionTrace->_new HASHLIST
Private class method (constructor).
Constructs and returns a Dpchrist::ExceptionTrace object.
Sole argument must be a reference to a hash. The following fields
are required:
-package
-file
-line
-subroutine
-payload
Caller may provide additional fields.
Calls Dpchrist::lib::_fatal on error.
list()
Dpchrist::ExceptionTrace->list
Public class method (read-only class attribute accessor).
Returns the Dpchrist::ExceptionTrace::List object (singleton).
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
OBJECT METHODS
is_exception()
$e->is_exception
Public object method (read-only object attribute accessor).
Returns true (1) if the object was created by throw().
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
is_rethrow()
$e->is_rethrow
Public object method (read-only object attribute accessor).
Returns true (1) if the object was created by a call to throw() with
no arguments (e.g. rethrow).
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
It is an error to call this method if is_exception() is false.
is_trace()
$e->is_trace
Public object method (read-only object attribute accessor).
Returns true (1) if the object was created by trace().
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
filename()
$e->filename
Public object method (read-only object attribute accessor).
Returns the __FILE__ string where throw() or trace() was called.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
last_exception()
$e->last_exception
Public object method (read-only object attribute accessor).
If this object was created by throw() with no arguments, returns the
exception object that was rethrown.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
It is an error to call this method if is_rethrow() is false.
line()
$e->line
Public object method (read-only object attribute accessor).
Returns the __LINE__ number where throw() or trace() was called.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
package()
$e->package
Public object method (read-only object attribute accessor).
Returns the __PACKAGE__ string where throw() or trace() was called.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
payload()
$e->payload
Public object method (read-only object attribute accessor).
Returns argument array given to throw() or trace().
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
subroutine()
$e->subroutine
Public object method (read-only object attribute accessor).
Returns the __SUB__ string where throw() or trace() was called.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
tag()
$e->tag
Public object method (read-only object attribute accessor).
Returns the __TAG__ string throw() or trace() was called.
Accepts no arguments.
Calls Dpchrist::lib::_fatal on error.
FUNCTIONAL INTERFACE SUBROUTINES
isa_exception($)
isa_exception EXPR
Public subroutine (type test).
Returns true (1) if EXPR is a Dpchrist::ExceptionTrace object that
was created by throw(). Otherwise, returns the undefined value.
This method tries to not generate errors, even if the argument is
undefined or the wrong type.
throw
throw LIST
throw
Public subroutine (constructor).
The first form creates a new Dpchrist::ExceptionTrace exception
object, saves a reference to LIST as the payload, stores the object
on the internal list, and throws an exception.
The second form (rethrow) searches the internal list for the last
exception, creates a new Dpchrist::ExceptionTrace object using the
same payload from the last exception, stores the object on the
internal list, and throws an exception.
Calls Dpchrist::lib::_fatal on error.
trace
trace LIST
Public subroutine (constructor).
Creates a new Dpchrist::ExceptionTrace object, saves a reference to
LIST as the payload, stores object on the internal list, and returns
LIST.
Calls Dpchrist::lib::_fatal on error.
EXPORT
None by default.
All of the functional interface subroutines may be imported by using
the ':all' tag:
use Dpchrist::Debug qw( :all );
See 'perldoc Export' for everything in between.
INSTALLATION
perl Makefile.PL
make
make test
make install
DEPENDENCIES
Dpchrist::lib
SEE ALSO
Dpchrist::Tag
AUTHOR
David Paul Christensen dpchrist@holgerdanske.com
COPYRIGHT AND LICENSE
Copyright (C) 2009 by David Paul Christensen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
USA.
| Attachment | Size |
|---|---|
| Dpchrist-ExceptionTrace-1.017.tar_.gz | 21.5 KB |
| Dpchrist-ExceptionTrace-1.017-README.txt | 30.92 KB |
| Dpchrist-ExceptionTrace-List-1.009-README.txt | 2.96 KB |