File Coverage

blib/lib/Mojolicious/Plugin/Syslog.pm
Criterion Covered Total %
statement 48 48 100.0
branch 14 20 70.0
condition 17 36 47.2
subroutine 18 18 100.0
pod 1 1 100.0
total 98 123 79.6


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Syslog;
2 2     2   3454 use Mojo::Base 'Mojolicious::Plugin';
  2         6  
  2         16  
3              
4 2     2   1785 use Sys::Syslog qw(:standard :macros);
  2         10345  
  2         1395  
5              
6             our $VERSION = '0.06';
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 264 my ($self, $app, $config) = @_;
18              
19             $self->_add_syslog($app, %$config)
20             if $config->{enable} // $ENV{MOJO_SYSLOG_ENABLE}
21 4 100 66     35 // $app->mode ne 'development';
      33        
22              
23             $self->_add_access_log($app, %$config)
24 4 50 66     32 if $config->{access_log} // $ENV{MOJO_SYSLOG_ACCESS_LOG};
25             }
26              
27             sub _add_access_log {
28 4     4   13 my ($self, $app, %config) = @_;
29              
30 4   50     13 my $log_format = $config{access_log} || $ENV{MOJO_SYSLOG_ACCESS_LOG} || 'v1';
31 4 100       18 $log_format = '%H "%P" (%I) %C %M (%Ts)' if $log_format =~ /^v?1$/;
32 4 100       14 $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   48564 shift->helpers->timing->begin(__PACKAGE__);
37             }
38 4         51 );
39              
40             my %extractors = (
41 2 50   2   7 A => sub { $_[1]->headers->user_agent || '' },
42 6     6   22 C => sub { $_[2]->code },
43 2 50   2   9 F => sub { $_[1]->headers->referrer || '' },
44 6     6   27 H => sub { $_[1]->method },
45 2     2   9 I => sub { $_[1]->request_id },
46 2 50   2   8 M => sub { $_[2]->message || $_[2]->default_message($_[2]->code) },
47 5     5   19 P => sub { $_[1]->url->path->to_abs_string },
48 2     2   22 R => sub { $_[0]->tx->remote_address },
49 3   50 3   11 T => sub { $_[0]->helpers->timing->elapsed(__PACKAGE__) // 0 },
50 2     2   7 U => sub { $_[1]->url->to_abs->to_string },
51 4         120 );
52              
53 4   66     27 my $app_log = $log_format =~ m!\%I\b! && $app->log;
54              
55 4         41 my $re = join '|', sort keys %extractors;
56 4         80 $re = qr{\%($re)};
57              
58             $app->hook(
59             after_dispatch => sub {
60 6     6   269078 my $c = shift;
61 6   66     57 my $log = $app_log || $c->log;
62 6         127 my ($req, $res) = ($c->req, $c->res);
63 6 100       121 my $level = $res->is_server_error ? 'warn' : 'info';
64 6         125 my $message = $log_format;
65 6         86 $message =~ s!$re!$extractors{$1}->($c, $req, $res)!ge;
  32         1298  
66 6         1191 $log->$level($message);
67             }
68 4         23 );
69             }
70              
71             sub _add_syslog {
72 1     1   3 my ($self, $app, %config) = @_;
73              
74 1   33     8 $config{facility} ||= $ENV{MOJO_SYSLOG_FACILITY} || LOG_USER;
      33        
75 1   33     8 $config{ident} ||= $ENV{MOJO_SYSLOG_IDENT} || $app->moniker;
      33        
76 1   50     7 $config{logopt} ||= $ENV{MOJO_SYSLOG_LOGOPT} || 'ndelay,pid';
      33        
77              
78 1         14 openlog @config{qw(ident logopt facility)};
79 1 50       13 $app->log->unsubscribe('message') if $config{only_syslog};
80 1         75 $app->log->unsubscribe(message => \&_syslog);
81 1         24 $app->log->on(message => \&_syslog);
82             }
83              
84             sub _syslog {
85 4     4   2175 my ($log, $level, @msg) = @_;
86 4 50       68 syslog $PRIORITY{$level}, '%s', join ' ', @msg if $PRIORITY{$level};
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