File Coverage

blib/lib/Perlbal/Plugin/Stats.pm
Criterion Covered Total %
statement 15 79 18.9
branch 0 12 0.0
condition 0 2 0.0
subroutine 5 14 35.7
pod 0 4 0.0
total 20 111 18.0


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;