File Coverage

blib/lib/Mojo/Log/Role/AttachLogger.pm
Criterion Covered Total %
statement 39 77 50.6
branch 13 52 25.0
condition 7 18 38.8
subroutine 8 13 61.5
pod 1 1 100.0
total 68 161 42.2


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