1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
package Bugzilla::Metrics::Reporter;
use strict;
use warnings;
use Bugzilla::Constants;
use File::Slurp;
use File::Temp;
use JSON;
# most reporters should detach from the httpd process.
# reporters which do not detach will block completion of the http response.
use constant DETACH => 1;
# class method to start the delivery script in the background
sub background {
my ($class, $collector) = @_;
# we need to remove parent links to avoid looped structures, which
# encode_json chokes on
_walk_timers($collector->{root}, sub { delete $_[0]->{parent} });
# serialisation
my $json = encode_json({ env => $collector->{env}, times => $collector->{root} });
# write to temp filename
my $fh = File::Temp->new( UNLINK => 0 );
if (!$fh) {
warn "Failed to create temp file: $!\n";
return;
}
binmode($fh, ':utf8');
print $fh $json;
close($fh) or die "$fh : $!";
my $filename = $fh->filename;
# spawn delivery worker
my $command = bz_locations()->{'cgi_path'} . "/metrics.pl '$class' '$filename' &";
$ENV{PATH} = '';
system($command);
}
# run the reporter immediately
sub foreground {
my ($class, $collector) = @_;
my $reporter = $class->new({ hashref => { env => $collector->{env}, times => $collector->{root} } });
$reporter->report();
}
sub new {
my ($invocant, $args) = @_;
my $class = ref($invocant) || $invocant;
# load from either a json_filename or hashref
my $self;
if ($args->{json_filename}) {
$self = decode_json(read_file($args->{json_filename}, binmode => ':utf8'));
unlink($args->{json_filename});
}
else {
$self = $args->{hashref};
}
bless($self, $class);
# remove redundant data
$self->walk_timers(sub {
my ($timer) = @_;
$timer->{start_time} = delete $timer->{first_time};
delete $timer->{children}
if exists $timer->{children} && !scalar(@{ $timer->{children} });
});
return $self;
}
sub walk_timers {
my ($self, $callback) = @_;
_walk_timers($self->{times}, $callback, undef);
}
sub _walk_timers {
my ($timer, $callback, $parent) = @_;
$callback->($timer, $parent);
if (exists $timer->{children}) {
foreach my $child (@{ $timer->{children} }) {
_walk_timers($child, $callback, $timer);
}
}
}
sub report {
die "abstract method call";
}
1;
|