line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
501
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
2
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
42
|
|
3
|
|
|
|
|
|
|
package Exception::Reporter::Sender::Dir 0.015; |
4
|
|
|
|
|
|
|
# ABSTRACT: a report sender that writes to directories on the filesystem |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
5
|
use parent 'Exception::Reporter::Sender'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
9
|
|
|
|
|
|
|
#pod |
10
|
|
|
|
|
|
|
#pod my $sender = Exception::Reporter::Sender::Dir->new({ |
11
|
|
|
|
|
|
|
#pod root => '/var/error/my-app', |
12
|
|
|
|
|
|
|
#pod }); |
13
|
|
|
|
|
|
|
#pod |
14
|
|
|
|
|
|
|
#pod =head1 OVERVIEW |
15
|
|
|
|
|
|
|
#pod |
16
|
|
|
|
|
|
|
#pod This report sender writes reports to the file system. Given a report with |
17
|
|
|
|
|
|
|
#pod bunch dumpable items, the Dir sender will make a directory and write each item |
18
|
|
|
|
|
|
|
#pod to a file in it, using the ident when practical, and a generated filename |
19
|
|
|
|
|
|
|
#pod otherwise. |
20
|
|
|
|
|
|
|
#pod |
21
|
|
|
|
|
|
|
#pod =cut |
22
|
|
|
|
|
|
|
|
23
|
1
|
|
|
1
|
|
43
|
use Digest::MD5 (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
13
|
|
24
|
1
|
|
|
1
|
|
666
|
use JSON (); |
|
1
|
|
|
|
|
10498
|
|
|
1
|
|
|
|
|
42
|
|
25
|
1
|
|
|
1
|
|
933
|
use Path::Tiny; |
|
1
|
|
|
|
|
12447
|
|
|
1
|
|
|
|
|
84
|
|
26
|
1
|
|
|
1
|
|
556
|
use String::Truncate; |
|
1
|
|
|
|
|
4597
|
|
|
1
|
|
|
|
|
7
|
|
27
|
1
|
|
|
1
|
|
235
|
use Try::Tiny; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
86
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub new { |
30
|
1
|
|
|
1
|
0
|
16
|
my ($class, $arg) = @_; |
31
|
|
|
|
|
|
|
|
32
|
1
|
|
33
|
|
|
5
|
my $root = $arg->{root} || Carp::confess("missing 'root' argument"); |
33
|
|
|
|
|
|
|
|
34
|
1
|
|
|
1
|
|
7
|
use Path::Tiny; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
699
|
|
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
|
|
4
|
$root = path($root); |
37
|
|
|
|
|
|
|
|
38
|
1
|
50
|
33
|
|
|
30
|
if (-e $root && ! -d $root) { |
39
|
0
|
|
|
|
|
0
|
Carp::confess("given root <$root> is not a writable directory"); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
1
|
50
|
|
|
|
43
|
$root->mkpath unless -e $root; |
43
|
|
|
|
|
|
|
|
44
|
1
|
|
|
|
|
46
|
return bless { |
45
|
|
|
|
|
|
|
root => $root, |
46
|
|
|
|
|
|
|
}, $class; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#pod =head2 send_report |
50
|
|
|
|
|
|
|
#pod |
51
|
|
|
|
|
|
|
#pod $dir_reporter->send_report(\@summaries, \%arg, \%internal_arg); |
52
|
|
|
|
|
|
|
#pod |
53
|
|
|
|
|
|
|
#pod This method makes a subdirectory for the report and writes it out. |
54
|
|
|
|
|
|
|
#pod |
55
|
|
|
|
|
|
|
#pod C<%arg> is the same set of arguments given to Exception::Reporter's |
56
|
|
|
|
|
|
|
#pod C method. Arguments that will have an effect include: |
57
|
|
|
|
|
|
|
#pod |
58
|
|
|
|
|
|
|
#pod reporter - the name of the program reporting the exception |
59
|
|
|
|
|
|
|
#pod handled - if true, the reported exception was handled and the user |
60
|
|
|
|
|
|
|
#pod saw a simple error message; adds C<< "handled":true >> |
61
|
|
|
|
|
|
|
#pod to the JSON body of the report |
62
|
|
|
|
|
|
|
#pod |
63
|
|
|
|
|
|
|
#pod C<%internal_arg> contains data produced by the Exception::Reporter using this |
64
|
|
|
|
|
|
|
#pod object. It includes the C of the report and the C calling the |
65
|
|
|
|
|
|
|
#pod reporter. |
66
|
|
|
|
|
|
|
#pod |
67
|
|
|
|
|
|
|
#pod The return value of C is not defined. |
68
|
|
|
|
|
|
|
#pod |
69
|
|
|
|
|
|
|
#pod =cut |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
my $JSON; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub send_report { |
74
|
1
|
|
|
1
|
1
|
5
|
my ($self, $summaries, $arg, $internal_arg) = @_; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# ?!? Presumably this can't really happen, but... you know what they say |
77
|
|
|
|
|
|
|
# about zero-summary incidents, right? -- rjbs, 2012-07-03 |
78
|
1
|
50
|
|
|
|
6
|
Carp::confess("can't report a zero-summary incident!") unless @$summaries; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# We always use this file for internal use. |
81
|
1
|
|
|
|
|
8
|
my %manifest = ('report.json' => { description => 'report metadata' }); |
82
|
|
|
|
|
|
|
my %report = ( |
83
|
|
|
|
|
|
|
guid => $internal_arg->{guid}, |
84
|
1
|
|
|
|
|
8
|
manifest => \%manifest, |
85
|
|
|
|
|
|
|
); |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
|
|
3
|
my $n = 1; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my $safename = sub { |
90
|
9
|
|
|
9
|
|
26
|
my ($name) = @_; |
91
|
|
|
|
|
|
|
# Surely this is sub-optimal: -- rjbs, 2016-07-19 |
92
|
9
|
|
|
|
|
22
|
$name =~ s{\.\.}{DOTDOT}g; |
93
|
9
|
|
|
|
|
18
|
$name =~ s{/}{BACKSLASH}g; |
94
|
9
|
|
|
|
|
22
|
$name =~ s{[^-_0-9a-z.]}{-}gi; |
95
|
|
|
|
|
|
|
|
96
|
9
|
|
|
|
|
13
|
my $base = $name; |
97
|
9
|
|
|
|
|
33
|
$name = "$base-" . $n++ while $manifest{$name}; |
98
|
|
|
|
|
|
|
|
99
|
9
|
|
|
|
|
25
|
return $name; |
100
|
1
|
|
|
|
|
7
|
}; |
101
|
|
|
|
|
|
|
|
102
|
1
|
|
|
|
|
15
|
my $root = $self->{root}->child($internal_arg->{guid}); |
103
|
1
|
|
|
|
|
123
|
$root->mkpath; |
104
|
|
|
|
|
|
|
|
105
|
1
|
|
|
|
|
314
|
my @parts; |
106
|
1
|
|
|
|
|
4
|
GROUP: for my $summary (@$summaries) { |
107
|
6
|
|
|
|
|
2245
|
my @these_parts; |
108
|
|
|
|
|
|
|
|
109
|
6
|
|
|
|
|
18
|
my $t_path = \&path; |
110
|
6
|
100
|
|
|
|
7
|
if (@{ $summary->[1] } > 1) { |
|
6
|
|
|
|
|
23
|
|
111
|
1
|
|
|
|
|
5
|
my $name = $safename->($summary->[0]); |
112
|
1
|
|
|
|
|
4
|
$manifest{$name} = { ident => $summary->[0] }; |
113
|
|
|
|
|
|
|
|
114
|
1
|
|
|
|
|
5
|
$root->child($name)->mkpath; |
115
|
1
|
|
|
|
|
196
|
my $target_path = path($name); |
116
|
|
|
|
|
|
|
|
117
|
1
|
|
|
3
|
|
34
|
$t_path = sub { $target_path->child($_[0]) }; |
|
3
|
|
|
|
|
11
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
6
|
|
|
|
|
11
|
for my $inner (@{ $summary->[1] }) { |
|
6
|
|
|
|
|
14
|
|
121
|
|
|
|
|
|
|
my $file = $t_path->( |
122
|
8
|
|
50
|
|
|
2169
|
$safename->( $inner->{filename} || 'inner' ) |
123
|
|
|
|
|
|
|
); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
$manifest{$file} = { |
126
|
|
|
|
|
|
|
filename => $inner->{filename}, |
127
|
|
|
|
|
|
|
content_type => $inner->{mimetype}, |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
(($inner->{body_is_bytes} && $inner->{charset}) |
130
|
|
|
|
|
|
|
? (charset => $inner->{charset}) |
131
|
8
|
100
|
100
|
|
|
306
|
: ()), |
132
|
|
|
|
|
|
|
}; |
133
|
|
|
|
|
|
|
|
134
|
8
|
100
|
|
|
|
47
|
my $method = $inner->{body_is_bytes} ? 'spew_raw' : 'spew_utf8'; |
135
|
8
|
|
|
|
|
20
|
$root->child($file)->$method($inner->{body}); |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
1
|
50
|
|
|
|
429
|
if ($arg->{handled}) { |
140
|
1
|
|
|
|
|
3
|
$report{handled} = \1; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
1
|
|
|
|
|
3
|
my ($package, $filename, $line) = @{ $internal_arg->{caller} }; |
|
1
|
|
|
|
|
4
|
|
144
|
|
|
|
|
|
|
|
145
|
1
|
|
|
|
|
6
|
$report{reporter} = $arg->{reporter}; |
146
|
1
|
|
|
|
|
5
|
$report{caller} = "$filename line $line ($package)"; |
147
|
|
|
|
|
|
|
|
148
|
1
|
|
33
|
|
|
37
|
$JSON ||= JSON->new->canonical->pretty; |
149
|
1
|
|
|
|
|
34
|
my $json = $JSON->encode(\%report); |
150
|
|
|
|
|
|
|
|
151
|
1
|
|
|
|
|
5
|
$root->child('report.json')->spew_utf8($json); |
152
|
|
|
|
|
|
|
|
153
|
1
|
|
|
|
|
489
|
return; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
1; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
__END__ |