summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Metrics/Reporter.pm
blob: 88a08c9b8362224794ef96a0b37571642b492b32 (plain)
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;