| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
68279
|
use strict; |
|
|
1
|
|
|
|
|
13
|
|
|
|
1
|
|
|
|
|
30
|
|
|
2
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
62
|
|
|
3
|
|
|
|
|
|
|
package Exception::Reporter 0.015; |
|
4
|
|
|
|
|
|
|
# ABSTRACT: a generic exception-reporting object |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
|
7
|
|
|
|
|
|
|
#pod |
|
8
|
|
|
|
|
|
|
#pod B This is an experimental refactoring of some long-standing internal |
|
9
|
|
|
|
|
|
|
#pod code. It might get even more refactored. Once I've sent a few hundred |
|
10
|
|
|
|
|
|
|
#pod thousand exceptions through it, I'll remove this warning... |
|
11
|
|
|
|
|
|
|
#pod |
|
12
|
|
|
|
|
|
|
#pod First, you create a reporter. Probably you stick it someplace globally |
|
13
|
|
|
|
|
|
|
#pod accessible, like MyApp->reporter. |
|
14
|
|
|
|
|
|
|
#pod |
|
15
|
|
|
|
|
|
|
#pod my $reporter = Exception::Reporter->new({ |
|
16
|
|
|
|
|
|
|
#pod always_dump => { env => sub { \%ENV } }, |
|
17
|
|
|
|
|
|
|
#pod senders => [ |
|
18
|
|
|
|
|
|
|
#pod Exception::Reporter::Sender::Email->new({ |
|
19
|
|
|
|
|
|
|
#pod from => 'root@example.com', |
|
20
|
|
|
|
|
|
|
#pod to => 'SysAdmins ', |
|
21
|
|
|
|
|
|
|
#pod }), |
|
22
|
|
|
|
|
|
|
#pod ], |
|
23
|
|
|
|
|
|
|
#pod summarizers => [ |
|
24
|
|
|
|
|
|
|
#pod Exception::Reporter::Summarizer::Email->new, |
|
25
|
|
|
|
|
|
|
#pod Exception::Reporter::Summarizer::File->new, |
|
26
|
|
|
|
|
|
|
#pod Exception::Reporter::Summarizer::ExceptionClass->new, |
|
27
|
|
|
|
|
|
|
#pod Exception::Reporter::Summarizer::Fallback->new, |
|
28
|
|
|
|
|
|
|
#pod ], |
|
29
|
|
|
|
|
|
|
#pod }); |
|
30
|
|
|
|
|
|
|
#pod |
|
31
|
|
|
|
|
|
|
#pod Later, some exception has been thrown! Maybe it's an L-based |
|
32
|
|
|
|
|
|
|
#pod exception, or a string, or a L object or who knows what. |
|
33
|
|
|
|
|
|
|
#pod |
|
34
|
|
|
|
|
|
|
#pod try { |
|
35
|
|
|
|
|
|
|
#pod ... |
|
36
|
|
|
|
|
|
|
#pod } catch { |
|
37
|
|
|
|
|
|
|
#pod MyApp->reporter->report_exception( |
|
38
|
|
|
|
|
|
|
#pod [ |
|
39
|
|
|
|
|
|
|
#pod [ exception => $_ ], |
|
40
|
|
|
|
|
|
|
#pod [ request => $current_request ], |
|
41
|
|
|
|
|
|
|
#pod [ uploading => Exception::Reporter::Dumpable::File->new($filename) ], |
|
42
|
|
|
|
|
|
|
#pod ], |
|
43
|
|
|
|
|
|
|
#pod ); |
|
44
|
|
|
|
|
|
|
#pod }; |
|
45
|
|
|
|
|
|
|
#pod |
|
46
|
|
|
|
|
|
|
#pod The sysadmins will get a nice email report with all the dumped data, and |
|
47
|
|
|
|
|
|
|
#pod reports will thread. Awesome, right? |
|
48
|
|
|
|
|
|
|
#pod |
|
49
|
|
|
|
|
|
|
#pod =head1 OVERVIEW |
|
50
|
|
|
|
|
|
|
#pod |
|
51
|
|
|
|
|
|
|
#pod Exception::Reporter takes a bunch of input (the I) and tries to |
|
52
|
|
|
|
|
|
|
#pod figure out how to summarize them and build them into a report to send to |
|
53
|
|
|
|
|
|
|
#pod somebody. Probably a human being. |
|
54
|
|
|
|
|
|
|
#pod |
|
55
|
|
|
|
|
|
|
#pod It does this with two kinds of plugins: summarizers and senders. |
|
56
|
|
|
|
|
|
|
#pod |
|
57
|
|
|
|
|
|
|
#pod The summarizers' job is to convert each dumpable into a simple hashref |
|
58
|
|
|
|
|
|
|
#pod describing it. The senders' job is to take those hashrefs and send them to |
|
59
|
|
|
|
|
|
|
#pod somebody who cares. |
|
60
|
|
|
|
|
|
|
#pod |
|
61
|
|
|
|
|
|
|
#pod =cut |
|
62
|
|
|
|
|
|
|
|
|
63
|
1
|
|
|
1
|
|
512
|
use Data::GUID guid_string => { -as => '_guid_string' }; |
|
|
1
|
|
|
|
|
20846
|
|
|
|
1
|
|
|
|
|
7
|
|
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
#pod =method new |
|
66
|
|
|
|
|
|
|
#pod |
|
67
|
|
|
|
|
|
|
#pod my $reporter = Exception::Reporter->new(\%arg); |
|
68
|
|
|
|
|
|
|
#pod |
|
69
|
|
|
|
|
|
|
#pod This returns a new reporter. Valid arguments are: |
|
70
|
|
|
|
|
|
|
#pod |
|
71
|
|
|
|
|
|
|
#pod summarizers - an arrayref of summarizer objects; required |
|
72
|
|
|
|
|
|
|
#pod senders - an arrayref of sender objects; required |
|
73
|
|
|
|
|
|
|
#pod dumper - a Exception::Reporter::Dumper used for dumping data |
|
74
|
|
|
|
|
|
|
#pod always_dump - a hashref of coderefs used to generate extra dumpables |
|
75
|
|
|
|
|
|
|
#pod caller_level - if given, the reporter will look n frames up; see below |
|
76
|
|
|
|
|
|
|
#pod |
|
77
|
|
|
|
|
|
|
#pod The C hashref bears a bit more explanation. When |
|
78
|
|
|
|
|
|
|
#pod C> is called, each entry in C will be |
|
79
|
|
|
|
|
|
|
#pod evaluated and appended to the list of given dumpables. This lets you make your |
|
80
|
|
|
|
|
|
|
#pod reporter always include some more useful information. |
|
81
|
|
|
|
|
|
|
#pod |
|
82
|
|
|
|
|
|
|
#pod I<...but remember!> The reporter is probably doing its job in a C |
|
83
|
|
|
|
|
|
|
#pod block, which means that anything that might have been changed C-ly in |
|
84
|
|
|
|
|
|
|
#pod your C block will I be the same when evaluated as part of the |
|
85
|
|
|
|
|
|
|
#pod C code. This might not matter often, but keep it in mind when |
|
86
|
|
|
|
|
|
|
#pod setting up your reporter. |
|
87
|
|
|
|
|
|
|
#pod |
|
88
|
|
|
|
|
|
|
#pod In real code, you're likely to create one Exception::Reporter object and make |
|
89
|
|
|
|
|
|
|
#pod it globally accessible through some method. That method adds a call frame, and |
|
90
|
|
|
|
|
|
|
#pod Exception::Reporter sometimes looks at C to get a default. If you want |
|
91
|
|
|
|
|
|
|
#pod to skip those intermedite call frames, pass C. It will be used |
|
92
|
|
|
|
|
|
|
#pod as the number of frames up the stack to look. It defaults to zero. |
|
93
|
|
|
|
|
|
|
#pod |
|
94
|
|
|
|
|
|
|
#pod =cut |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub new { |
|
97
|
3
|
|
|
3
|
1
|
12
|
my ($class, $arg) = @_; |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
my $self = { |
|
100
|
|
|
|
|
|
|
summarizers => $arg->{summarizers}, |
|
101
|
|
|
|
|
|
|
senders => $arg->{senders}, |
|
102
|
|
|
|
|
|
|
dumper => $arg->{dumper}, |
|
103
|
|
|
|
|
|
|
always_dump => $arg->{always_dump}, |
|
104
|
3
|
|
100
|
|
|
28
|
caller_level => $arg->{caller_level} || 0, |
|
105
|
|
|
|
|
|
|
}; |
|
106
|
|
|
|
|
|
|
|
|
107
|
3
|
100
|
|
|
|
15
|
if ($self->{always_dump}) { |
|
108
|
2
|
|
|
|
|
4
|
for my $key (keys %{ $self->{always_dump} }) { |
|
|
2
|
|
|
|
|
12
|
|
|
109
|
|
|
|
|
|
|
Carp::confess("non-coderef entry in always_dump: $key") |
|
110
|
2
|
50
|
|
|
|
11
|
unless ref($self->{always_dump}{$key}) eq 'CODE'; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
3
|
|
66
|
|
|
25
|
$self->{dumper} ||= do { |
|
115
|
1
|
|
|
|
|
15
|
require Exception::Reporter::Dumper::YAML; |
|
116
|
1
|
|
|
|
|
9
|
Exception::Reporter::Dumper::YAML->new; |
|
117
|
|
|
|
|
|
|
}; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Carp::confess("entry in dumper is not an Exception::Reporter::Dumper") |
|
120
|
3
|
50
|
|
|
|
21
|
unless $self->{dumper}->isa('Exception::Reporter::Dumper'); |
|
121
|
|
|
|
|
|
|
|
|
122
|
3
|
|
|
|
|
9
|
for my $test (qw(Summarizer Sender)) { |
|
123
|
6
|
|
|
|
|
18
|
my $class = "Exception::Reporter::$test"; |
|
124
|
6
|
|
|
|
|
18
|
my $key = "\L${test}s"; |
|
125
|
|
|
|
|
|
|
|
|
126
|
6
|
50
|
33
|
|
|
19
|
Carp::confess("no $key given") unless $arg->{$key} and @{ $arg->{$key} }; |
|
|
6
|
|
|
|
|
19
|
|
|
127
|
|
|
|
|
|
|
Carp::confess("entry in $key is not an $class") |
|
128
|
6
|
50
|
|
|
|
20
|
if grep { ! $_->isa($class) } @{ $arg->{$key} }; |
|
|
14
|
|
|
|
|
106
|
|
|
|
6
|
|
|
|
|
16
|
|
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
3
|
|
|
|
|
10
|
bless $self => $class; |
|
132
|
|
|
|
|
|
|
|
|
133
|
3
|
|
|
|
|
17
|
$_->register_reporter($self) for $self->_summarizers; |
|
134
|
|
|
|
|
|
|
|
|
135
|
3
|
|
|
|
|
17
|
return $self; |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
7
|
|
|
7
|
|
14
|
sub _summarizers { return @{ $_[0]->{summarizers} }; } |
|
|
7
|
|
|
|
|
34
|
|
|
139
|
4
|
|
|
4
|
|
8
|
sub _senders { return @{ $_[0]->{senders} }; } |
|
|
4
|
|
|
|
|
14
|
|
|
140
|
|
|
|
|
|
|
|
|
141
|
6
|
|
|
6
|
0
|
40
|
sub dumper { return $_[0]->{dumper} } |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
#pod =method report_exception |
|
144
|
|
|
|
|
|
|
#pod |
|
145
|
|
|
|
|
|
|
#pod $reporter->report_exception(\@dumpables, \%arg); |
|
146
|
|
|
|
|
|
|
#pod |
|
147
|
|
|
|
|
|
|
#pod This method makes the reporter do its job: summarize dumpables and send a |
|
148
|
|
|
|
|
|
|
#pod report. |
|
149
|
|
|
|
|
|
|
#pod |
|
150
|
|
|
|
|
|
|
#pod Useful options in C<%arg> are: |
|
151
|
|
|
|
|
|
|
#pod |
|
152
|
|
|
|
|
|
|
#pod reporter - the program or authority doing the reporting; defaults to |
|
153
|
|
|
|
|
|
|
#pod the calling package |
|
154
|
|
|
|
|
|
|
#pod |
|
155
|
|
|
|
|
|
|
#pod handled - this indicates that this exception has been handled and that |
|
156
|
|
|
|
|
|
|
#pod the user has not seen a terrible crash; senders might use |
|
157
|
|
|
|
|
|
|
#pod this to decide who needs to get woken up |
|
158
|
|
|
|
|
|
|
#pod |
|
159
|
|
|
|
|
|
|
#pod extra_rcpts - this can be an arrayref of email addresses to be used as |
|
160
|
|
|
|
|
|
|
#pod extra envelope recipients by the Email sender |
|
161
|
|
|
|
|
|
|
#pod |
|
162
|
|
|
|
|
|
|
#pod Each entry in C<@dumpables> is expected to look like this: |
|
163
|
|
|
|
|
|
|
#pod |
|
164
|
|
|
|
|
|
|
#pod [ $short_name, $value, \%arg ] |
|
165
|
|
|
|
|
|
|
#pod |
|
166
|
|
|
|
|
|
|
#pod The short name is used for a few things, including identifying the dumps inside |
|
167
|
|
|
|
|
|
|
#pod the report produced. It's okay to have duplicated short names. |
|
168
|
|
|
|
|
|
|
#pod |
|
169
|
|
|
|
|
|
|
#pod The value can, in theory, be I. It can be C, any kind of |
|
170
|
|
|
|
|
|
|
#pod object, or whatever you want to stick in a scalar. It's possible that |
|
171
|
|
|
|
|
|
|
#pod extremely exotic values could confuse the "fallback" summarizer of last resort, |
|
172
|
|
|
|
|
|
|
#pod but for the most part, anything goes. |
|
173
|
|
|
|
|
|
|
#pod |
|
174
|
|
|
|
|
|
|
#pod The C<%arg> entry isn't used for anything by the core libraries that ship with |
|
175
|
|
|
|
|
|
|
#pod Exception::Reporter, but you might want to use it for your own purposes. Feel |
|
176
|
|
|
|
|
|
|
#pod free. |
|
177
|
|
|
|
|
|
|
#pod |
|
178
|
|
|
|
|
|
|
#pod The reporter will try to summarize each dumpable by asking each summarizer, in |
|
179
|
|
|
|
|
|
|
#pod order, whether it C the dumpable. If it can, it will be asked |
|
180
|
|
|
|
|
|
|
#pod to C the dumpable. The summaries are collected into a structure |
|
181
|
|
|
|
|
|
|
#pod that looks like this: |
|
182
|
|
|
|
|
|
|
#pod |
|
183
|
|
|
|
|
|
|
#pod [ |
|
184
|
|
|
|
|
|
|
#pod [ dumpable_short_name => \@summaries ], |
|
185
|
|
|
|
|
|
|
#pod ... |
|
186
|
|
|
|
|
|
|
#pod ] |
|
187
|
|
|
|
|
|
|
#pod |
|
188
|
|
|
|
|
|
|
#pod If a given dumpable can't be dumped by any summarizer, a not-very-useful |
|
189
|
|
|
|
|
|
|
#pod placeholder is put in its place. |
|
190
|
|
|
|
|
|
|
#pod |
|
191
|
|
|
|
|
|
|
#pod The arrayref constructed is passed to the C method of each sender, |
|
192
|
|
|
|
|
|
|
#pod in turn. |
|
193
|
|
|
|
|
|
|
#pod |
|
194
|
|
|
|
|
|
|
#pod =cut |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub report_exception { |
|
197
|
4
|
|
|
4
|
1
|
15993
|
my ($self, $dumpables, $arg) = @_; |
|
198
|
4
|
|
50
|
|
|
12
|
$dumpables ||= []; |
|
199
|
4
|
|
50
|
|
|
10
|
$arg ||= {}; |
|
200
|
|
|
|
|
|
|
|
|
201
|
4
|
|
|
|
|
19
|
my $guid = _guid_string; |
|
202
|
|
|
|
|
|
|
|
|
203
|
4
|
|
|
|
|
1120
|
my @caller = caller( $self->{caller_level} ); |
|
204
|
4
|
|
33
|
|
|
14
|
$arg->{reporter} ||= $caller[0]; |
|
205
|
|
|
|
|
|
|
|
|
206
|
4
|
|
|
|
|
15
|
my $summaries = $self->collect_summaries($dumpables); |
|
207
|
|
|
|
|
|
|
|
|
208
|
4
|
|
|
|
|
14
|
for my $sender ($self->_senders) { |
|
209
|
4
|
|
|
|
|
31
|
$sender->send_report( |
|
210
|
|
|
|
|
|
|
$summaries, |
|
211
|
|
|
|
|
|
|
$arg, |
|
212
|
|
|
|
|
|
|
{ |
|
213
|
|
|
|
|
|
|
guid => $guid, |
|
214
|
|
|
|
|
|
|
caller => \@caller, |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
); |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
4
|
|
|
|
|
123
|
return $guid; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
#pod =method collect_summaries |
|
223
|
|
|
|
|
|
|
#pod |
|
224
|
|
|
|
|
|
|
#pod $reporter->report_exception(\@dumpables); |
|
225
|
|
|
|
|
|
|
#pod |
|
226
|
|
|
|
|
|
|
#pod This method is used by L to convert dumpables into |
|
227
|
|
|
|
|
|
|
#pod summaries. It may be called directly by summarizers through |
|
228
|
|
|
|
|
|
|
#pod C<< $self->reporter->collect_summaries(\@dumpables); >> if your |
|
229
|
|
|
|
|
|
|
#pod summarizers receive dumpables that may be handled by another summarizer. Be |
|
230
|
|
|
|
|
|
|
#pod wary though, because you could possibly create an endless loop... |
|
231
|
|
|
|
|
|
|
#pod |
|
232
|
|
|
|
|
|
|
#pod =cut |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub collect_summaries { |
|
235
|
4
|
|
|
4
|
1
|
10
|
my ($self, $dumpables) = @_; |
|
236
|
|
|
|
|
|
|
|
|
237
|
4
|
|
|
|
|
25
|
my @sumz = $self->_summarizers; |
|
238
|
|
|
|
|
|
|
|
|
239
|
4
|
|
|
|
|
9
|
my @summaries; |
|
240
|
|
|
|
|
|
|
|
|
241
|
4
|
|
|
|
|
9
|
DUMPABLE: for my $dumpable ( |
|
242
|
|
|
|
|
|
|
@$dumpables, |
|
243
|
3
|
|
|
|
|
14
|
map {; [ $_, $self->{always_dump}{$_}->() ] } |
|
244
|
4
|
|
|
|
|
18
|
sort keys %{$self->{always_dump}} |
|
245
|
|
|
|
|
|
|
) { |
|
246
|
15
|
|
|
|
|
46
|
for my $sum (@sumz) { |
|
247
|
48
|
100
|
|
|
|
508
|
next unless $sum->can_summarize($dumpable); |
|
248
|
15
|
|
|
|
|
114
|
push @summaries, [ $dumpable->[0], [ $sum->summarize($dumpable) ] ]; |
|
249
|
15
|
|
|
|
|
372
|
next DUMPABLE; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
0
|
|
|
|
|
0
|
push @summaries, [ |
|
253
|
|
|
|
|
|
|
$dumpable->[0], |
|
254
|
|
|
|
|
|
|
[ { |
|
255
|
|
|
|
|
|
|
ident => "UNKNOWN", |
|
256
|
|
|
|
|
|
|
body => "the entry for <$dumpable->[0]> could not be summarized", |
|
257
|
|
|
|
|
|
|
mimetype => 'text/plain', |
|
258
|
|
|
|
|
|
|
filename => 'unknown.txt', |
|
259
|
|
|
|
|
|
|
} ], |
|
260
|
|
|
|
|
|
|
]; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
4
|
|
|
|
|
23
|
return \@summaries; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
1; |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
__END__ |