package Mojo::Log; use Mojo::Base 'Mojo::EventEmitter'; use Carp 'croak'; use Fcntl ':flock'; use Mojo::Util 'encode'; has format => sub { \&_format }; has handle => sub { # File if (my $path = shift->path) { croak qq{Can't open log file "$path": $!} unless open my $file, '>>', $path; return $file; } # STDERR return \*STDERR; }; has history => sub { [] }; has level => 'debug'; has max_history_size => 10; has 'path'; # Supported log level my $LEVEL = {debug => 1, info => 2, warn => 3, error => 4, fatal => 5}; sub append { my ($self, $msg) = @_; return unless my $handle = $self->handle; flock $handle, LOCK_EX; $handle->print(encode('UTF-8', $msg)) or croak "Can't write to log: $!"; flock $handle, LOCK_UN; } sub debug { shift->log(debug => @_) } sub error { shift->log(error => @_) } sub fatal { shift->log(fatal => @_) } sub info { shift->log(info => @_) } sub is_debug { shift->is_level('debug') } sub is_error { shift->is_level('error') } sub is_fatal { shift->is_level('fatal') } sub is_info { shift->is_level('info') } sub is_level { $LEVEL->{lc pop} >= $LEVEL->{$ENV{MOJO_LOG_LEVEL} || shift->level}; } sub is_warn { shift->is_level('warn') } sub log { shift->emit('message', lc shift, @_) } sub new { my $self = shift->SUPER::new(@_); $self->on(message => \&_message); return $self; } sub warn { shift->log(warn => @_) } sub _format { '[' . localtime(shift) . '] [' . shift() . '] ' . join("\n", @_, ''); } sub _message { my ($self, $level) = (shift, shift); return unless $self->is_level($level); my $max = $self->max_history_size; my $history = $self->history; push @$history, my $msg = [time, $level, @_]; shift @$history while @$history > $max; $self->append($self->format->(@$msg)); } 1; =encoding utf8 =head1 NAME Mojo::Log - Simple logger =head1 SYNOPSIS use Mojo::Log; # Log to STDERR my $log = Mojo::Log->new; # Customize log file location and minimum log level my $log = Mojo::Log->new(path => '/var/log/mojo.log', level => 'warn'); # Log messages $log->debug('Why is this not working?'); $log->info('FYI: it happened again.'); $log->warn('This might be a problem.'); $log->error('Garden variety error.'); $log->fatal('Boom!'); =head1 DESCRIPTION L is a simple logger for L projects. =head1 EVENTS L inherits all events from L and can emit the following new ones. =head2 message $log->on(message => sub { my ($log, $level, @lines) = @_; ... }); Emitted when a new message gets logged. $log->unsubscribe('message'); $log->on(message => sub { my ($log, $level, @lines) = @_; say "$level: ", @lines; }); =head1 ATTRIBUTES L implements the following attributes. =head2 format my $cb = $log->format; $log = $log->format(sub {...}); A callback for formatting log messages. $log->format(sub { my ($time, $level, @lines) = @_; return "[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious.\n"; }); =head2 handle my $handle = $log->handle; $log = $log->handle(IO::Handle->new); Log filehandle used by default L event, defaults to opening L or C. =head2 history my $history = $log->history; $log = $log->history([[time, 'debug', 'That went wrong.']]); The last few logged messages. =head2 level my $level = $log->level; $log = $log->level('debug'); Active log level, defaults to C. Available log levels are C, C, C, C and C, in that order. Note that the C environment variable can override this value. =head2 max_history_size my $size = $log->max_history_size; $log = $log->max_history_size(5); Maximum number of logged messages to store in L, defaults to C<10>. =head2 path my $path = $log->path $log = $log->path('/var/log/mojo.log'); Log file path used by L. =head1 METHODS L inherits all methods from L and implements the following new ones. =head2 append $log->append("[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious.\n"); Append message to L. =head2 debug $log = $log->debug('You screwed up, but that is ok.'); $log = $log->debug('All', 'cool!'); Log debug message. =head2 error $log = $log->error('You really screwed up this time.'); $log = $log->error('Wow', 'seriously!'); Log error message. =head2 fatal $log = $log->fatal('Its over...'); $log = $log->fatal('Bye', 'bye!'); Log fatal message. =head2 info $log = $log->info('You are bad, but you prolly know already.'); $log = $log->info('Ok', 'then!'); Log info message. =head2 is_level my $bool = $log->is_level('debug'); Check log level. =head2 is_debug my $bool = $log->is_debug; Check for debug log level. =head2 is_error my $bool = $log->is_error; Check for error log level. =head2 is_fatal my $bool = $log->is_fatal; Check for fatal log level. =head2 is_info my $bool = $log->is_info; Check for info log level. =head2 is_warn my $bool = $log->is_warn; Check for warn log level. =head2 log $log = $log->log(debug => 'This should work.'); $log = $log->log(debug => 'This', 'too!'); Emit L event. =head2 new my $log = Mojo::Log->new; Construct a new L object and subscribe to L event with default logger. =head2 warn $log = $log->warn('Dont do that Dave...'); $log = $log->warn('No', 'really!'); Log warn message. =head1 SEE ALSO L, L, L. =cut