File Coverage

blib/lib/MooseX/Role/Loggable.pm
Criterion Covered Total %
statement 51 51 100.0
branch 12 12 100.0
condition 3 3 100.0
subroutine 12 12 100.0
pod 2 2 100.0
total 80 80 100.0


line stmt bran cond sub pod time code
1 6     6   66097 use strict;
  6         15  
  6         242  
2 6     6   35 use warnings;
  6         12  
  6         345  
3             package MooseX::Role::Loggable;
4             {
5             $MooseX::Role::Loggable::VERSION = '0.112';
6             }
7             # ABSTRACT: Extensive, yet simple, logging role using Log::Dispatchouli
8              
9 6     6   31 use Carp;
  6         11  
  6         430  
10 6     6   5123 use Safe::Isa;
  6         2663  
  6         917  
11 6     6   35 use Moo::Role;
  6         10  
  6         39  
12 6     6   8368 use MooX::Types::MooseLike::Base qw<Bool Str>;
  6         43489  
  6         628  
13 6     6   5850 use Sub::Quote 'quote_sub';
  6         24108  
  6         381  
14 6     6   6758 use Log::Dispatchouli;
  6         319088  
  6         211  
15 6     6   5810 use namespace::sweep;
  6         126506  
  6         47  
16              
17             my %attr_meth_map = (
18             logger_facility => 'facility',
19             logger_ident => 'ident',
20             log_to_file => 'to_file',
21             log_to_stdout => 'to_stdout',
22             log_to_stderr => 'to_stderr',
23             log_fail_fatal => 'fail_fatal',
24             log_muted => 'muted',
25             log_quiet_fatal => 'quiet_fatal',
26             );
27              
28             has debug => (
29             is => 'ro',
30             isa => Bool,
31             default => sub {0},
32             );
33              
34             has logger_facility => (
35             is => 'ro',
36             isa => Str,
37             default => sub {'local6'},
38             );
39              
40             has logger_ident => (
41             is => 'ro',
42             isa => Str,
43             default => sub { ref shift },
44             );
45              
46             has log_to_file => (
47             is => 'ro',
48             isa => Bool,
49             default => sub {0},
50             );
51              
52             has log_to_stdout => (
53             is => 'ro',
54             isa => Bool,
55             default => sub {0},
56             );
57              
58             has log_to_stderr => (
59             is => 'ro',
60             isa => Bool,
61             default => sub {0},
62             );
63              
64             has log_file => (
65             is => 'ro',
66             isa => Str,
67             predicate => 'has_log_file',
68             );
69              
70             has log_path => (
71             is => 'ro',
72             isa => Str,
73             predicate => 'has_log_path',
74             );
75              
76             has log_pid => (
77             is => 'ro',
78             isa => Bool,
79             default => sub {1},
80             );
81              
82             has log_fail_fatal => (
83             is => 'ro',
84             isa => Bool,
85             default => sub {1},
86             );
87              
88             has log_muted => (
89             is => 'ro',
90             isa => Bool,
91             default => sub {0},
92             );
93              
94             has log_quiet_fatal => (
95             is => 'ro',
96             isa => quote_sub(q{
97             use Safe::Isa;
98             $_[0] || $_[0]->$_isa( ref [] )
99             or die "$_[0] must be a string or arrayref"
100             }),
101             default => sub {'stderr'},
102             );
103              
104             has logger => (
105             is => 'lazy',
106             isa => quote_sub(q{
107             use Safe::Isa;
108             $_[0]->$_isa('Log::Dispatchouli') ||
109             $_[0]->$_isa('Log::Dispatchouli::Proxy')
110             or die "$_[0] must be a Log::Dispatchouli object";
111             }),
112              
113             handles => [ qw/
114             log log_fatal log_debug
115             set_debug clear_debug set_prefix clear_prefix set_muted clear_muted
116             / ],
117             );
118              
119             sub _build_logger {
120 7     7   6006 my $self = shift;
121 7         21 my %optional = ();
122              
123 7         17 foreach my $option ( qw<log_file log_path> ) {
124 14         105 my $method = "has_$option";
125 14 100       84 if ( $self->$method ) {
126 1         7 $optional{$option} = $self->$option;
127             }
128             }
129              
130 7         216 my $logger = Log::Dispatchouli->new( {
131             debug => $self->debug,
132             ident => $self->logger_ident,
133             facility => $self->logger_facility,
134             to_file => $self->log_to_file,
135             to_stdout => $self->log_to_stdout,
136             to_stderr => $self->log_to_stderr,
137             log_pid => $self->log_pid,
138             fail_fatal => $self->log_fail_fatal,
139             muted => $self->log_muted,
140             quiet_fatal => $self->log_quiet_fatal,
141             %optional,
142             } );
143              
144 7         103092 return $logger;
145             }
146              
147             # if we already have a logger, use its values
148             sub BUILDARGS {
149 13     13 1 98906 my $class = shift;
150 13         43 my %args = @_;
151 13         67 my @items = qw<
152             debug logger_facility logger_ident
153             log_to_file log_to_stdout log_to_stderr log_file log_path
154             log_pid log_fail_fatal log_muted log_quiet_fatal
155             >;
156              
157 13 100       61 if ( exists $args{'logger'} ) {
158 5 100 100     21 $args{'logger'}->$_isa('Log::Dispatchouli') ||
159             $args{'logger'}->$_isa('Log::Dispatchouli::Proxy')
160             or croak 'logger must be a Log::Dispatchouli object';
161              
162 4         87 foreach my $item (@items) {
163             # if value is overridden, don't touch it
164 48 100       112 my $attr = exists $attr_meth_map{$item} ?
165             $attr_meth_map{$item} :
166             $item;
167              
168 48 100       97 if ( exists $args{$item} ) {
169             # override logger configuration
170 1         5 $args{'logger'}{$attr} = $args{$item};
171             } else {
172             # override our attributes if it's in logger
173 47 100       132 exists $args{'logger'}{$attr}
174             and $args{$item} = $args{'logger'}{$attr};
175             }
176             }
177             }
178              
179 12         313 return {%args};
180             }
181              
182             sub log_fields {
183 1     1 1 1567 my $self = shift;
184 1         3 my $warning =
185             '[MooseX::Role::Loggable] Calling ->log_fields() is deprecated, ' .
186             'it will be removed in the next version';
187              
188 1         9 $self->log( { level => 'warning' }, $warning );
189 1         2361 carp $warning;
190              
191 1         571 return ( logger => $self->logger );
192             }
193              
194             1;
195              
196              
197              
198             =pod
199              
200             =head1 NAME
201              
202             MooseX::Role::Loggable - Extensive, yet simple, logging role using Log::Dispatchouli
203              
204             =head1 VERSION
205              
206             version 0.112
207              
208             =head1 SYNOPSIS
209              
210             package My::Object;
211              
212             use Moose; # or Moo
213             with 'MooseX::Role::Loggable';
214              
215             sub do_this {
216             my $self = shift;
217             $self->set_prefix('[do_this] ');
218             $self->log_debug('starting...');
219             ...
220             $self->log_debug('more stuff');
221             $self->clear_prefix;
222             }
223              
224             =head1 DESCRIPTION
225              
226             This is a role to provide logging ability to whoever consumes it using
227             L<Log::Dispatchouli>. Once you consume this role, you have attributes and
228             methods for logging defined automatically.
229              
230             package MyObject;
231             use Moose # Moo works too
232             with 'MooseX::Role::Loggable';
233              
234             sub run {
235             my $self = shift;
236              
237             $self->log('Trying to do something');
238              
239             # this only gets written if debug flag is on
240             $self->log_debug('Some debugging output');
241              
242             $self->log(
243             { level => 'critical' },
244             'Critical log message',
245             );
246              
247             $self->log_fatal('Log and die');
248             }
249              
250             This module uses L<Moo> so it takes as little resources as it can by default,
251             and can seamlessly work with both L<Moo> or L<Moose>.
252              
253             =head1 Propagating logging definitions
254              
255             Sometimes your objects create additional object which might want to log
256             using the same settings. You can simply give them the same logger object.
257              
258             package Parent;
259             use Moose;
260             with 'MooseX::Role::Loggable';
261              
262             has child => (
263             is => 'ro',
264             isa => 'Child',
265             lazy => 1,
266             builder => '_build_child',
267             );
268              
269             sub _build_child {
270             my $self = shift;
271             return Child->new( logger => $self->logger );
272             }
273              
274             =head1 ATTRIBUTES
275              
276             =head2 debug
277              
278             A boolean for whether you're in debugging mode or not.
279              
280             Default: B<no>.
281              
282             Read-only.
283              
284             =head2 logger_facility
285              
286             The facility the logger would use. This is useful for syslog.
287              
288             Default: B<local6>.
289              
290             =head2 logger_ident
291              
292             The ident the logger would use. This is useful for syslog.
293              
294             Default: B<calling object's class name>.
295              
296             Read-only.
297              
298             =head2 log_to_file
299              
300             A boolean that determines if the logger would log to a file.
301              
302             Default location of the file is in F</tmp>.
303              
304             Default: B<no>.
305              
306             Read-only.
307              
308             =head2 log_to_stdout
309              
310             A boolean that determines if the logger would log to STDOUT.
311              
312             Default: B<no>.
313              
314             =head2 log_to_stderr
315              
316             A boolean that determines if the logger would log to STDERR.
317              
318             Default: B<no>.
319              
320             =head2 log_file
321              
322             The leaf name for the log file.
323              
324             Default: B<undef>
325              
326             =head2 log_path
327              
328             The path for the log file.
329              
330             Default: B<undef>
331              
332             =head2 log_pid
333              
334             Whether to append the PID to the log filename.
335              
336             Default: B<yes>
337              
338             =head2 log_fail_fatal
339              
340             Whether failure to log is fatal.
341              
342             Default: B<yes>
343              
344             =head2 log_muted
345              
346             Whether only fatals are logged.
347              
348             Default: B<no>
349              
350             =head2 log_quiet_fatal
351              
352             From L<Log::Dispatchouli>:
353             I<'stderr' or 'stdout' or an arrayref of zero, one, or both fatal log messages
354             will not be logged to these>.
355              
356             Default: B<stderr>
357              
358             =head2 logger
359              
360             A L<Log::Dispatchouli> object.
361              
362             =head1 METHODS
363              
364             All methods here are imported from L<Log::Dispatchouli>. You can read its
365             documentation to understand them better.
366              
367             =head2 log
368              
369             Log a message.
370              
371             =head2 log_debug
372              
373             Log a message only if in debug mode.
374              
375             =head2 log_fatal
376              
377             Log a message and die.
378              
379             =head2 set_debug
380              
381             Set the debug flag.
382              
383             =head2 clear_debug
384              
385             Clear the debug flag.
386              
387             =head2 set_prefix
388              
389             Set a prefix for all next messages.
390              
391             =head2 clear_prefix
392              
393             Clears the prefix for all next messages.
394              
395             =head2 set_muted
396              
397             Sets the mute property, which makes only fatal messages logged.
398              
399             =head2 clear_muted
400              
401             Clears the mute property.
402              
403             =head2 BUILDARGS
404              
405             You shouldn't care about this. It takes care of propagating attributes
406             from a given logger (if you provided one) to the attributes this role provides.
407              
408             =head2 log_fields
409              
410             B<DEPRECATED>.
411              
412             Please pass the logger attribute instead:
413              
414             SomeObject->new( logger => $parent->logger );
415              
416             =head1 DEBUGGING
417              
418             Occassionally you might encounter the following error:
419              
420             no ident specified when using Log::Dispatchouli at Loggable.pm line 117.
421              
422             The problem does not stem from L<MooseX::Role::Loggable>, but from a builder
423             calling a logging method before the logger is built. Since L<Moo> and L<Moose>
424             do not assure order of building attributes, which means that some attributes
425             might not exist by the time you need them.
426              
427             This specific error happens when the C<ident> attribute isn't built by the
428             time a builder runs. In order to avoid it, the attribute which uses the builder
429             should be made lazy, and then called in the C<BUILD> method. Here is an
430             example:
431              
432             package Stuff;
433              
434             use Moose;
435             with 'MooseX::Role::Logger';
436              
437             has db => (
438             is => 'ro',
439             lazy => 1,
440             builder => '_build_db',
441             }
442              
443             sub _build_db {
444             my $self = shift;
445             $self->log_debug('Building DB');
446             ...
447             }
448              
449             sub BUILD {
450             my $self = shift;
451             $self->db;
452             }
453              
454             This makes the C<db> attribute non-lazy, but during run-time. This will assure
455             that all the logging attributes are created B<before> you build the C<db>
456             attribute and call C<log_debug>.
457              
458             =head1 AUTHOR
459              
460             Sawyer X <xsawyerx@cpan.org>
461              
462             =head1 COPYRIGHT AND LICENSE
463              
464             This software is copyright (c) 2012 by Sawyer X.
465              
466             This is free software; you can redistribute it and/or modify it under
467             the same terms as the Perl 5 programming language system itself.
468              
469             =cut
470              
471              
472             __END__
473