File Coverage

blib/lib/Log/Emitter.pm
Criterion Covered Total %
statement 47 48 97.9
branch 4 6 66.6
condition 4 9 44.4
subroutine 23 24 95.8
pod 11 12 91.6
total 89 99 89.9


line stmt bran cond sub pod time code
1             package Log::Emitter;
2              
3 2     2   98344 use Carp 'croak';
  2         2  
  2         100  
4 2     2   11 use Fcntl ':flock';
  2         3  
  2         259  
5 2     2   10 use Encode 'find_encoding';
  2         6  
  2         96  
6 2     2   10 use IO::Handle ();
  2         2  
  2         34  
7              
8 2     2   1068 use Moo;
  2         24932  
  2         15  
9 2     2   3242 use MooX::ChainedAttributes;
  2         8301  
  2         12  
10 2     2   5103 use namespace::clean;
  2         22715  
  2         8  
11              
12             with 'Role::EventEmitter';
13              
14             our $VERSION = '0.003';
15              
16             has format => (is => 'rw', lazy => 1, chained => 1, default => sub { \&_format });
17             has handle => (is => 'rw', lazy => 1, chained => 1, clearer => 1, default => sub {
18              
19             # STDERR
20             return \*STDERR unless my $path = shift->path;
21              
22             # File
23             croak qq{Can't open log file "$path": $!} unless open my $file, '>>', $path;
24             return $file;
25             });
26             has history => (is => 'rw', lazy => 1, chained => 1, default => sub { [] });
27             has level => (is => 'rw', lazy => 1, chained => 1, default => 'debug');
28             has max_history_size => (is => 'rw', lazy => 1, chained => 1, default => 10);
29             has path => (is => 'rw', chained => 1, trigger => sub { shift->clear_handle });
30              
31             # Supported log levels
32             my $LEVEL = {debug => 1, info => 2, warn => 3, error => 4, fatal => 5};
33              
34             # Encoding cache
35             my $CACHE;
36              
37 5     5 0 7553 sub BUILD { shift->on(message => \&_message) }
38              
39             sub append {
40 9     9 1 22 my ($self, $msg) = @_;
41              
42 9 50       311 return unless my $handle = $self->handle;
43 9         558 flock $handle, LOCK_EX;
44 9 50       26 $handle->print(_encoding()->encode("$msg")) or croak "Can't write to log: $!";
45 9         657 flock $handle, LOCK_UN;
46             }
47              
48 4     4 1 218 sub debug { shift->_log(debug => @_) }
49 5     5 1 1910 sub error { shift->_log(error => @_) }
50 4     4 1 919 sub fatal { shift->_log(fatal => @_) }
51 2     2 1 1154 sub info { shift->_log(info => @_) }
52              
53 5     5 1 13471 sub is_debug { shift->_now('debug') }
54 5     5 1 3066 sub is_error { shift->_now('error') }
55             # For Log::Contextual compatiblity
56 0     0 1 0 sub is_fatal { 1 }
57 5     5 1 2789 sub is_info { shift->_now('info') }
58 5     5 1 2838 sub is_warn { shift->_now('warn') }
59              
60 1     1 1 888 sub warn { shift->_log(warn => @_) }
61              
62             sub _encoding {
63 9   33 9   111 $CACHE ||= find_encoding('UTF-8') || croak "Unknown encoding 'UTF-8'";
      66        
64             }
65              
66             sub _format {
67 12     12   1046 '[' . localtime(shift) . '] [' . shift() . '] ' . join "\n", @_, '';
68             }
69              
70 16     16   58 sub _log { shift->emit('message', shift, @_) }
71              
72             sub _message {
73 11     11   534 my ($self, $level) = (shift, shift);
74              
75 11 100       27 return unless $self->_now($level);
76              
77 9         1375 my $max = $self->max_history_size;
78 9         1445 my $history = $self->history;
79 9         418 push @$history, my $msg = [time, $level, @_];
80 9         47 shift @$history while @$history > $max;
81              
82 9         292 $self->append($self->format->(@$msg));
83             }
84              
85 31   33 31   1198 sub _now { $LEVEL->{pop()} >= $LEVEL->{$ENV{LOG_EMITTER_LEVEL} || shift->level} }
86              
87             1;
88              
89             =encoding utf8
90              
91             =head1 NAME
92              
93             Log::Emitter - Simple logger
94              
95             =head1 SYNOPSIS
96              
97             use Log::Emitter;
98              
99             # Log to STDERR
100             my $log = Log::Emitter->new;
101              
102             # Customize log file location and minimum log level
103             my $log = Log::Emitter->new(path => '/var/log/emitter.log', level => 'warn');
104              
105             # Log messages
106             $log->debug('Not sure what is happening here');
107             $log->info('FYI: it happened again');
108             $log->warn('This might be a problem');
109             $log->error('Garden variety error');
110             $log->fatal('Boom');
111              
112             =head1 DESCRIPTION
113              
114             L is a simple logger based on L.
115              
116             L is compatible with L for global logging.
117              
118             use Log::Emitter;
119             use Log::Contextual ':log', 'set_logger', -levels => [qw(debug info warn error fatal)];
120             set_logger(Log::Emitter->new);
121            
122             log_info { "Here's some info" };
123             log_error { "Uh-oh, error occured" };
124              
125             =head1 EVENTS
126              
127             L composes all events from L in addition to
128             emitting the following.
129              
130             =head2 message
131              
132             $log->on(message => sub {
133             my ($log, $level, @lines) = @_;
134             ...
135             });
136              
137             Emitted when a new message gets logged.
138              
139             $log->unsubscribe('message');
140             $log->on(message => sub {
141             my ($log, $level, @lines) = @_;
142             say "$level: ", @lines;
143             });
144              
145             =head1 ATTRIBUTES
146              
147             L implements the following attributes.
148              
149             =head2 format
150              
151             my $cb = $log->format;
152             $log = $log->format(sub {...});
153              
154             A callback for formatting log messages.
155              
156             $log->format(sub {
157             my ($time, $level, @lines) = @_;
158             return "[Thu May 15 17:47:04 2014] [info] I ♥ Logging\n";
159             });
160              
161             =head2 handle
162              
163             my $handle = $log->handle;
164             $log = $log->handle(IO::Handle->new);
165              
166             Log filehandle used by default L event, defaults to opening
167             L or C. Reset when L is set.
168              
169             =head2 history
170              
171             my $history = $log->history;
172             $log = $log->history([[time, 'debug', 'That went wrong']]);
173              
174             The last few logged messages.
175              
176             =head2 level
177              
178             my $level = $log->level;
179             $log = $log->level('debug');
180              
181             Active log level, defaults to C. Available log levels are C,
182             C, C, C and C, in that order. Note that the
183             C environment variable can override this value.
184              
185             =head2 max_history_size
186              
187             my $size = $log->max_history_size;
188             $log = $log->max_history_size(5);
189              
190             Maximum number of logged messages to store in L, defaults to C<10>.
191              
192             =head2 path
193              
194             my $path = $log->path
195             $log = $log->path('/var/log/emitter.log');
196              
197             Log file path used by L. Setting this attribute will reset
198             L.
199              
200             =head1 METHODS
201              
202             L composes all methods from L in addition to
203             the following.
204              
205             =head2 new
206              
207             my $log = Log::Emitter->new;
208              
209             Construct a new L object and subscribe to L event
210             with default logger.
211              
212             =head2 append
213              
214             $log->append("[Thu May 15 17:47:04 2014] [info] I ♥ Logging\n");
215              
216             Append message to L.
217              
218             =head2 debug
219              
220             $log = $log->debug('You screwed up, but that is ok');
221             $log = $log->debug('All', 'cool');
222              
223             Emit L event and log debug message.
224              
225             =head2 error
226              
227             $log = $log->error('You really screwed up this time');
228             $log = $log->error('Wow', 'seriously');
229              
230             Emit L event and log error message.
231              
232             =head2 fatal
233              
234             $log = $log->fatal('Its over...');
235             $log = $log->fatal('Bye', 'bye');
236              
237             Emit L event and log fatal message.
238              
239             =head2 info
240              
241             $log = $log->info('You are bad, but you prolly know already');
242             $log = $log->info('Ok', 'then');
243              
244             Emit L event and log info message.
245              
246             =head2 is_debug
247              
248             my $bool = $log->is_debug;
249              
250             Check for debug log level.
251              
252             =head2 is_error
253              
254             my $bool = $log->is_error;
255              
256             Check for error log level.
257              
258             =head2 is_fatal
259              
260             my $bool = $log->is_fatal;
261              
262             Always true.
263              
264             =head2 is_info
265              
266             my $bool = $log->is_info;
267              
268             Check for info log level.
269              
270             =head2 is_warn
271              
272             my $bool = $log->is_warn;
273              
274             Check for warn log level.
275              
276             =head2 warn
277              
278             $log = $log->warn('Dont do that Dave...');
279             $log = $log->warn('No', 'really');
280              
281             Emit L event and log warn message.
282              
283             =head1 BUGS
284              
285             Report any issues on the public bugtracker.
286              
287             =head1 AUTHOR
288              
289             Dan Book
290              
291             =head1 COPYRIGHT AND LICENSE
292              
293             This software is Copyright (c) 2015 by Dan Book.
294              
295             This is free software, licensed under:
296              
297             The Artistic License 2.0 (GPL Compatible)
298              
299             =head1 SEE ALSO
300              
301             L, L
302              
303             =for Pod::Coverage BUILD