File Coverage

blib/lib/Mojolicious/Plugin/Syslog.pm
Criterion Covered Total %
statement 48 48 100.0
branch 13 18 72.2
condition 17 36 47.2
subroutine 18 18 100.0
pod 1 1 100.0
total 97 121 80.1


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Syslog;
2 2     2   2903 use Mojo::Base 'Mojolicious::Plugin';
  2         4  
  2         11  
3              
4 2     2   1435 use Sys::Syslog qw(:standard :macros);
  2         8659  
  2         1182  
5              
6             our $VERSION = '0.05';
7              
8             my %PRIORITY = (
9             debug => LOG_DEBUG,
10             error => LOG_ERR,
11             fatal => LOG_CRIT,
12             info => LOG_INFO,
13             warn => LOG_WARNING,
14             );
15              
16             sub register {
17 4     4 1 246 my ($self, $app, $config) = @_;
18              
19             $self->_add_syslog($app, %$config)
20             if $config->{enable} // $ENV{MOJO_SYSLOG_ENABLE}
21 4 100 66     31 // $app->mode ne 'development';
      33        
22              
23             $self->_add_access_log($app, %$config)
24 4 50 66     29 if $config->{access_log} // $ENV{MOJO_SYSLOG_ACCESS_LOG};
25             }
26              
27             sub _add_access_log {
28 4     4   9 my ($self, $app, %config) = @_;
29              
30 4   50     12 my $log_format = $config{access_log} || $ENV{MOJO_SYSLOG_ACCESS_LOG} || 'v1';
31 4 100       14 $log_format = '%H "%P" (%I) %C %M (%Ts)' if $log_format =~ /^v?1$/;
32 4 100       11 $log_format = '%R %H %U %C "%F" "%A" (%Ts)' if $log_format =~ /^v?2$/;
33              
34             $app->hook(
35             before_routes => sub {
36 6     6   41593 shift->helpers->timing->begin(__PACKAGE__);
37             }
38 4         25 );
39              
40             my %extractors = (
41 2 50   2   7 A => sub { $_[1]->headers->user_agent || '' },
42 6     6   30 C => sub { $_[2]->code },
43 2 50   2   7 F => sub { $_[1]->headers->referrer || '' },
44 6     6   29 H => sub { $_[1]->method },
45 2     2   7 I => sub { $_[1]->request_id },
46 2 50   2   8 M => sub { $_[2]->message || $_[2]->default_message($_[2]->code) },
47 5     5   17 P => sub { $_[1]->url->path->to_abs_string },
48 2     2   28 R => sub { $_[0]->tx->remote_address },
49 3   50 3   8 T => sub { $_[0]->helpers->timing->elapsed(__PACKAGE__) // 0 },
50 2     2   5 U => sub { $_[1]->url->to_abs->to_string },
51 4         102 );
52              
53 4   66     20 my $app_log = $log_format =~ m!\%I\b! && $app->log;
54              
55 4         35 my $re = join '|', sort keys %extractors;
56 4         61 $re = qr{\%($re)};
57              
58             $app->hook(
59             after_dispatch => sub {
60 6     6   261149 my $c = shift;
61 6   66     56 my $log = $app_log || $c->log;
62 6         112 my ($req, $res) = ($c->req, $c->res);
63 6 100       101 my $level = $res->is_server_error ? 'warn' : 'info';
64 6         107 my $message = $log_format;
65 6         74 $message =~ s!$re!$extractors{$1}->($c, $req, $res)!ge;
  32         1108  
66 6         1043 $log->$level($message);
67             }
68 4         19 );
69             }
70              
71             sub _add_syslog {
72 1     1   3 my ($self, $app, %config) = @_;
73              
74 1   33     6 $config{facility} ||= $ENV{MOJO_SYSLOG_FACILITY} || LOG_USER;
      33        
75 1   33     7 $config{ident} ||= $ENV{MOJO_SYSLOG_IDENT} || $app->moniker;
      33        
76 1   50     5 $config{logopt} ||= $ENV{MOJO_SYSLOG_LOGOPT} || 'ndelay,pid';
      33        
77              
78 1         12 openlog @config{qw(ident logopt facility)};
79 1 50       11 $app->log->unsubscribe('message') if $config{only_syslog};
80 1         68 $app->log->unsubscribe(message => \&_syslog);
81 1         20 $app->log->on(message => \&_syslog);
82             }
83              
84             sub _syslog {
85 4     4   1885 my ($log, $level, @msg) = @_;
86 4         52 syslog $PRIORITY{$level}, '%s', join ' ', @msg;
87             }
88              
89             1;
90              
91             =encoding utf8
92              
93             =head1 NAME
94              
95             Mojolicious::Plugin::Syslog - A plugin for enabling a Mojolicious app to log to syslog
96              
97             =head1 SYNOPSIS
98              
99             use Mojolicious::Lite;
100             plugin syslog => {facility => 'local0'};
101              
102             =head1 DESCRIPTION
103              
104             L is a L plugin for making
105             L use L in addition (or instead) of file logging.
106              
107             This can be useful when starting Hypnotoad through Systemd, but want simple
108             logging of error messages to syslog.
109              
110             This plugin can also be used for only access logging, as an alternative to
111             L. This is done by forcing L to
112             "0" and enabling L.
113              
114             =head1 METHODS
115              
116             =head2 register
117              
118             $app->plugin(syslog => \%config);
119             $self->register($app, \%config);
120              
121             Used to register the plugin in your L application. Available
122             config parameters are:
123              
124             =over 2
125              
126             =item * access_log
127              
128             Used to enable logging of access to resources with a route enpoint. This means
129             that static files will not be logged, even if this option is enabled. It is
130             also possible to set the default value using the C
131             environment variable.
132              
133             This can be "v1", "v2" or a custom format. The default is currently "v1", but
134             that might change in the future.
135              
136             .---------------------------------------.
137             | Version | Format |
138             |---------|-----------------------------|
139             | v1 | %H "%P" (%I) %C %M (%Ts) |
140             | v2 | %R %H %U %C "%F" "%A" (%Ts) |
141             '---------------------------------------'
142              
143             Supported log variables:
144              
145             .----------------------------------------------------.
146             | Variable | Value |
147             |----------|-----------------------------------------|
148             | %A | User-Agent request header |
149             | %C | Response status code, ex "200" |
150             | %F | Referer request header |
151             | %H | HTTP request method, ex "GET", "POST" |
152             | %I | Mojolicious request ID |
153             | %M | Response message, ex OK |
154             | %P | Request URL path |
155             | %R | Remote address |
156             | %T | Time in seconds for this request |
157             | %U | Absolute request URL, without user info |
158             '----------------------------------------------------'
159              
160             =item * enable
161              
162             Need to be true to activate this plugin. Will use the "MOJO_SYSLOG_ENABLE"
163             environment variable or default to true if L is something
164             else than "development"
165              
166             =item * facility
167              
168             The syslog facility to use. Default to "MOJO_SYSLOG_FACILITY" environment
169             variable or default to "user".
170              
171             The default is EXPERIMENTAL.
172              
173             =item * ident
174              
175             The syslog ident to use. Default to "MOJO_SYSLOG_IDENT" environment variable or
176             L.
177              
178             =item * only_syslog
179              
180             Set this to true to disabled the default L logging to file/stderr.
181              
182             =back
183              
184             =head1 AUTHOR
185              
186             Jan Henning Thorsen
187              
188             =head1 COPYRIGHT AND LICENSE
189              
190             Copyright (C) 2019, Jan Henning Thorsen.
191              
192             This program is free software, you can redistribute it and/or modify it under
193             the terms of the Artistic License version 2.0.
194              
195             =cut