File Coverage

blib/lib/Log/Timer.pm
Criterion Covered Total %
statement 61 61 100.0
branch 6 8 75.0
condition 13 17 76.4
subroutine 14 14 100.0
pod 4 4 100.0
total 98 104 94.2


line stmt bran cond sub pod time code
1             package Log::Timer;
2              
3 1     1   111716 use strict;
  1         8  
  1         24  
4 1     1   4 use warnings;
  1         2  
  1         21  
5 1     1   4 use Exporter 'import';
  1         1  
  1         64  
6             our $VERSION = '1.0.1'; # VERSION
7             # ABSTRACT: track nested timing information
8              
9              
10             our @EXPORT = our @EXPORT_OK = qw/ timer subroutine_timer /;
11              
12 1     1   322 use Guard::Timer;
  1         2  
  1         42  
13 1     1   465 use List::AllUtils qw/ max /;
  1         14269  
  1         72  
14 1     1   450 use Statistics::Descriptive;
  1         24150  
  1         32  
15 1     1   8 use Log::Any qw/ $log /;
  1         2  
  1         7  
16              
17              
18             my $name__context = {
19             "" => {
20             indent => 1,
21             },
22             };
23             my $message_stats = {};
24              
25              
26             sub timer {
27 5     5 1 5149 my ($message, $options) = @_;
28 5   100     16 $options //= {};
29              
30 5   100     17 my $log_prefix = $options->{prefix} // "";
31              
32             # If you run async code, you need to distinguish the context this
33             # is running in. Human readable.
34 5   100     14 my $context_name = $options->{context} //= "";
35              
36             # Create context at the same level we're at, assuming we're always
37             # branching off the default "process wide" context
38             my $context = $name__context->{ $context_name } //= {
39             start_indent => $name__context->{ "" }->{indent},
40 5   100     12 %{ $name__context->{ "" } },
  1         6  
41             };
42              
43 5   50     14 my $indent_increase = $options->{indent_increase} // 4;
44              
45 5         7 $context->{indent} += $indent_increase;
46              
47 5 100       20 my $context_prefix = $context_name eq ""
48             ? ""
49             : "$context_name: ";
50              
51             return timer_guard(
52             sub {
53 5     5   10 my $duration = shift;
54 5         12 _collect_timing($message, $duration);
55              
56             # Un-nest indentation level
57 5         535 my $indent = $context->{indent} -= $indent_increase;
58              
59 5         22 my $indentation = " " x max(1, $indent);
60 5         30 $log->trace("${log_prefix}duration($duration)$indentation$context_prefix$message");
61              
62             # Clean up context if we're done with it
63 5 100       295 if(my $start_indent = $context->{start_indent}) {
64 1 50       4 if( $indent == $start_indent ) {
65 1         12 delete $name__context->{ $context_name };
66             }
67             }
68             },
69 5         26 4, # decimal points
70             );
71             }
72              
73              
74             sub subroutine_timer {
75 1     1 1 2002 my ($message, $options) = @_;
76 1   50     8 $options //= {};
77              
78 1   50     18 my $depth = $options->{depth} || 1;
79 1         7 my (undef, undef, undef, $subroutine) = caller($depth);
80 1         30 $subroutine =~ s/::(\w+)$/->$1/;
81 1 50       5 $message = defined($message) ? ": $message" : "";
82              
83 1         4 return timer( "$subroutine$message", $options );
84             }
85              
86             sub _collect_timing {
87 5     5   10 my ($message, $duration) = @_;
88 5   66     43 my $stats = $message_stats->{$message} ||= Statistics::Descriptive::Sparse->new();
89 5         114 $stats->add_data($duration);
90             }
91              
92              
93             sub report_timing_stats {
94 2     2 1 1983 my @what = ( "mean", "sum", "min", "max", "standard_deviation" );
95              
96             my @messages =
97 4         6 map { $_->{message} }
98             reverse
99 5         33 sort { $a->{order} <=> $b->{order} }
100             map {
101 2         8 my $stats = $message_stats->{$_};
  4         103  
102             +{
103             message => "Stats: "
104             . sprintf("%-12s", "count(" . $stats->count . "), ")
105             . join(
106             ", ",
107 4         9 map { sprintf("$_(%.3f)", ($stats->$_)) } @what
  20         148  
108             ) . " for ($_)",
109             order => $stats->sum,
110             };
111             }
112             keys %$message_stats;
113              
114 2         7 for my $message (@messages) {
115 4         104 $log->info($message);
116             }
117              
118 2         45 clear_timing_stats();
119             }
120              
121              
122             sub clear_timing_stats {
123 2     2 1 21 $message_stats = {};
124             }
125              
126             END {
127 1     1   24058 report_timing_stats();
128             }
129              
130             1;
131              
132             __END__