| blib/lib/Unix/Conf/Err.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 12 | 59 | 20.3 |
| branch | 0 | 22 | 0.0 |
| condition | n/a | ||
| subroutine | 4 | 14 | 28.5 |
| pod | 7 | 7 | 100.0 |
| total | 23 | 102 | 22.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | # Error handling class. To be used by all modules. | ||||||
| 2 | # | ||||||
| 3 | # Copyright Karthik Krishnamurthy |
||||||
| 4 | # | ||||||
| 5 | =head1 NAME | ||||||
| 6 | |||||||
| 7 | Unix::Conf::Err - This module is an internal module for error handling | ||||||
| 8 | purposes. | ||||||
| 9 | |||||||
| 10 | =head1 SYNOPSIS | ||||||
| 11 | |||||||
| 12 | Refer to the documentation of Unix::Conf for creating error objects. | ||||||
| 13 | Accessing the class constructor for Unix::Conf::Err is not preferred | ||||||
| 14 | as the location of the class and consequently its namespace might | ||||||
| 15 | change. The preferred way is | ||||||
| 16 | |||||||
| 17 | use Unix::Conf; | ||||||
| 18 | sub foo () | ||||||
| 19 | { | ||||||
| 20 | return (Unix::Conf::->_err ('chdir')) | ||||||
| 21 | unless (chdir ('/etc')); | ||||||
| 22 | } | ||||||
| 23 | |||||||
| 24 | # or | ||||||
| 25 | |||||||
| 26 | sub foo () | ||||||
| 27 | { | ||||||
| 28 | return ( | ||||||
| 29 | Unix::Conf::->_err ( | ||||||
| 30 | 'object_method', | ||||||
| 31 | 'argument not an object of class BLAH' | ||||||
| 32 | ) | ||||||
| 33 | ) unless (ref ($obj) eq 'BLAH'); | ||||||
| 34 | } | ||||||
| 35 | |||||||
| 36 | In the calling function, save the return value, test it for | ||||||
| 37 | truth, print error message on STDERR and continue. | ||||||
| 38 | |||||||
| 39 | $ret->warn ("Error executing foo ()") | ||||||
| 40 | unless (($ret = foo ())); | ||||||
| 41 | |||||||
| 42 | Increase debugging information to print the cause of error | ||||||
| 43 | and a full stacktrace and die. | ||||||
| 44 | |||||||
| 45 | unless (($ret = foo ())) { | ||||||
| 46 | $ret->debuglevel (2); | ||||||
| 47 | $ret->die ("Error executing foo"); | ||||||
| 48 | } | ||||||
| 49 | |||||||
| 50 | Get state information from the error object and use it to | ||||||
| 51 | print error ourselves instead of using the provided 'warn' | ||||||
| 52 | and 'die' methods. | ||||||
| 53 | |||||||
| 54 | use CGI; | ||||||
| 55 | my $q = new CGI; | ||||||
| 56 | # do stuff | ||||||
| 57 | unless (($ret = foo ())) { | ||||||
| 58 | my $stacktrace = $ret->stacktrace (); | ||||||
| 59 | $stacktrace =~ s/\n/ /g; |
||||||
| 60 | print $q->header ('text/html'), | ||||||
| 61 | $q->start_html ( "Error" ), | ||||||
| 62 | $q->h1 ( "Error" ), | ||||||
| 63 | $q->p ( "Could not execute foo () "), |
||||||
| 64 | $q->p ( "because " ), |
||||||
| 65 | $q->p ( $ret->errmsg () ), | ||||||
| 66 | $q->p ("at "), |
||||||
| 67 | $q->p ( $ret->where () ), | ||||||
| 68 | $q->p ($stacktrace); | ||||||
| 69 | $q->end_html; | ||||||
| 70 | exit; | ||||||
| 71 | } | ||||||
| 72 | |||||||
| 73 | =head1 DESCRIPTION | ||||||
| 74 | |||||||
| 75 | A Unix::Conf::Err object saves the state of the call stack at the | ||||||
| 76 | time its creation. The idea behind a Unix::Conf::Err object style | ||||||
| 77 | error handling is allowing the caller to decide how to handle the | ||||||
| 78 | error without using eval blocks around all Unix::Conf::* library | ||||||
| 79 | calls. The error object can be used to throw exceptions too, as the | ||||||
| 80 | string operator is overloaded to return the error string, depending | ||||||
| 81 | on the debuglevel. | ||||||
| 82 | |||||||
| 83 | =cut | ||||||
| 84 | |||||||
| 85 | package Unix::Conf::Err; | ||||||
| 86 | |||||||
| 87 | 1 | 1 | 8 | use 5.6.0; | |||
| 1 | 3 | ||||||
| 1 | 34 | ||||||
| 88 | 1 | 1 | 4 | use strict; | |||
| 1 | 1 | ||||||
| 1 | 21 | ||||||
| 89 | 1 | 1 | 4 | use warnings; | |||
| 1 | 2 | ||||||
| 1 | 546 | ||||||
| 90 | |||||||
| 91 | =over 4 | ||||||
| 92 | |||||||
| 93 | =item new () | ||||||
| 94 | |||||||
| 95 | Arguments | ||||||
| 96 | PREFIX, | ||||||
| 97 | ERRMSG, | ||||||
| 98 | |||||||
| 99 | Unix::Conf::Err class constructor. If ERRMSG is not specified, a | ||||||
| 100 | stringified version of "$!" is used. Using Unix::Conf::Err->new is | ||||||
| 101 | deprecated. The preferred way to create a Unix::Conf::Err object is | ||||||
| 102 | to use the Unix::Conf->_err method. Call Unix::Conf->_err () at the | ||||||
| 103 | point of error so that it will store error data/stack at the time of | ||||||
| 104 | error to be used later. | ||||||
| 105 | |||||||
| 106 | =cut | ||||||
| 107 | |||||||
| 108 | sub new | ||||||
| 109 | { | ||||||
| 110 | 0 | 0 | 1 | my $class = shift; | |||
| 111 | 0 | my $errobj = {}; | |||||
| 112 | 0 | $errobj->{DEBUGLEVEL} = 0; | |||||
| 113 | 0 | ($errobj->{PREFIX}, $errobj->{ERRMSG}) = @_; | |||||
| 114 | 0 | 0 | $errobj->{ERRMSG} = "$!" unless ($errobj->{ERRMSG}); | ||||
| 115 | 0 | my $ctr = 0; | |||||
| 116 | # store the stack context at time of constructor | ||||||
| 117 | 0 | while (($errobj->{STACK}[$ctr]{PACKAGE}, $errobj->{STACK}[$ctr]{FILE}, $errobj->{STACK}[$ctr]{LINE}, $errobj->{STACK}[$ctr]{SUB}) = caller ($ctr)) { | |||||
| 118 | 0 | $ctr++; | |||||
| 119 | } | ||||||
| 120 | 0 | return (bless ($errobj, $class)); | |||||
| 121 | } | ||||||
| 122 | |||||||
| 123 | =item debuglevel () | ||||||
| 124 | |||||||
| 125 | Arguments | ||||||
| 126 | DEBUGLEVEL, | ||||||
| 127 | |||||||
| 128 | This method can be invoked through both a class and object. When | ||||||
| 129 | invoked through Unix::Conf, it sets the class wide debuglevel to | ||||||
| 130 | the argument. When invoked through an object, it sets only the | ||||||
| 131 | object private debuglevel to the argument. In case both debuglevels | ||||||
| 132 | are set, error message is printed at the maximum of the class wide | ||||||
| 133 | debuglevel and object specific debuglevel. Valid values for | ||||||
| 134 | DEBUGLEVEL are 0, 1, and 2. At level 0 only only the string passed | ||||||
| 135 | to warn ()/die () methods are printed. At 1, the output of | ||||||
| 136 | errmsg () and where () is added. At level 2, the output of | ||||||
| 137 | stacktrace () is added to the output. | ||||||
| 138 | |||||||
| 139 | =cut | ||||||
| 140 | |||||||
| 141 | my $Debug_Level = 0; | ||||||
| 142 | sub debuglevel | ||||||
| 143 | { | ||||||
| 144 | 0 | 0 | 1 | my ($self, $d) = @_; | |||
| 145 | 0 | 0 | if (defined ($d)) { | ||||
| 146 | # sanity check | ||||||
| 147 | 0 | 0 | $d = 2 if ($d > 2); | ||||
| 148 | 0 | 0 | $d = 0 if ($d < 0); | ||||
| 149 | 0 | 0 | if (ref ($self)) { | ||||
| 150 | 0 | $self->{DEBUGLEVEL} = $d; | |||||
| 151 | } | ||||||
| 152 | else { | ||||||
| 153 | 0 | $Debug_Level = $d; | |||||
| 154 | } | ||||||
| 155 | 0 | return ($d); | |||||
| 156 | } | ||||||
| 157 | # whichever is greater must have been set. so return that one. | ||||||
| 158 | return ( | ||||||
| 159 | 0 | 0 | $Debug_Level > $self->{DEBUGLEVEL} ? $Debug_Level : $self->{DEBUGLEVEL} | ||||
| 160 | ); | ||||||
| 161 | } | ||||||
| 162 | |||||||
| 163 | =item where () | ||||||
| 164 | |||||||
| 165 | Prints information about the stack frame in which the error occured | ||||||
| 166 | along with the line number and file. | ||||||
| 167 | |||||||
| 168 | =cut | ||||||
| 169 | |||||||
| 170 | sub where | ||||||
| 171 | { | ||||||
| 172 | 0 | 0 | 1 | my $self = $_[0]; | |||
| 173 | 0 | return ("in $self->{STACK}[1]{SUB}() at line $self->{STACK}[0]{LINE} in $self->{STACK}[0]{FILE}\n"); | |||||
| 174 | } | ||||||
| 175 | |||||||
| 176 | =item why () | ||||||
| 177 | |||||||
| 178 | Prints "PREFIX: ERRMSG". | ||||||
| 179 | |||||||
| 180 | =cut | ||||||
| 181 | |||||||
| 182 | sub why | ||||||
| 183 | { | ||||||
| 184 | 0 | 0 | 1 | my $self = $_[0]; | |||
| 185 | 0 | return ("$self->{PREFIX}: $self->{ERRMSG}\n"); | |||||
| 186 | } | ||||||
| 187 | |||||||
| 188 | =item stacktrace () | ||||||
| 189 | |||||||
| 190 | Prints the complete stacktrace information at the time of creation | ||||||
| 191 | of the object. | ||||||
| 192 | |||||||
| 193 | =cut | ||||||
| 194 | |||||||
| 195 | sub stacktrace | ||||||
| 196 | { | ||||||
| 197 | 0 | 0 | 1 | my $self = $_[0]; | |||
| 198 | 0 | my $errmsg; | |||||
| 199 | # caller invoked in _err returns 2 extra stack frames. don't know why | ||||||
| 200 | # need to debug later | ||||||
| 201 | 0 | my ($ctr, $stacklength) = (1, scalar (@{$self->{STACK}}) - 2); | |||||
| 0 | |||||||
| 202 | 0 | while ($ctr <= $stacklength) { | |||||
| 203 | 0 | $errmsg .= "$self->{STACK}[$ctr]{SUB}() called at line $self->{STACK}[$ctr]{LINE} in $self->{STACK}[$ctr]{FILE}\n"; | |||||
| 204 | 0 | $ctr++; | |||||
| 205 | } | ||||||
| 206 | 0 | return $errmsg; | |||||
| 207 | } | ||||||
| 208 | |||||||
| 209 | =item warn () | ||||||
| 210 | |||||||
| 211 | Arguments | ||||||
| 212 | ERRMSG, | ||||||
| 213 | |||||||
| 214 | Prints ERRMSG to STDERR. | ||||||
| 215 | |||||||
| 216 | =cut | ||||||
| 217 | |||||||
| 218 | # Arguments: errstr (optional) | ||||||
| 219 | sub warn (;$) | ||||||
| 220 | { | ||||||
| 221 | 0 | 0 | 1 | warn (&__stringify); | |||
| 222 | } | ||||||
| 223 | |||||||
| 224 | =item die () | ||||||
| 225 | |||||||
| 226 | Arguments | ||||||
| 227 | ERRMSG, | ||||||
| 228 | |||||||
| 229 | Prints ERRMSG to STDERR and die's. | ||||||
| 230 | |||||||
| 231 | =cut | ||||||
| 232 | |||||||
| 233 | # Arguments: errstr (optional) | ||||||
| 234 | sub die (;$) | ||||||
| 235 | { | ||||||
| 236 | 0 | 0 | 1 | die (&__stringify); | |||
| 237 | } | ||||||
| 238 | |||||||
| 239 | # Overloaded functions | ||||||
| 240 | 1 | 8 | use overload '""' => \&__interpret_as_string, | ||||
| 241 | 'bool' => \&__interpret_as_bool, | ||||||
| 242 | 1 | 1 | 1508 | 'eq' => \&__interpret_as_string; | |||
| 1 | 1054 | ||||||
| 243 | |||||||
| 244 | sub __interpret_as_string | ||||||
| 245 | { | ||||||
| 246 | 0 | 0 | my $self = shift; | ||||
| 247 | 0 | return (__stringify ($self)); | |||||
| 248 | } | ||||||
| 249 | |||||||
| 250 | # If the PREFIX key exists then the constructor has been called. | ||||||
| 251 | sub __interpret_as_bool | ||||||
| 252 | { | ||||||
| 253 | 0 | 0 | my $self = shift; | ||||
| 254 | #return (exists ($self->{PREFIX}) ? undef : 1); | ||||||
| 255 | 0 | 0 | return (exists ($self->{PREFIX}) ? 0 : 1); | ||||
| 256 | } | ||||||
| 257 | |||||||
| 258 | sub __stringify ($;$) | ||||||
| 259 | { | ||||||
| 260 | 0 | 0 | my ($self, $errstr) = @_; | ||||
| 261 | |||||||
| 262 | # The whole error message is constructed in $errmsg and returned | ||||||
| 263 | 0 | my $errmsg = ""; | |||||
| 264 | |||||||
| 265 | # if argument is present get it in $errmsg. it is usually present when | ||||||
| 266 | # called from the die/warn methods | ||||||
| 267 | 0 | 0 | if ($errstr) { | ||||
| 268 | 0 | $errmsg .= "$errstr\n"; | |||||
| 269 | } | ||||||
| 270 | |||||||
| 271 | # when debuglevel is 1 and above include reason and point of error | ||||||
| 272 | 0 | 0 | $self->debuglevel () >= 1 && do { | ||||
| 273 | # $errmsg might be empty because no argument was passed to die/warn | ||||||
| 274 | # meth or because __stringify was called from the string overload | ||||||
| 275 | # handler. | ||||||
| 276 | 0 | 0 | $errmsg .= "\nbecause\n" | ||||
| 277 | if ($errmsg); | ||||||
| 278 | 0 | $errmsg .= &why.&where; | |||||
| 279 | }; | ||||||
| 280 | 0 | 0 | $self->debuglevel () == 2 && do { | ||||
| 281 | 0 | $errmsg .= "\nPrinting stack backtrace\n"; | |||||
| 282 | 0 | $errmsg .= &stacktrace; | |||||
| 283 | }; | ||||||
| 284 | 0 | return ($errmsg); | |||||
| 285 | } | ||||||
| 286 | |||||||
| 287 | 1; | ||||||
| 288 | __END__ |