File Coverage

blib/lib/Catalyst/Plugin/Statsd.pm
Criterion Covered Total %
statement 21 21 100.0
branch 1 2 50.0
condition n/a
subroutine 7 7 100.0
pod 2 2 100.0
total 31 32 96.8


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::Statsd;
2              
3             # ABSTRACT: Log Catalyst stats to statsd
4              
5 2     2   3847655 use v5.10.1;
  2         20  
6              
7 2     2   9 use Moose::Role;
  2         4  
  2         17  
8              
9 2     2   10061 use POSIX qw/ ceil /;
  2         4  
  2         16  
10 2     2   2938 use Ref::Util qw/ is_plain_arrayref /;
  2         10  
  2         138  
11              
12             # RECOMMEND PREREQ: Ref::Util::XS
13              
14 2     2   13 use namespace::autoclean;
  2         3  
  2         15  
15              
16             requires qw/ log_stats /;
17              
18             our $VERSION = 'v0.7.2';
19              
20              
21             sub statsd_client {
22 4     4 1 10 my ($c) = @_;
23 4         12 return $c->req->env->{'psgix.monitor.statsd'};
24             }
25              
26              
27              
28             sub statsd_metric_name_filter {
29 6     6 1 13 my ($c, $stat) = @_;
30              
31 6 50       20 return "$stat" unless is_plain_arrayref($stat);
32              
33 6         16 my $metric = "catalyst.stats." . $stat->[1] . ".time";
34 6         37 $metric =~ s/[^\w\-_]+/./g;
35              
36 6         23 return $metric;
37             }
38              
39             around log_stats => sub {
40             my ( $next, $c ) = @_;
41              
42             state $config = $c->config->{'Plugin::Statsd'} // {};
43              
44             if ( my $client = $c->statsd_client) {
45              
46             my $stat = [ -1, "catalyst.response.time", $c->stats->elapsed ];
47             my $metric = $c->statsd_metric_name_filter($stat) or next;
48              
49             $client->timing_ms( "catalyst.response.time",
50             ceil( $stat->[2] * 1000 ) );
51              
52             foreach my $stat ( $c->stats->report ) {
53              
54             my $metric = $c->statsd_metric_name_filter($stat) or next;
55             my $timing = ceil( $stat->[2] * 1000 );
56              
57             $client->timing_ms( $metric, $timing );
58              
59             }
60              
61             }
62              
63             my $disabled = $config->{disable_stats_report} // !$c->debug;
64              
65             $c->$next unless $disabled;
66             };
67              
68             around finalize => sub {
69             my ($next, $c) = @_;
70              
71             if (my $client = $c->statsd_client) {
72              
73             if ($c->can("sessionid") && (my $id = $c->sessionid)) {
74             $client->set_add("catalyst.sessionid", "$id");
75             }
76             # Plack::Middleware::Session
77             elsif (my $options = $c->req->env->{'psgix.session.options'}) {
78             if (my $id = $options->{id}) {
79             $client->set_add("catalyst.sessionid", "$id");
80             }
81             }
82             }
83              
84             $c->$next();
85             };
86              
87              
88             1;
89              
90             __END__
91              
92             =pod
93              
94             =encoding UTF-8
95              
96             =head1 NAME
97              
98             Catalyst::Plugin::Statsd - Log Catalyst stats to statsd
99              
100             =head1 VERSION
101              
102             version v0.7.2
103              
104             =head1 SYNOPSIS
105              
106             use Catalyst qw/
107             Statsd
108             -Stats=1
109             /;
110              
111             use Net::Statsd::Tiny;
112              
113             __PACKAGE__->config(
114             'psgi_middleware', [
115             Statsd => {
116             client => Net::Statsd::Tiny->new,
117             },
118             ],
119             );
120              
121             # (or you can specify the Statsd middleware in your
122             # application's PSGI file.)
123              
124             =head1 DESCRIPTION
125              
126             This plugin will log L<Catalyst> timing statistics to statsd.
127              
128             =head2 CONFIGURATION
129              
130             __PACKAGE__->config(
131              
132             'Plugin::Statsd' => {
133             disable_stats_report => 0,
134             },
135              
136             );
137              
138             =head2 C<disable_stats_report>
139              
140             Enabling stats will also log a table of statistics to the Catalyst
141             log. If you do not want this, then set C<disable_stats_report>
142             to true.
143              
144             Note that if you are modifying the C<log_stats> method or using
145             another plugin that does this, then this may interfere with that if
146             you disable the stats report.
147              
148             This defaults to
149              
150             !$c->debug
151              
152             =head1 METHODS
153              
154             =head2 C<statsd_client>
155              
156             $c->statsd_client;
157              
158             Returns the statsd client.
159              
160             This is the statsd client used by L<Plack::Middleware::Statsd>.
161              
162             =head2 C<statsd_metric_name_filter>
163              
164             $c->statsd_metric_name_filter( $stat_or_name );
165              
166             This method returns the name to be used for logging stats, or C<undef>
167             if the metric should be ignored.
168              
169             Only alphanumeric characters, hyphens or underscores in namespaces are
170             accepted. All other characters are converted to dots, with consecutive
171             dots compressed into a single dot.
172              
173             If it is passed a non-arrayref, then it will stringify the argument
174             and return that.
175              
176             If it is passed an array reference, then it assumes the argument comes
177             from L<Catalyst::Stats> report and is converted into a suitable metric
178             name.
179              
180             You can override or modify this method to filter out which metrics you
181             want logged, or to change the names of the metrics.
182              
183             =head1 METRICS
184              
185             =head2 C<catalyst.response.time>
186              
187             This logs the Catalyst reponse time that is normally reported by
188             Catalyst. However, it is probably unnecessary since
189             L<Plack::Middleware::Statsd> also logs response times.
190              
191             =head2 C<catalyst.sessionid>
192              
193             If L<Catalyst::Plugin::Session> or L<Plack::Middleware::Session> is
194             used, or anything that adds a C<sessionid> method to the context, then
195             the session id is added as a set, to count the number of unique
196             sessions.
197              
198             =head2 C<catalyst.stats.*.time>
199              
200             These are metrics generated from L<Catalyst::Stats>.
201              
202             All non-word characters in the paths in an action are changed to dots,
203             e.g. the timing for an action C</foo/bar> will be logged with the
204             metric C<catalyst.stats.foo.bar.time>.
205              
206             The metric name is generated by L</statsd_metric_name_filter>.
207              
208             =head1 KNOWN ISSUES
209              
210             =head2 Custom Profiling Points
211              
212             If you have custom profiling points, then these will be treated as
213             top-level names in the C<catalyst.stats.*> namespaces, e.g.
214              
215             my $stats = $c->stats;
216             $stats->profile( begin => 'here' );
217              
218             ...
219              
220             $stats->profile( end => 'here' );
221              
222             will be logged to statsd in the C<catalyst.stats.here.time> namespace.
223              
224             If you do not want this, then you can work around this by prefixing
225             the block name with a controller name, e.g.
226              
227             $stats->profile( begin => 'controller.here' );
228              
229             =head2 Large Databases When Profiling
230              
231             When profiling your application, the size of your stats database may
232             grow quite large.
233              
234             Your database storage and retention settings should be adjusted
235             accordingly.
236              
237             =head1 SEE ALSO
238              
239             =over
240              
241             =item *
242              
243             L<Catalyst::Stats>
244              
245             =item *
246              
247             L<Plack::Middleware::Statsd>
248              
249             =item *
250              
251             L<Net::Statsd::Tiny>
252              
253             =back
254              
255             =head1 SOURCE
256              
257             The development version is on github at L<https://github.com/robrwo/CatalystX-Statsd>
258             and may be cloned from L<git://github.com/robrwo/CatalystX-Statsd.git>
259              
260             =head1 BUGS
261              
262             Please report any bugs or feature requests on the bugtracker website
263             L<https://github.com/robrwo/CatalystX-Statsd/issues>
264              
265             When submitting a bug or request, please include a test-file or a
266             patch to an existing test-file that illustrates the bug or desired
267             feature.
268              
269             =head1 AUTHOR
270              
271             Robert Rothenberg <rrwo@cpan.org>
272              
273             The initial development of this module was sponsored by Science Photo
274             Library L<https://www.sciencephoto.com>.
275              
276             =head1 CONTRIBUTOR
277              
278             =for stopwords Slaven Rezić
279              
280             Slaven Rezić <slaven@rezic.de>
281              
282             =head1 COPYRIGHT AND LICENSE
283              
284             This software is Copyright (c) 2018-2019 by Robert Rothenberg.
285              
286             This is free software, licensed under:
287              
288             The Artistic License 2.0 (GPL Compatible)
289              
290             =cut