line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
########################################################################### |
2
|
|
|
|
|
|
|
# basic Perlbal statistics gatherer |
3
|
|
|
|
|
|
|
########################################################################### |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Perlbal::Plugin::Stats; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
2473
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
41
|
|
8
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
35
|
|
9
|
1
|
|
|
1
|
|
6
|
no warnings qw(deprecated); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
45
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
8
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
59
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# setup our package variables |
14
|
|
|
|
|
|
|
our %statobjs; # { svc_name => [ service, statobj ], svc_name => [ service, statobj ], ... } |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# define all stats keys here |
17
|
|
|
|
|
|
|
our @statkeys = qw( files_sent files_reproxied |
18
|
|
|
|
|
|
|
web_requests proxy_requests |
19
|
|
|
|
|
|
|
proxy_requests_highpri ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# called when we're being added to a service |
22
|
|
|
|
|
|
|
sub register { |
23
|
0
|
|
|
0
|
0
|
|
my ($class, $svc) = @_; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# create a stats object |
26
|
0
|
|
|
|
|
|
my $sobj = Perlbal::Plugin::Stats::Storage->new(); |
27
|
0
|
|
|
|
|
|
$statobjs{$svc->{name}} = [ $svc, $sobj ]; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# simple events we count are done here. when the hook on the left side is called, |
30
|
|
|
|
|
|
|
# we simply increment the count of the stat on the right side. |
31
|
0
|
|
|
|
|
|
my %simple = qw( |
32
|
|
|
|
|
|
|
start_send_file files_sent |
33
|
|
|
|
|
|
|
start_file_reproxy files_reproxied |
34
|
|
|
|
|
|
|
start_web_request web_requests |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# create hooks for %simple things |
38
|
0
|
|
|
|
|
|
while (my ($hook, $stat) = each %simple) { |
39
|
0
|
|
|
|
|
|
eval "\$svc->register_hook('Stats', '$hook', sub { \$sobj->{'$stat'}++; return 0; });"; |
40
|
0
|
0
|
|
|
|
|
return undef if $@; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# more complicated statistics |
44
|
|
|
|
|
|
|
$svc->register_hook('Stats', 'backend_client_assigned', sub { |
45
|
0
|
|
|
0
|
|
|
my Perlbal::BackendHTTP $be = shift; |
46
|
0
|
|
|
|
|
|
my Perlbal::ClientProxy $cp = $be->{client}; |
47
|
0
|
|
|
|
|
|
$sobj->{pending}->{"$cp"} = [ gettimeofday() ]; |
48
|
0
|
0
|
|
|
|
|
($cp->{high_priority} ? $sobj->{proxy_requests_highpri} : $sobj->{proxy_requests})++; |
49
|
0
|
|
|
|
|
|
return 0; |
50
|
0
|
|
|
|
|
|
}); |
51
|
|
|
|
|
|
|
$svc->register_hook('Stats', 'backend_response_received', sub { |
52
|
0
|
|
|
0
|
|
|
my Perlbal::BackendHTTP $be = shift; |
53
|
0
|
|
|
|
|
|
my Perlbal::ClientProxy $obj = $be->{client}; |
54
|
0
|
|
|
|
|
|
my $ot = delete $sobj->{pending}->{"$obj"}; |
55
|
0
|
0
|
|
|
|
|
return 0 unless defined $ot; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# now construct data to put in recent |
58
|
0
|
0
|
|
|
|
|
if (defined $obj->{req_headers}) { |
59
|
0
|
|
0
|
|
|
|
my $uri = 'http://' . ($obj->{req_headers}->header('Host') || 'unknown') . $obj->{req_headers}->request_uri; |
60
|
0
|
|
|
|
|
|
push @{$sobj->{recent}}, sprintf('%-6.4f %s', tv_interval($ot), $uri); |
|
0
|
|
|
|
|
|
|
61
|
0
|
0
|
|
|
|
|
shift(@{$sobj->{recent}}) if scalar(@{$sobj->{recent}}) > 100; # if > 100 items, lose one |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
} |
63
|
0
|
|
|
|
|
|
return 0; |
64
|
0
|
|
|
|
|
|
}); |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
return 1; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# called when we're no longer active on a service |
70
|
|
|
|
|
|
|
sub unregister { |
71
|
0
|
|
|
0
|
0
|
|
my ($class, $svc) = @_; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# clean up time |
74
|
0
|
|
|
|
|
|
$svc->unregister_hooks('Stats'); |
75
|
0
|
|
|
|
|
|
delete $statobjs{$svc->{name}}; |
76
|
0
|
|
|
|
|
|
return 1; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# called when we are loaded |
80
|
|
|
|
|
|
|
sub load { |
81
|
|
|
|
|
|
|
# setup a management command to dump statistics |
82
|
|
|
|
|
|
|
Perlbal::register_global_hook("manage_command.stats", sub { |
83
|
0
|
|
|
0
|
|
|
my @res; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# create temporary object for stats storage |
86
|
0
|
|
|
|
|
|
my $gsobj = Perlbal::Plugin::Stats::Storage->new(); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# dump per service |
89
|
0
|
|
|
|
|
|
foreach my $svc (keys %statobjs) { |
90
|
0
|
|
|
|
|
|
my $sobj = $statobjs{$svc}->[1]; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# for now, simply dump the numbers we have |
93
|
0
|
|
|
|
|
|
foreach my $key (sort @statkeys) { |
94
|
0
|
|
|
|
|
|
push @res, sprintf("%-15s %-25s %12d", $svc, $key, $sobj->{$key}); |
95
|
0
|
|
|
|
|
|
$gsobj->{$key} += $sobj->{$key}; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# global stats |
100
|
0
|
|
|
|
|
|
foreach my $key (sort @statkeys) { |
101
|
0
|
|
|
|
|
|
push @res, sprintf("%-15s %-25s %12d", 'total', $key, $gsobj->{$key}); |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
push @res, "."; |
105
|
0
|
|
|
|
|
|
return \@res; |
106
|
0
|
|
|
0
|
0
|
|
}); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# recent requests and how long they took |
109
|
|
|
|
|
|
|
Perlbal::register_global_hook("manage_command.recent", sub { |
110
|
0
|
|
|
0
|
|
|
my @res; |
111
|
0
|
|
|
|
|
|
foreach my $svc (keys %statobjs) { |
112
|
0
|
|
|
|
|
|
my $sobj = $statobjs{$svc}->[1]; |
113
|
0
|
|
|
|
|
|
push @res, "$svc $_" |
114
|
0
|
|
|
|
|
|
foreach @{$sobj->{recent}}; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
|
push @res, "."; |
118
|
0
|
|
|
|
|
|
return \@res; |
119
|
0
|
|
|
|
|
|
}); |
120
|
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
return 1; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# called for a global unload |
125
|
|
|
|
|
|
|
sub unload { |
126
|
|
|
|
|
|
|
# unregister our global hooks |
127
|
0
|
|
|
0
|
0
|
|
Perlbal::unregister_global_hook('manage_command.stats'); |
128
|
0
|
|
|
|
|
|
Perlbal::unregister_global_hook('manage_command.recent'); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# take out all service stuff |
131
|
0
|
|
|
|
|
|
foreach my $statref (values %statobjs) { |
132
|
0
|
|
|
|
|
|
$statref->[0]->unregister_hooks('Stats'); |
133
|
|
|
|
|
|
|
} |
134
|
0
|
|
|
|
|
|
%statobjs = (); |
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
return 1; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# statistics storage object |
140
|
|
|
|
|
|
|
package Perlbal::Plugin::Stats::Storage; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
use fields ( |
143
|
1
|
|
|
|
|
27
|
'files_sent', # files sent from disk (includes reproxies and regular web requests) |
144
|
|
|
|
|
|
|
'files_reproxied', # files we've sent via reproxying (told to by backend) |
145
|
|
|
|
|
|
|
'web_requests', # requests we sent ourselves (no reproxy, no backend) |
146
|
|
|
|
|
|
|
'proxy_requests', # regular requests that went to a backend to be served |
147
|
|
|
|
|
|
|
'proxy_requests_highpri', # same as above, except high priority |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
'pending', # hashref; { "obj" => time_start } |
150
|
|
|
|
|
|
|
'recent', # arrayref; strings of recent URIs and times |
151
|
1
|
|
|
1
|
|
1668
|
); |
|
1
|
|
|
|
|
8
|
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub new { |
154
|
0
|
|
|
0
|
|
|
my Perlbal::Plugin::Stats::Storage $self = shift; |
155
|
0
|
0
|
|
|
|
|
$self = fields::new($self) unless ref $self; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# 0 initialize everything here |
158
|
0
|
|
|
|
|
|
$self->{$_} = 0 foreach @Perlbal::Plugin::Stats::statkeys; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# other setup |
161
|
0
|
|
|
|
|
|
$self->{pending} = {}; |
162
|
0
|
|
|
|
|
|
$self->{recent} = []; |
163
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
return $self; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
1; |