File Coverage

blib/lib/Mojo/Netdata/Collector/HTTP.pm
Criterion Covered Total %
statement 89 90 98.8
branch 31 36 86.1
condition 16 22 72.7
subroutine 10 10 100.0
pod 2 2 100.0
total 148 160 92.5


line stmt bran cond sub pod time code
1             package Mojo::Netdata::Collector::HTTP;
2 1     1   444 use Mojo::Base 'Mojo::Netdata::Collector', -signatures;
  1         2  
  1         6  
3              
4 1     1   688 use Mojo::UserAgent;
  1         81558  
  1         9  
5 1     1   45 use Mojo::Netdata::Util qw(logf safe_id);
  1         1  
  1         53  
6 1     1   6 use Time::HiRes qw(time);
  1         2  
  1         9  
7              
8             require Mojo::Netdata;
9             our $VERSION = $Mojo::Netdata::VERSION;
10              
11             has jobs => sub ($self) { +[] };
12             has type => 'HTTP';
13             has ua => sub { Mojo::UserAgent->new(insecure => 0, connect_timeout => 5, request_timeout => 5) };
14             has update_every => 30;
15              
16 3     3 1 21 sub register ($self, $config, $netdata) {
  3         5  
  3         5  
  3         5  
  3         4  
17             $config->{update_every} ? $self->update_every($config->{update_every})
18 3 50       22 : $netdata->update_every >= 10 ? $self->update_every($netdata->update_every)
    50          
19             : $self->update_every(30);
20              
21 3 100       42 $self->ua->insecure($config->{insecure}) if defined $config->{insecure};
22 3 100       21 $self->ua->connect_timeout($config->{connect_timeout}) if defined $config->{connect_timeout};
23 3 100       16 $self->ua->request_timeout($config->{request_timeout}) if defined $config->{request_timeout};
24 3 50 50     21 $self->ua->proxy->detect if $config->{proxy} // 1;
25 3   33     142 $self->ua->transactor->name($config->{user_agent} || "Mojo-Netdata/$VERSION (Perl)");
26 3         87 $self->jobs([]);
27              
28 3 50       18 my @jobs = ref $config->{jobs} eq 'HASH' ? %{$config->{jobs}} : @{$config->{jobs}};
  0         0  
  3         11  
29 3         8 while (my $url = shift @jobs) {
30 13 100       73 my $job = $self->_make_job($url => ref $jobs[0] eq 'HASH' ? shift @jobs : {}, $config);
31 13 100       2160 push @{$self->jobs}, $job if $job;
  11         25  
32             }
33              
34 3 50       17 return @{$self->jobs} ? $self : undef;
  3         6  
35             }
36              
37 1     1 1 376 sub update_p ($self) {
  1         2  
  1         1  
38 1         5 my ($ua, @p) = ($self->ua);
39              
40 1         7 my $t0 = time;
41 1         2 for my $job (@{$self->jobs}) {
  1         3  
42 3         2251 my $tx = $ua->build_tx(@{$job->[0]});
  3         16  
43 2     2   3 push @p, $ua->start_p($tx)->then(sub ($tx) {
  2         165551  
  2         7  
44 2         13 $job->[1]->($tx, $t0);
45 1     1   2 })->catch(sub ($err) {
  1         40113  
  1         4  
46 1         7 $job->[1]->($tx, $t0, {message => $err});
47 3         1208 });
48             }
49              
50 1         2037 return Mojo::Promise->all(@p);
51             }
52              
53 13     13   16 sub _make_job ($self, $url, $params, $defaults) {
  13         17  
  13         16  
  13         16  
  13         14  
  13         14  
54 13         35 $url = Mojo::URL->new($url);
55 13 100       2158 return undef unless my $host = $url->host;
56              
57 12   100     79 my $headers = Mojo::Headers->new->from_hash($defaults->{headers} || {});
58 12 100       223 $headers->header($_ => $params->{headers}{$_}) for keys %{$params->{headers} || {}};
  12         71  
59 12 100       73 ($headers->header(Host => $url->host), $url->host($params->{via})) if $params->{via};
60              
61 12   66     85 my $dimension = $params->{dimension} || $headers->host || $url->host;
62 12   66     118 my $family = $params->{family} || $defaults->{family} || $headers->host || $url->host;
63              
64 12         89 my $code_chart = $self->chart("${family}_code")->title("HTTP Status code for $family")
65             ->context('httpcheck.code')->family($family)->units('#');
66              
67 12 100       383 if ($code_chart->dimension($dimension)) {
68 1         19 logf(warnings => 'Family "%s" already has dimension "%s".', $family, $dimension);
69 1         5 return undef;
70             }
71              
72 11         57 my $time_chart = $self->chart("${family}_time")->title("Response time for $family")
73             ->context('httpcheck.responsetime')->family($family)->units('ms');
74              
75 11         349 $code_chart->dimension($dimension => {});
76 11         33 $time_chart->dimension($dimension => {});
77              
78 9     9   14 my $update = sub ($tx, $t0, $err = undef) {
  9         468  
  9         14  
  9         13  
  9         19  
79 9   66     43 $err ||= $tx->error;
80 9         162 my $req = $tx->req;
81 9   100     38 my $code = $tx->res->code // 0;
82 9   100     76 my @msg = ($req->method, $req->url, $err || {code => $code}, $req->headers->to_hash(1));
83 9 100       436 logf(($err ? 'warnings' : 'debug'), '%s %s == %s %s', @msg);
84              
85 9         95 $time_chart->dimension($dimension => {value => int(1000 * (time - $t0))});
86 9         52 $code_chart->dimension($dimension => {value => $code});
87 11         49 };
88              
89 11         16 my @data;
90 11         34 push @data, $headers->to_hash(1);
91             push @data,
92             exists $params->{json} ? (json => $params->{json})
93             : exists $params->{form} ? (form => $params->{form})
94             : exists $params->{body} ? ($params->{body})
95 11 100       178 : ();
    100          
    100          
96              
97 11   100     45 return [[$params->{method} || 'GET', $url->to_unsafe_string, @data], $update];
98             }
99              
100             1;
101              
102             =encoding utf8
103              
104             =head1 NAME
105              
106             Mojo::Netdata::Collector::HTTP - A website monitorer for Mojo::Netdata
107              
108             =head1 SYNOPSIS
109              
110             =head2 Config
111              
112             Below is an example C config file. Note
113             that the file can have any name and you have have as many as you want, as long
114             as it has the C<.conf.pl> extension.
115              
116             {
117             # Required
118             collector => 'Mojo::Netdata::Collector::HTTP',
119              
120             # Optional
121             insecure => 0, # Set to "1" to allow insecure SSL/TLS connections
122             connect_timeout => 5, # Max time for the connection to be established
123             request_timeout => 5, # Max time for the whole request to complete
124             proxy => 1, # Set to "0" to disable proxy auto-detect
125             update_every => 30, # How often to run the "jobs" below
126             user_agent => '...', # Custom User-Agent name
127              
128             # Default values, unless defined in the job
129             family => 'default-family-name',
130             headers => {'X-Merged-With' => 'headers inside job config'},
131              
132             # Required - List of URLs and an optional config hash (object)
133             jobs => [
134              
135             # List of URLs to check (Config is optional)
136             'https://superwoman.example.com',
137             'https://superman.example.com',
138              
139             # URL and config parameters
140             'https://example.com' => {
141             method => 'GET', # GET (Default), HEAD, POST, ...
142             headers => {'X-Foo' => 'bar'}, # HTTP headers
143              
144             # Replace "host" in the URL with this IP and set the "Host" header
145             via => '192.168.2.1',
146              
147             # Set "dimension" to get a custom label in the chart.
148             # Default to the "Host" header or the host part of the URL.
149             dimension => 'foo', # Default: "example.com"
150              
151             # Set "family" to group multiple domains together in one chart,
152             # Default to the "Host" header or the host part of the URL.
153             family => 'bar', # Default: "example.com"
154              
155             # Only one of these can be present
156             json => {...}, # JSON HTTP body
157             form => {key => $value}, # Form data
158             body => '...', # Raw HTTP body
159             },
160             ],
161             };
162              
163             =head2 Health
164              
165             Here is an example C file:
166              
167             template: web_server_code
168             on: httpcheck.code
169             class: Errors
170             type: Web Server
171             component: HTTP endpoint
172             plugin: mojo
173             lookup: max -5m absolute foreach *
174             every: 1m
175             warn: $this >= 300 && $this < 500
176             crit: $this >= 500 && $this != 503
177             to: webmaster
178              
179             template: web_server_up
180             on: httpcheck.code
181             class: Errors
182             type: Web Server
183             component: HTTP endpoint
184             plugin: mojo
185             lookup: min -5m absolute foreach *
186             every: 1m
187             crit: $this == 0
188             units: up/down
189             to: webmaster
190              
191             =head1 DESCRIPTION
192              
193             L is a collector that can chart web page
194             response time and HTTP status codes.
195              
196             =head1 ATTRIBUTES
197              
198             =head2 jobs
199              
200             $array_ref = $self->jobs;
201              
202             A list of jobs generated by L.
203              
204             =head2 type
205              
206             $str = $collector->type;
207              
208             Defaults to "http".
209              
210             =head2 ua
211              
212             $ua = $collector->ua;
213              
214             Holds a L.
215              
216             =head2 update_every
217              
218             $num = $chart->update_every;
219              
220             Default value is 30. See L for more
221             details.
222              
223             =head1 METHODS
224              
225             =head2 register
226              
227             $collector = $collector->register(\%config, $netdata);
228              
229             Returns a L<$collector> object if any "jobs" are defined in C<%config>. Will
230             also set L from C<%config> or use L
231             if it is 10 or greater.
232              
233             =head2 update_p
234              
235             $p = $collector->update_p;
236              
237             Gathers information about the "jobs" registered.
238              
239             =head1 SEE ALSO
240              
241             L and L.
242              
243             =cut