File Coverage

blib/lib/Mojo/Log/Role/AttachLogger.pm
Criterion Covered Total %
statement 39 76 51.3
branch 13 50 26.0
condition 7 15 46.6
subroutine 8 13 61.5
pod 1 1 100.0
total 68 155 43.8


line stmt bran cond sub pod time code
1             package Mojo::Log::Role::AttachLogger;
2              
3 3     3   1897 use Role::Tiny;
  3         5  
  3         18  
4 3     3   420 use Carp ();
  3         7  
  3         39  
5 3     3   1231 use Import::Into ();
  3         6826  
  3         52  
6 3     3   17 use Module::Runtime ();
  3         6  
  3         33  
7 3     3   11 use Scalar::Util ();
  3         6  
  3         2206  
8              
9             our $VERSION = 'v1.0.3';
10              
11             our @CARP_NOT = 'Mojolicious::Plugin::Log::Any';
12              
13             requires 'on';
14              
15             sub attach_logger {
16 4     4 1 1602 my ($self, $logger, $opt) = @_;
17 4 50       12 Carp::croak 'No logger passed' unless defined $logger;
18 4         8 my ($category, $prepend, $separator);
19 4 100       12 if (ref $opt) {
20 3         10 ($category, $prepend, $separator) = @$opt{qw[ category prepend_level message_separator ]};
21             } else {
22 1         2 $category = $opt;
23             }
24 4   100     18 $category //= 'Mojo::Log';
25 4   100     15 $prepend //= 1;
26 4   100     17 $separator //= "\n";
27              
28 4         10 my $do_log;
29 4 100 0     17 if (Scalar::Util::blessed($logger)) {
    50          
    0          
    0          
30 3 50 33     64 if ($logger->isa('Log::Any::Proxy')) {
    50          
    50          
    50          
31             $do_log = sub {
32 0     0   0 my ($self, $level, @msg) = @_;
33 0 0       0 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
34 0 0       0 $msg = "[$level] $msg" if $prepend;
35 0         0 $logger->$level($msg);
36 0         0 };
37             } elsif ($logger->isa('Log::Dispatch')) {
38             $do_log = sub {
39 0     0   0 my ($self, $level, @msg) = @_;
40 0 0       0 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
41 0 0       0 $msg = "[$level] $msg" if $prepend;
42 0 0       0 $level = 'critical' if $level eq 'fatal';
43 0         0 $logger->log(level => $level, message => $msg);
44 0         0 };
45             } elsif ($logger->isa('Log::Dispatchouli') or $logger->isa('Log::Dispatchouli::Proxy')) {
46             $do_log = sub {
47 0     0   0 my ($self, $level, @msg) = @_;
48 0 0       0 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
49 0 0       0 $msg = "[$level] $msg" if $prepend;
50 0 0       0 return $logger->log_debug($msg) if $level eq 'debug';
51             # hacky but we don't want to use log_fatal because it throws an
52             # exception, we want to allow real exceptions to propagate, and we
53             # can't localize a call to set_muted
54 0 0 0     0 local $logger->{muted} = 0 if $level eq 'fatal' and $logger->get_muted;
55 0         0 $logger->log($msg);
56 0         0 };
57             } elsif ($logger->isa('Mojo::Log')) {
58             $do_log = sub {
59 45     45   96130 my ($self, $level, @msg) = @_;
60 45         153 $logger->$level(@msg);
61 3         23 };
62             } else {
63 0         0 Carp::croak "Unsupported logger object class " . ref($logger);
64             }
65             } elsif ($logger eq 'Log::Any') {
66 1         5 require Log::Any;
67 1         6 $logger = Log::Any->get_logger(category => $category);
68             $do_log = sub {
69 10     10   3388 my ($self, $level, @msg) = @_;
70 10 100       29 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
71 10 50       22 $msg = "[$level] $msg" if $prepend;
72 10         49 $logger->$level($msg);
73 1         3865 };
74             } elsif ($logger eq 'Log::Log4perl') {
75 0         0 require Log::Log4perl;
76 0         0 $logger = Log::Log4perl->get_logger($category);
77             $do_log = sub {
78 0     0   0 my ($self, $level, @msg) = @_;
79 0 0       0 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
80 0 0       0 $msg = "[$level] $msg" if $prepend;
81 0         0 $logger->$level($msg);
82 0         0 };
83             } elsif ($logger eq 'Log::Contextual' or "$logger"->isa('Log::Contextual')) {
84 0         0 Module::Runtime::require_module("$logger");
85 0         0 Log::Contextual->VERSION('0.008001');
86 0         0 my %functions = map { ($_ => "slog_$_") } qw(debug info warn error fatal);
  0         0  
87 0         0 "$logger"->import::into(ref($self), values %functions);
88             $do_log = sub {
89 0     0   0 my ($self, $level, @msg) = @_;
90 0 0       0 my $msg = @msg > 1 ? join($separator, @msg) : $msg[0];
91 0 0       0 $msg = "[$level] $msg" if $prepend;
92 0         0 $self->can($functions{$level})->($msg);
93 0         0 };
94             } else {
95 0         0 Carp::croak "Unsupported logger class $logger";
96             }
97            
98 4         24 $self->on(message => $do_log);
99            
100 4         29 return $self;
101             }
102              
103             1;
104              
105             =head1 NAME
106              
107             Mojo::Log::Role::AttachLogger - Use other loggers for Mojo::Log
108              
109             =head1 SYNOPSIS
110              
111             use Mojo::Log;
112             my $log = Mojo::Log->with_roles('+AttachLogger')->new->unsubscribe('message');
113            
114             # Log::Any
115             use Log::Any::Adapter {category => 'Mojo::Log', message_separator => ' '}, 'Syslog';
116             $log->attach_logger('Log::Any', 'Some::Category');
117            
118             # Log::Contextual
119             use Log::Contextual::WarnLogger;
120             use Log::Contextual -logger => Log::Contextual::WarnLogger->new({env_prefix => 'MYAPP'});
121             $log->attach_logger('Log::Contextual');
122            
123             # Log::Dispatch
124             use Log::Dispatch;
125             my $logger = Log::Dispatch->new(outputs => ['File::Locked',
126             min_level => 'warning',
127             filename => '/path/to/file.log',
128             mode => 'append',
129             newline => 1,
130             callbacks => sub { my %p = @_; '[' . localtime() . '] ' . $p{message} },
131             ]);
132             $log->attach_logger($logger);
133            
134             # Log::Dispatchouli
135             use Log::Dispatchouli;
136             my $logger = Log::Dispatchouli->new({ident => 'MyApp', facility => 'daemon', to_file => 1});
137             $log->attach_logger($logger);
138            
139             # Log::Log4perl
140             use Log::Log4perl;
141             Log::Log4perl->init('/path/to/log.conf');
142             $log->attach_logger('Log::Log4perl', 'Some::Category');
143            
144             =head1 DESCRIPTION
145              
146             L is a L role for L that
147             redirects log messages to an external logging framework. L
148             currently recognizes the strings C, C,
149             C, and objects of the classes C,
150             C, C, and C.
151              
152             The default L event handler is not suppressed by
153             L, so if you want to suppress the default behavior, you
154             should unsubscribe from the message event first. Unsubscribing from the message
155             event will also remove any loggers attached by L.
156              
157             Since L 8.06, the L event will not be sent
158             for messages below the log level set in the L object, so the
159             attached logger will only receive log messages exceeding the configured level.
160              
161             L can be used to attach a logger to the
162             L application logger and suppress the default message event
163             handler.
164              
165             =head1 METHODS
166              
167             L composes the following methods.
168              
169             =head2 attach_logger
170              
171             $log = $log->attach_logger($logger, $options);
172              
173             Subscribes to L and passes log messages to the given
174             logging framework or object. The second argument is optionally a category
175             (default C) or hashref of options. The log level will be prepended
176             to the message in square brackets (except when passing to another L
177             object, or L is false).
178              
179             The following loggers are recognized:
180              
181             =over
182              
183             =item Log::Any
184              
185             The string C will use a global L logger with the specified
186             category (defaults to C).
187              
188             =item Log::Any::Proxy
189              
190             A L object can be passed directly and will be used for logging
191             in the standard manner, using the object's existing category.
192              
193             =item Log::Contextual
194              
195             The string C will use the global L logger.
196             Package loggers are not supported. Note that L
197             may be difficult to use with L logging due to the asynchronous
198             nature of the dispatch cycle.
199              
200             =item Log::Dispatch
201              
202             A L object can be passed to be used for logging. The C
203             log level will be mapped to C.
204              
205             =item Log::Dispatchouli
206              
207             A L object can be passed to be used for logging. The
208             C log level will log messages even if the object is C, but an
209             exception will not be thrown as L normally does.
210              
211             =item Log::Log4perl
212              
213             The string C will use a global L logger with the
214             specified category (defaults to C).
215              
216             =item Mojo::Log
217              
218             Another L object can be passed to be used for logging.
219              
220             =back
221              
222             The following options are supported:
223              
224             =over
225              
226             =item category
227              
228             Category name (defaults to Mojo::Log).
229              
230             =item prepend_level
231              
232             Prepend the log level to messages in the form C<[$level]> (default for
233             non-L loggers). Set false to disable.
234              
235             =item message_separator
236              
237             String to separate multiple messages. Defaults to newline.
238              
239             =back
240              
241             =head1 BUGS
242              
243             Report any issues on the public bugtracker.
244              
245             =head1 AUTHOR
246              
247             Dan Book
248              
249             =head1 COPYRIGHT AND LICENSE
250              
251             This software is Copyright (c) 2017 by Dan Book.
252              
253             This is free software, licensed under:
254              
255             The Artistic License 2.0 (GPL Compatible)
256              
257             =head1 SEE ALSO
258              
259             L, L, L, L,
260             L, L