File Coverage

blib/lib/Merror.pm
Criterion Covered Total %
statement 40 117 34.1
branch 11 48 22.9
condition 16 46 34.7
subroutine 7 13 53.8
pod 8 11 72.7
total 82 235 34.8


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Merror - OOP errorhandling with stacktracing ability
6              
7             =head1 VERSION
8              
9             1.4
10              
11             =head1 SYNOPSIS
12            
13             use Merror;
14            
15             function create_error {
16             my $obj = Merror->new(stackdepth => 16);
17             #indicate that an error happened
18             $obj->error(1);
19             #set an error code
20             $obj->ec(-1);
21             #set an error description
22             $obj->et("This is an error message");
23             return($obj);
24             }
25            
26             my $error_obj = create_error();
27             #check if an error occured
28             if($error_obj->error()) {
29             #print out the errorcode (-1)
30             print("Errorcode: " . $obj->ec() ."\n");
31             #print out the error description (This is an error message)
32             print("Error description: " . $obj->et() . "\n");
33             #print out the captured stacktrace
34             $obj->stacktrace();
35             } else {
36             print("No error occured\n");
37             }
38            
39             =head1 DESCRIPTION
40              
41             Merror gives you the ability to C and catch errors in an OOP way. That means if you dont catch errors probably your code will continue running.
42             One C feature of Merror is that it captures a stacktrace when an error occured that you can print out.
43              
44             =head1 METHODS
45              
46             =over 4
47              
48             =item B>B< value, ...)>
49              
50             Constructor.
51              
52             Example:
53             my $obj = Merror->new( stackdepth => 64 );
54            
55             the following options are available:
56              
57             =over 4
58              
59             =item stackdepth
60              
61             Number defining the maximum tracing depth of a stacktrace.
62              
63             # example: define tracingdepth of 64
64             stackdepth => 64
65            
66             =item errorfile
67              
68             Name of a file containing you errormapping, See section ERRORFILE for more information about the syntax of the file.
69              
70             # example: define errorfile C
71             errorfile => C
72            
73             =item ramusage
74              
75             If set to an value other than undef or false the complete mapping of a given errorfile will be held in RAM instead of parsing it new every time you call an errorcode or error descrption.
76              
77             # example: define ramusage
78             ramusage => 1
79            
80             =back
81              
82             =item B
83              
84             Call this method with 1 as parameter C to indicate that an error happened. You can fetch the error state by calling this method without any paramter. It returns 1 if an error occured or 0 if not.
85              
86             Example:
87             my $obj = Merror->new(stackdepth => 10, errorfile => C, ramusage=>0);
88            
89             ...
90            
91             #if you formerly called $obj->error(1) than this query whill return 1
92             if($obj->error()( {
93             ...
94             }
95              
96             =item B
97              
98             If you call this method with a number this will set the errorcode. Call it without any parameter to get the last errorcode back.
99              
100             Example:
101             # set error code -255
102             $obj->ec(-255);
103             # print out the last errorcode
104             print($obj->ec()."\n");
105            
106             =item B
107              
108             Call this method with a string paramter and this string will be set as the error description. Call it without any parameter to get back the last error description.
109              
110             Example:
111             # set error description
112             $obj->et("Fatal unresolvable error occured");
113             # print out the last error description
114             print($obj->et()."\n");
115            
116             =item B
117              
118             This method searches the errorfile for errorCode and sets the errorcode and error description from the mapping
119              
120             Example:
121             # we got the following mapping in our errorfile: 24: Could not connect to given host
122             # set error code and description depending on the mapping
123             $obj->mappedError(24);
124             # print out the errorcode: 24
125             print($obj->ec()."\n");
126             # print out the error description: Could not connect to given host
127             print($obj->et()."\n");
128            
129             =item B
130              
131             Prints out the caputed stacktrace.
132              
133             =item B
134              
135             Returns the captured stacktrace as an array where every element is one level ov the stacktrace
136              
137             Example:
138             my @st = $obj->return_stacktrace();
139             foreach (@st) {
140             print("$_\n");
141             }
142            
143             =item B
144              
145             You can treat this method as an copy-operator for Merror structures. It will copy the complete internal state ob the calling object into an hash reference indicated by C
146              
147             Example:
148             use Merror;
149             my $obj = Merror->new();
150             $obj->ec(13);
151             $obj->et("Test error");
152            
153             # will print out: 13
154             print($obj->ec()."\n");
155             # will print out: Test error
156             print($obj->et()."\n");
157            
158             # now copy the internal state
159             my $obj2 = {};
160             $obj->merror_copy($obj2);
161            
162             # will print out: 13
163             print($obj2->ec()."\n");
164             # will print out: Test error
165             print($obj2->et()."\n");
166            
167             =back
168              
169             =head1 ERRORFILE
170              
171             By defining a file in the constructor parameter C you have the ability to use this file for your error descriptions.
172             The syntax of every line of the file is:
173              
174             [Errorcode]: [Errordescription]
175             -255: Unknown Error occurred
176              
177             Lines starting with a # will be ignored.
178             Every line will be parsed through this regular expression:
179              
180             /^(\d{1,})\s{0,}:\s{0,}(.*)/
181              
182             =head1 BUGS
183            
184             None known
185            
186             =head1 ACKNOWLEDGEMENTS
187              
188             If you find any bugs or got some feature you wish to have implemented please register at C.
189              
190             =head1 COPYRIGHT
191              
192             See README.
193              
194             =head1 AVAILABILITY
195              
196             You can allways get the latest version from CPAN.
197              
198             =head1 AUTHOR
199              
200             Markus Mazurczak
201              
202             =cut
203              
204             package Merror;
205             our $VERSION = '1.4';
206              
207 1     1   28006 use strict;
  1         4  
  1         38  
208 1     1   6 use warnings;
  1         1  
  1         1768  
209              
210             sub new {
211 5     5 1 868 my $invocant = shift;
212 5         16 my %opts = @_;
213 5   33     32 my $class = ref($invocant) || $invocant;
214 5   100     78 my $self = {
      100        
      100        
215             ERROR => 0,
216             EC => 0,
217             ET => "",
218             STACKDEPTH => ($opts{stackdepth} || 64),
219             ERRORFILE => ($opts{errorfile} || undef),
220             RAMUSAGE => ($opts{ramusage} || 0),
221             ERRMAPPING => { },
222             STACK => { },
223             };
224            
225 5         11 bless $self, $class;
226            
227 5 100 100     94 if(defined($self->{ERRORFILE}) && !-r $self->{ERRORFILE}) {
    50 66        
228 1         2 $self->{ERROR} = 1;
229 1         2 $self->{EC} = -255;
230 1         4 $self->{ET} = "Could not read configured error mapping file: " . $self->{ERRORFILE};
231             } elsif(defined($self->{ERRORFILE}) && $self->{RAMUSAGE} != 0) {
232 0         0 $self->parseErrorFile();
233             }
234 5         27 return($self);
235             }
236              
237             sub error {
238 2     2 1 9 my $self = shift;
239 2 100       6 if(@_) {
240 1         3 $self->{ERROR} = 1;
241 1         4 fillstack($self);
242             }
243 1         7 else { return($self->{ERROR}); }
244             }
245              
246             sub ec {
247 2     2 1 3 my $self = shift;
248 2 100       7 if(@_) { $self->{EC} = $_[0]; }
  1         3  
249 1         2 else { $self->{ERROR}=0;return($self->{EC}); }
  1         5  
250             }
251              
252             sub et {
253 2     2 1 3 my $self = shift;
254 2 100       6 if(@_) { $self->{ET} = $_[0]; }
  1         4  
255 1         2 else { $self->{ERROR}=0;return($self->{ET}); }
  1         5  
256             }
257              
258             sub mappedError {
259 0     0 1 0 my $self = shift;
260 0         0 my $errorcode = shift;
261            
262 0 0 0     0 if(!defined($errorcode) || !defined($self->{ERRORFILE})) {
263 0         0 $self->ec(-255);
264 0         0 $self->et("Undefined error occured");
265             } else {
266 0         0 $self->ec($errorcode);
267 0         0 $self->et($self->mapErrorCode($errorcode));
268             }
269             }
270              
271             sub stacktrace {
272 0     0 1 0 my $self = shift;
273 0         0 $self->{ERROR}=0;
274 0         0 foreach my $stacklevel(sort(keys(%{$self->{STACK}}))) {
  0         0  
275 0         0 print("Level: $stacklevel -- File: ");
276 0         0 print($self->{STACK}->{$stacklevel}->{FILE}." -- Pkg: ");
277 0         0 print($self->{STACK}->{$stacklevel}->{PKGNAME}." -- Sub: ");
278 0         0 print($self->{STACK}->{$stacklevel}->{SUBROUTINE}." -- Line: ");
279 0         0 print($self->{STACK}->{$stacklevel}->{LINE}."\n");
280             }
281             }
282              
283             sub return_stacktrace {
284 0     0 1 0 my $self = shift;
285 0         0 my @stack_array;
286 0         0 my $counter = 0;
287 0         0 $self->{ERROR}=0;
288 0         0 foreach my $stacklevel(sort(keys(%{$self->{STACK}}))) {
  0         0  
289 0         0 $stack_array[$counter] = "Level: $stacklevel -- File: ";
290 0         0 $stack_array[$counter + 1] = $self->{STACK}->{$stacklevel}->{FILE}." -- Pkg: ";
291 0         0 $stack_array[$counter + 2] = $self->{STACK}->{$stacklevel}->{PKGNAME}." -- Sub: ";
292 0         0 $stack_array[$counter + 3] = $self->{STACK}->{$stacklevel}->{SUBROUTINE}." -- Line: ";
293 0         0 $stack_array[$counter + 4] = $self->{STACK}->{$stacklevel}->{LINE}."\n";
294 0         0 $counter += 5;
295             }
296 0         0 return(@stack_array);
297             }
298              
299             sub merror_copy {
300 0     0 1 0 my ($self, $to) = @_;
301 0 0       0 if($self->{EC}) { $to->{EC} = $self->{EC}; }
  0         0  
302 0 0       0 if($self->{ET}) { $to->{ET} = $self->{ET}; }
  0         0  
303 0 0       0 if($self->{ERROR}) { $to->{ERROR} = $self->{ERROR}; }
  0         0  
304 0 0       0 if($self->{STACKDEPTH}) { $to->{STACKDEPTH} = $self->{STACKDEPTH}; }
  0         0  
305 0 0       0 if($self->{STACK}) { $to->{STACK} = $self->{STACK}; }
  0         0  
306 0 0       0 if($self->{ERRORFILE}) { $to->{ERRORFILE} = $self->{ERRORFILE}; }
  0         0  
307 0 0       0 if($self->{RAMUSAGE}) { $to->{RAMUSAGE} = $self->{RAMUSAGE}; }
  0         0  
308 0 0       0 if($self->{ERRMAPPING}) { $to->{ERRMAPPING} = $self->{ERRMAPPING}; }
  0         0  
309             }
310              
311             # private method
312             # Captures the stacktrace with a max depth of $self->{STACKDEPTH}
313             sub fillstack {
314 1     1 0 1 my $self = shift;
315 1         8 for(my $i=0; $i<$self->{STACKDEPTH}; $i++) {
316 8 100       20 if(!defined(caller($i))) { return; }
  1         3  
317 7         38 my ($pkgname, $file, $line, $subroutine,) = caller($i);
318 7   50     33 $self->{STACK}->{$i}->{PKGNAME} = ($pkgname || "");
319 7   50     19 $self->{STACK}->{$i}->{FILE} = ($file || "");
320 7   50     19 $self->{STACK}->{$i}->{LINE} = ($line || "");
321 7   50     31 $self->{STACK}->{$i}->{SUBROUTINE} = ($subroutine || "");
322             }
323             }
324              
325             # private method
326             # If user wants to parse the complete error mapping file and save it into ram than this function is called
327             sub parseErrorFile {
328 0     0 0   my $self = shift;
329            
330 0 0         open(MAPFILE, $self->{ERRORFILE}) or die("This should never happen (function: parseErrorFile, file: Merror.pm).");
331 0           while(my $line = ) {
332 0           chomp($line);
333 0 0 0       next if($line =~ /^#/ || $line =~ /^\s{0,}$/);
334 0           my ($number, $desc) = ($line =~ /^(\d{1,})\s{0,}:\s{0,}(.*)/);
335 0 0 0       if(!defined($number) || !defined($desc) || $number =~ /^\s{1,}$/ || $desc =~ /^\s{1,}$/) {
      0        
      0        
336 0           $self->{ERROR} = 1;
337 0           $self->{EC} = -254;
338 0           $self->{ET} = "Syntax error in error mapping file (".$self->{ERRORFILE}.") in line: $.";
339 0           return;
340             }
341 0           $self->{ERRMAPPING}->{$number} = $desc;
342             }
343 0           close(MAPFILE);
344             }
345              
346             # private method
347             # Returns the error description of the given errorcode. If errorcode is undef than an undefined error will be returned.
348             sub mapErrorCode {
349 0     0 0   my $self = shift;
350 0           my $errorcode = shift;
351 0 0         if(!defined($errorcode)) {
352 0           return("Undefined error occured");
353             }
354            
355 0 0         if($self->{RAMUSAGE} != 0) {
356 0   0       return($self->{ERRMAPPING}->{$errorcode} || "Undefined error occured");
357             }
358            
359 0 0         open(MAPFILE, $self->{ERRORFILE}) or die("This should never happen (function: parseErrorFile, file: Merror.pm).");
360 0           while(my $line = ) {
361 0           chomp($line);
362 0 0 0       next if($line =~ /^#/ || $line =~ /^\s{0,}$/);
363 0           my ($number, $desc) = ($line =~ /^(\d{1,})\s{0,}:\s{0,}(.*)/);
364 0 0 0       if($number =~ /^\s{1,}$/ || $desc =~ /^\s{1,}$/) {
365 0           return("Undefined error occured");
366             }
367 0 0         if("$errorcode" eq "$number") {
368 0           return($desc);
369             }
370             }
371 0           close(MAPFILE);
372 0           return("Undefined error occured");
373             }
374              
375             1;