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