File Coverage

blib/lib/Guard/Stats.pm
Criterion Covered Total %
statement 80 80 100.0
branch 16 18 88.8
condition 1 2 50.0
subroutine 21 21 100.0
pod 13 13 100.0
total 131 134 97.7


line stmt bran cond sub pod time code
1 4     4   140022 use strict;
  4         10  
  4         321  
2 4     4   23 use warnings;
  4         8  
  4         460  
3              
4             package Guard::Stats;
5              
6             =head1 NAME
7              
8             Guard::Stats - Create guard objects and gather averall usage statistics from them.
9              
10             =head1 SYNOPSIS
11              
12             Suppose we have a long-running application making heavy use of closures,
13             and need to monitor the number of executed, not executed, and gone subrefs.
14              
15             So we put a guard into each closure to update the statistics:
16              
17             # in initial section
18             use Guard::Stats;
19             my $stat = Guard::Stats->new;
20              
21             # when running
22             my $guard = $stat->guard;
23             my $callback = sub {
24             $guard->finish("taken route 1");
25             # now do useful stuff
26             };
27             # ... do whatever we need and call $callback eventually
28              
29             # in diagnostic procedures triggered by an external event
30             my $data = $stat->get_stat;
31             warn "$data->{running} callbacks still waiting to be executed";
32              
33             Of course, alive/dead counts of any objects (not only sub refs) may be
34             monitored in a similar way.
35              
36             =head1 DESCRIPTION
37              
38             A guard is a special object that does something useful in destructor, typically
39             freeing a resource or lock. These guards however don't free anything. Instead,
40             they call home to keep their master (YOU) informed.
41              
42             =head2 The classes
43              
44             Guard::Stats is a long-lived object that spawns guards and
45             gathers statistical information.
46              
47             Its public methods are guard() and various statistic getters.
48              
49             Guard::Stats::Instance is the guard. When it is DESTROYed, it signals the stat
50             object which created it.
51              
52             Its public methods are end( [$result] ) and is_done().
53              
54             =head2 The counters
55              
56             When a guard is created, the C counter increases. When it's detroyed,
57             C counter increases. C = C - C is the number of
58             guards that still exist.
59              
60             Additionally, guards implement a C method which indicates that
61             the action associates with the guard is complete. Typically a guard should
62             be destroyed soon afterwards. The guards for which neither DESTROY nor
63             end were called are considered C (this is used in C).
64              
65             The full matrix or DESTROY()/end() combinations is as follows:
66              
67             DESTROY: * 0 1
68             end:* total+ alive dead
69             end:0 ? running broken+
70             end:1 done+ zombie complete+
71              
72             A "+" marks values directly measured by Guard::Stats. They all happen to be
73             monotonous. Other statistics are derived from these.
74              
75             Note that counter showing end() NOT called regardless of DESTROY() does not
76             have a meaningful name (yet?).
77              
78             =head2 Running count callback
79              
80             Whenever number of guards in the C state passes given level,
81             a function may be called. This can be used to monitor load, prevent
82             uncontrolled memory usage growth, etc.
83              
84             See C below.
85              
86             =head1 METHODS
87              
88             =cut
89              
90             our $VERSION = 0.03;
91              
92 4     4   30 use Carp;
  4         11  
  4         342  
93 4     4   2550 use Guard::Stats::Instance;
  4         10  
  4         406  
94              
95             my @values;
96 4     4   270 BEGIN { @values = qw( total done complete broken ) };
97              
98 4     4   5127 use fields qw(guard_class time_stat results on_level), @values;
  4         8432  
  4         28  
99              
100             =head2 new (%options)
101              
102             %options may include:
103              
104             =over
105              
106             =item * time_stat - an object or class to store time statistics. The class
107             should support C and C operations for this to work.
108             Suitable candidates are L and
109             L (both have sublinear memory usage).
110              
111             =item * guard_class - packge name to override default guard class. See
112             "overriding guard class" below.
113              
114             =back
115              
116             =cut
117              
118             sub new {
119 5     5 1 1414 my $class = shift;
120 5         16 my %opt = @_;
121              
122 5         26 my $self = fields::new($class);
123 5 100       11780 if ( my $stat = $opt{time_stat} ) {
124 3 100       77 $stat->can("add_data")
125             or croak( __PACKAGE__.": time_stat object $stat doesn't have add_data() method" );
126 2 100       14 $self->{time_stat} = ref $stat ? $stat : $stat->new;
127             };
128 4   50     39 $self->{guard_class} = $opt{guard_class} || 'Guard::Stats::Instance';
129 4         28 $self->{$_} = 0 for @values;
130              
131 4         19 return $self;
132             };
133              
134             =head1 Creating and using guards
135              
136             =head2 guard( %options )
137              
138             Create a guard object. All options will be forwarded to the guard's new()
139             "as is", except for C and C which are reserved.
140              
141             As of current, the built-in guard class supports no other options, so
142             supplying a hash is useless unless the guard class is redefined. See
143             "overriding guard class" below. See also L for the
144             detailed description of default guard class.
145              
146             =cut
147              
148             sub guard {
149 28     28 1 4823 my __PACKAGE__ $self = shift;
150 28         53 my %opt = @_;
151              
152 28 100       167 my $g = $self->{guard_class}->new(
153             %opt,
154             owner => $self,
155             want_time => $self->{time_stat} ? 1 : 0,
156             );
157 28         63 $self->{total}++;
158 28         62 my $running = $self->running;
159 28 100       85 if (my $code = $self->{on_level}{$running}) {
160 1         4 $code->($running, $self);
161             };
162 28         87 return $g;
163             };
164              
165             =head2 $guard->end( [ $result ] )
166              
167             Signal that action associated with the guard is over. If $result is provided,
168             it is saved in a special hash (see get_stat_result() below). This can be used
169             e.g. to measure the number of successful/unsuccessful actions.
170              
171             Calling end() a second time on the same guard will result in a warning, and
172             change no counters.
173              
174             =head2 $guard->is_done
175              
176             Tell whether end() was ever called on the guard.
177              
178             =head2 undef $guard
179              
180             The guard's DESTROY() method will signal stats object that guard is gone, and
181             whether it was finished before destruction.
182              
183             =cut
184              
185             =head1 Statistics
186              
187             The following getters represent numbers of guards in respective states:
188              
189             =over
190              
191             =item * total() - all guards ever created;
192              
193             =item * dead() - DESTROY was called;
194              
195             =item * alive() - DESTROY was NOT called;
196              
197             =item * done() - end() was called;
198              
199             =item * complete() - both end() and DESTROY were called;
200              
201             =item * zombie() - end() was called, but not DESTROY;
202              
203             =item * running() - neither end() nor DESTROY called;
204              
205             =item * broken() - number of guards for which DESTROY was called,
206             but NOT end().
207              
208             =back
209              
210             Growing broken and/or zombie counts usually indicate something went wrong.
211              
212             =cut
213              
214             # create lots of identic subs
215             foreach (@values) {
216             my $name = $_;
217 4     4   10141 my $code = sub { return shift->{$name} };
218 4     4   1827 no strict 'refs'; ## no critic
  4         9  
  4         2944  
219             *$name = $code;
220             };
221              
222             sub running {
223 30     30 1 50 my __PACKAGE__ $self = shift;
224 30         79 return $self->{total} - $self->{done} - $self->{broken};
225             };
226             sub alive {
227 3     3 1 7 my __PACKAGE__ $self = shift;
228 3         14 return $self->{total} - $self->{complete} - $self->{broken};
229             };
230             sub dead {
231 1     1 1 988 my __PACKAGE__ $self = shift;
232 1         6 return $self->{complete} + $self->{broken};
233             };
234             sub zombie {
235 2     2 1 3 my __PACKAGE__ $self = shift;
236 2         9 return $self->{done} - $self->{complete};
237             };
238              
239             =head2 get_stat
240              
241             Get all statistics as a single hashref.
242              
243             =cut
244              
245             sub get_stat {
246 6     6 1 1514 my __PACKAGE__ $self = shift;
247 6         8 my %ret;
248 6         41 $ret{$_} = $self->{$_} for @values;
249 6         15 $ret{dead} = $ret{complete} + $ret{broken};
250 6         14 $ret{zombie} = $ret{done} - $ret{complete};
251 6         11 $ret{alive} = $ret{total} - $ret{dead};
252 6         19 $ret{running} = $ret{alive} - $ret{zombie};
253              
254 6         36 return \%ret;
255             };
256              
257             =head2 get_stat_result
258              
259             Provide statistics on agruments provided to end() method.
260              
261             =cut
262              
263             sub get_stat_result {
264 2     2 1 5 my __PACKAGE__ $self = shift;
265              
266 2         3 my %ret = %{ $self->{results} };
  2         12  
267 2         16 return \%ret;
268             };
269              
270             =head2 get_stat_time
271              
272             Return time statistics object, if any.
273              
274             =cut
275              
276             sub get_stat_time {
277 2     2 1 393 my __PACKAGE__ $self = shift;
278 2         13 return $self->{time_stat};
279             };
280              
281             =head2 on_level( $n, CODEREF )
282              
283             Set on_level callback. If $n is positive, run CODEREF->($n)
284             when number of running guard instances is increased to $n.
285              
286             If $n is negative or 0, run CODEREF->($n) when it is decreased to $n.
287              
288             CAVEAT: Normally, CODEREF should not die as it may be called within
289             a destructor.
290              
291             =cut
292              
293             sub on_level {
294 2     2 1 736 my __PACKAGE__ $self = shift;
295 2         4 my ($level, $code) = @_;
296 2         7 $self->{on_level}{$level} = $code;
297 2         30 return $self;
298             };
299              
300             =head1 Overriding guard class
301              
302             Custom guard classes may be used with Guard::Stats.
303              
304             A guard_class supplied to new() must exhibit the following properties:
305              
306             =over
307              
308             =item * It must have a new() method, accepting a hash. C=object and
309             C=0|1 parameters MUST be acceptable.
310              
311             =item * The object returned by new() MUST have end(), is_done() and DESTROY()
312             methods.
313              
314             =item * end() method MUST accept one or zero parameters.
315              
316             =item * end() method MUST call C with one or zero parameters
317             on the C object discussed above when called for the first time.
318              
319             =item * end() method MUST do nothing and emit a warning if called more than
320             once. It MAY die then.
321              
322             =item * is_done() method MUST return true if end() was ever called, and
323             false otherwise.
324              
325             =item * DESTROY() method MUST call C method on C
326             object with one boolean parameter equivalent to is_done() return value.
327              
328             =item * end() and DESTROY() methods MAY call add_stat_time() method on the
329             C object with one numeric parameter. Each guard object MUST call
330             add_stat_time only once.
331              
332             =back
333              
334             See C.
335              
336             =head1 Guard instance callbacks
337              
338             The following methods are called by the guard object in different stages of
339             its life. They should NOT be called directly (unless there's a need to fool
340             the stats object) and are only described for people who want to extend
341             the guard object class.
342              
343             =head2 add_stat_end( [ $result ] )
344              
345             =head2 add_stat_destroy( $end_was_called )
346              
347             =head2 add_stat_time( $time )
348              
349             =cut
350              
351             sub add_stat_end {
352 2     2 1 4 my __PACKAGE__ $self = shift;
353 2         4 my ($result, @rest) = @_;
354 2 50       7 $result = "" unless defined $result;
355              
356 2         4 $self->{done}++;
357 2         7 $self->{results}{$result}++;
358              
359 2         6 my $running = $self->running;
360 2 100       11 if (my $code = $self->{on_level}{-$running}) {
361 1         4 $code->($running, $self);
362             };
363             };
364              
365             sub add_stat_destroy {
366 28     28 1 32 my $self = shift;
367 28         53 my ($is_done) = @_;
368              
369 28 100       53 if ($is_done) {
370 2         6 $self->{complete}++;
371             } else {
372 26         71 $self->{broken}++;
373             };
374             };
375              
376             sub add_stat_time {
377 25     25 1 29 my __PACKAGE__ $self = shift;
378 25         28 my $t = shift;
379 25 50       52 return unless $self->{time_stat};
380 25         87 $self->{time_stat}->add_data($t);
381             };
382              
383             =head1 AUTHOR
384              
385             Konstantin S. Uvarin, C<< >>
386              
387             =head1 BUGS
388              
389             Please report any bugs or feature requests to C, or through
390             the web interface at L. I will be notified, and then you'll
391             automatically be notified of progress on your bug as I make changes.
392              
393             =head1 SUPPORT
394              
395             You can find documentation for this module with the perldoc command.
396              
397             perldoc Guard::Stats
398              
399              
400             You can also look for information at:
401              
402             =over 4
403              
404             =item * Github:
405              
406             L
407              
408             =item * RT: CPAN's request tracker (report bugs here)
409              
410             L
411              
412             =item * AnnoCPAN: Annotated CPAN documentation
413              
414             L
415              
416             =item * CPAN Ratings
417              
418             L
419              
420             =item * Search CPAN
421              
422             L
423              
424             =back
425              
426             =head1 ACKNOWLEDGEMENTS
427              
428             This module was initially written as part of my day job at
429             L.
430              
431             Vadim Vlasov was the first user of this package, and proposed
432             the C counter.
433              
434             =head1 SEE ALSO
435              
436             L - This module was created for monitoring callback
437             usage in AnyEvent-driven application. However, it allows for a broadeer usage.
438              
439             L - A single-threaded web-server handling multiple simultaneous
440             requests is probably the most natural environment for callback counting. See
441             C in this distribution.
442              
443             L - Another module for finding leaked callbacks.
444              
445             =head1 LICENSE AND COPYRIGHT
446              
447             Copyright 2013 Konstantin S. Uvarin.
448              
449             This program is free software; you can redistribute it and/or modify it
450             under the terms of either: the GNU General Public License as published
451             by the Free Software Foundation; or the Artistic License.
452              
453             See http://dev.perl.org/licenses/ for more information.
454              
455             =cut
456              
457             1; # End of Guard::Stats