summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Metrics/Reporter/ElasticSearch.pm
blob: 1bab2502cc25d2e9e92076620fe7c7112b486e64 (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
103
104
# 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::ElasticSearch;

use strict;
use warnings;

use parent 'Bugzilla::Metrics::Reporter';

use constant DETACH => 1;

sub report {
    my ($self) = @_;

    # build path array and flatten
    my @timers;
    $self->walk_timers(sub {
        my ($timer, $parent) = @_;
        $timer->{id} = scalar(@timers);
        if ($parent) {
            if (exists $timer->{children}) {
                if ($timer->{type} eq 'tmpl') {
                    $timer->{node} = 'tmpl: ' . $timer->{file};
                }
                elsif ($timer->{type} eq 'db') {
                    $timer->{node} = 'db';
                }
                else {
                    $timer->{node} = '?';
                }
            }
            $timer->{path} = [ @{ $parent->{path} }, $parent->{node} ];
            $timer->{parent} = $parent->{id};
        }
        else {
            $timer->{path} = [ ];
            $timer->{node} = $timer->{name};
        }
        push @timers, $timer;
    });

    # calculate timer-only durations
    $self->walk_timers(sub {
        my ($timer) = @_;
        my $child_duration = 0;
        if (exists $timer->{children}) {
            foreach my $child (@{ $timer->{children} }) {
                $child_duration += $child->{duration};
            }
        }
        $timer->{this_duration} = $timer->{duration} - $child_duration;
    });

    # massage each timer
    my $start_time = $self->{times}->{start_time};
    foreach my $timer (@timers) {
        # remove node name and children
        delete $timer->{node};
        delete $timer->{children};

        # show relative times
        $timer->{start_time} = $timer->{start_time} - $start_time;
        delete $timer->{end_time};

        # show times in ms instead of fractional seconds
        foreach my $field (qw( start_time duration this_duration )) {
            $timer->{$field} = sprintf('%.4f', $timer->{$field} * 1000) * 1;
        }
    }

    # remove private data from env
    delete $self->{env}->{user_agent};
    delete $self->{env}->{referer};

    # throw at ES
    require ElasticSearch;
    my $es = ElasticSearch->new(
        servers     => Bugzilla->params->{metrics_elasticsearch_server},
        transport   => 'http',
    );
    # the ElasticSearch module queries the server for a list of nodes and
    # connects directly to a random node. that bypasses our load balancer so we
    # disable that by setting the server list directly.
    $es->transport->servers(Bugzilla->params->{metrics_elasticsearch_server});
    # as the discovered node list is lazy-loaded, increase _refresh_in so it
    # won't call ->refresh_servers()
    $es->transport->{_refresh_in} = 1;
    $es->index(
        index   => Bugzilla->params->{metrics_elasticsearch_index},
        type    => Bugzilla->params->{metrics_elasticsearch_type},
        ttl     => Bugzilla->params->{metrics_elasticsearch_ttl},
        data    => {
            env     => $self->{env},
            times   => \@timers,
        },
    );
}

1;