1
# This program is copyright 2008-2011 Percona Inc.
2
# Feedback and improvements are welcome.
4
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
8
# This program is free software; you can redistribute it and/or modify it under
9
# the terms of the GNU General Public License as published by the Free Software
10
# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16
# Place, Suite 330, Boston, MA 02111-1307 USA.
18
# ###########################################################################
19
# EventTimeline package $Revision: 6590 $
20
# ###########################################################################
22
# Package: EventTimeLine
23
# EventTimeLine aggregates events that are adjacent to each other.
25
package EventTimeline;
28
use warnings FATAL => 'all';
29
use English qw(-no_match_vars);
30
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
32
Transformers->import(qw(parse_timestamp secs_to_time unix_timestamp));
34
use constant KEY => 0;
35
use constant CNT => 1;
36
use constant ATT => 2;
38
# The best way to see how to use this is to look at the .t file.
40
# %args is a hash containing:
41
# groupby An arrayref of names of properties to group/aggregate by.
42
# attributes An arrayref of names of properties to aggregate.
43
# Aggregation keeps the min, max and sum if it's a numeric
46
my ( $class, %args ) = @_;
47
foreach my $arg ( qw(groupby attributes) ) {
48
die "I need a $arg argument" unless $args{$arg};
51
my %is_groupby = map { $_ => 1 } @{$args{groupby}};
54
groupby => $args{groupby},
55
attributes => [ grep { !$is_groupby{$_} } @{$args{attributes}} ],
60
# Reset the aggregated data, but not anything the code has learned about
62
sub reset_aggregated_data {
64
$self->{results} = [];
67
# Aggregate an event hashref's properties.
69
my ( $self, $event ) = @_;
70
my $handler = $self->{handler};
72
$handler = $self->make_handler($event);
73
$self->{handler} = $handler;
75
return unless $handler;
79
# Return the aggregated results.
82
return $self->{results};
85
# Make subroutines that do things with events.
87
# $event: a sample event
90
# a subroutine with this signature:
93
my ( $self, $event ) = @_;
95
# Ripped off from Regexp::Common::number.
96
my $float_re = qr{[+-]?(?:(?=\d|[.])\d*(?:[.])\d{0,})?(?:[E](?:[+-]?\d+)|)}i;
97
my @lines; # lines of code for the subroutine
99
foreach my $attrib ( @{$self->{attributes}} ) {
100
my ($val) = $event->{$attrib};
101
next unless defined $val; # Can't decide type if it's undef.
103
my $type = $val =~ m/^(?:\d+|$float_re)$/o ? 'num'
104
: $val =~ m/^(?:Yes|No)$/ ? 'bool'
106
MKDEBUG && _d('Type for', $attrib, 'is', $type, '(sample:', $val, ')');
107
$self->{type_for}->{$attrib} = $type;
110
"\$val = \$event->{$attrib};",
111
'defined $val && do {',
113
"\$store = \$last->[ATT]->{$attrib} ||= {};",
116
if ( $type eq 'bool' ) {
117
push @lines, q{$val = $val eq 'Yes' ? 1 : 0;};
120
my $op = $type eq 'num' ? '<' : 'lt';
122
'$store->{min} = $val if !defined $store->{min} || $val '
123
. $op . ' $store->{min};',
125
$op = ($type eq 'num') ? '>' : 'gt';
127
'$store->{max} = $val if !defined $store->{max} || $val '
128
. $op . ' $store->{max};',
130
if ( $type eq 'num' ) {
131
push @lines, '$store->{sum} += $val;';
136
# Build a subroutine with the code.
139
'my ( $event ) = @_;',
140
'my ($val, $last, $store);', # NOTE: define all variables here
141
'$last = $results->[-1];',
144
map { "\$last->[KEY]->[$_] ne (\$event->{$self->{groupby}->[$_]} || 0)" }
145
(0 .. @{$self->{groupby}} -1))
149
map { "(\$event->{$self->{groupby}->[$_]} || 0)" }
150
(0 .. @{$self->{groupby}} -1))
152
' push @$results, $last;',
157
my $results = $self->{results}; # Referred to by the eval
158
my $code = join("\n", @lines);
159
$self->{code} = $code;
161
MKDEBUG && _d('Timeline handler:', $code);
162
my $sub = eval $code;
168
my ( $self, $results, $callback ) = @_;
169
$callback->("# " . ('#' x 72) . "\n");
170
$callback->("# " . join(',', @{$self->{groupby}}) . " report\n");
171
$callback->("# " . ('#' x 72) . "\n");
172
foreach my $res ( @$results ) {
175
if ( ($t = $res->[ATT]->{ts}) && $t->{min} ) {
176
my $min = parse_timestamp($t->{min});
178
if ( $t->{max} && $t->{max} gt $t->{min} ) {
179
my $max = parse_timestamp($t->{max});
180
my $diff = secs_to_time(unix_timestamp($max) - unix_timestamp($min));
188
push @vals, ('', '');
190
$callback->(sprintf("# %19s %7s %3d %s\n", @vals, $res->[CNT], $res->[KEY]->[0]));
195
my ($package, undef, $line) = caller 0;
196
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
197
map { defined $_ ? $_ : 'undef' }
199
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
204
# ###########################################################################
205
# End EventTimeline package
206
# ###########################################################################