Acme-Ghost
view release on metacpan or search on metacpan
lib/Acme/Ghost.pm view on Meta::CPAN
B<NOTE:> Internal use only for subclasses!
=head2 daemonize
$g = $g->daemonize;
Main routine for just daemonize.
This routine will check on the pid file, safely fork, create the pid file (storing the pid in the file),
become another user and group, close STDIN, STDOUT and STDERR, separate from the process group (become session leader),
and install $SIG{INT} to remove the pid file. In otherwords - daemonize.
All errors result in a die
=head2 filepid
my $filepid = $g->filepid;
This method returns L<Acme::Ghost::FilePid> object
=head2 flush
$self = $self->flush;
lib/Acme/Ghost.pm view on Meta::CPAN
This method returns PID of the daemon
=head2 set_gid
$g = $g->set_gid('1000 10001 10002');
$g = $g->set_gid(1000);
$g = $g->set_gid('nogroup');
$g = $g->set_gid;
Become another group. Arguments are groups (or group ids or space delimited list of group ids). All errors die
=head2 set_uid
$g = $g->set_uid(1000);
$g = $g->set_uid('nobody');
$g = $g->set_uid;
Become another user. Argument is user (or userid). All errors die
=head1 CONTROL METHODS
List of LSB Daemon Control Methods
These methods can be used to control the daemon behavior.
Every effort has been made to have these methods DWIM (Do What I Mean),
so that you can focus on just writing the code for your daemon.
=head2 ctrl
lib/Acme/Ghost/Log.pm view on Meta::CPAN
=head1 NAME
Acme::Ghost::Log - Simple logger
=head1 SYNOPSIS
use Acme::Ghost::Log;
my $log = Acme::Ghost::Log->new();
$log->error("My test error message to syslog")
# Using file
my $log = Acme::Ghost::Log->new(file => '/tmp/test.log');
$log->error("My test error message to /tmp/test.log")
# Customize minimum log level
my $log = Acme::Ghost::Log->new(level => 'warn');
# Log messages
$log->trace('Doing stuff');
$log->debug('Not sure what is happening here');
$log->info('FYI: it happened again');
$log->warn('This might be a problem');
$log->error('Garden variety error');
$log->fatal('Boom');
=head1 DESCRIPTION
Acme::Ghost::Log is a simple logger for Acme::Ghost logging after daemonization
=head2 new
my $log = Acme::Ghost::Log->new(
logopt => 'ndelay,pid',
facility => 'user',
level => 'debug',
ident => 'test.pl',
);
With default attributes
use Mojo::Log;
my $log = Acme::Ghost::Log->new( logger => Mojo::Log->new );
$log->error("Test error message");
This is example with external loggers
=head1 ATTRIBUTES
This class implements the following attributes
=head2 facility
This attribute sets facility for logging
lib/Acme/Ghost/Log.pm view on Meta::CPAN
Log filehandle, defaults to opening "file" or uses syslog if file not specified
=head2 ident
The B<ident> is prepended to every message
Default: script name C<basename($0)>
=head2 level
There are six predefined log levels: C<fatal>, C<error>, C<warn>, C<info>, C<debug>, and C<trace> (in descending priority).
The syslog supports followed additional log levels: C<emerg>, C<alert>, C<crit'> and C<notice> (in descending priority).
But we recommend not using them to maintain compatibility.
Your configured logging level has to at least match the priority of the logging message.
If your configured logging level is C<warn>, then messages logged with info(), debug(), and trace()
will be suppressed; fatal(), error() and warn() will make their way through, because their
priority is higher or equal than the configured setting.
Default: C<debug>
See also L<Sys::Syslog/Levels>
=head2 logger
This attribute perfoms to set predefined logger, eg. Mojo::Log
lib/Acme/Ghost/Log.pm view on Meta::CPAN
Log C<debug> message
=head2 emerg
$log->emerg('System is unusable');
$log->emerg('To', 'die');
Log C<emerg> message
=head2 error
$log->error('You really screwed up this time');
$log->error('Wow', 'seriously');
Log C<error> message
=head2 fatal
$log->fatal('Its over...');
$log->fatal('Bye', 'bye');
Log C<fatal> message
=head2 info
lib/Acme/Ghost/Log.pm view on Meta::CPAN
$log->info('Ok', 'then');
Log C<info> message
=head2 level
my $level = $log->level;
$log = $log->level('debug');
Active log level, defaults to debug.
Available log levels are C<trace>, C<debug>, C<info>, C<notice>, C<warn>, C<error>,
C<fatal> (C<crit>), C<alert> and C<emerg>, in that order
=head2 logger
my $logger = $log->logger;
This method returns the logger object or undef if not exists
=head2 notice
lib/Acme/Ghost/Log.pm view on Meta::CPAN
LOGOPTS => 'ndelay,pid', # For Sys::Syslog
SEPARATOR => ' ',
LOGFORMAT => '%s',
};
my %LOGLEVELS = (
'trace' => Sys::Syslog::LOG_DEBUG, # 7 debug-level message
'debug' => Sys::Syslog::LOG_DEBUG, # 7 debug-level message
'info' => Sys::Syslog::LOG_INFO, # 6 informational message
'notice' => Sys::Syslog::LOG_NOTICE, # 5 normal, but significant, condition
'warn' => Sys::Syslog::LOG_WARNING, # 4 warning conditions
'error' => Sys::Syslog::LOG_ERR, # 3 error conditions
'fatal' => Sys::Syslog::LOG_CRIT, # 2 critical conditions
'crit' => Sys::Syslog::LOG_CRIT, # 2 critical conditions
'alert' => Sys::Syslog::LOG_ALERT, # 1 action must be taken immediately
'emerg' => Sys::Syslog::LOG_EMERG, # 0 system is unusable
);
my %MAGIC = (
'trace' => 8,
'debug' => 7,
'info' => 6,
'notice' => 5,
'warn' => 4,
'error' => 3,
'fatal' => 2, 'crit' => 2,
'alert' => 1,
'emerg' => 0,
);
my %SHORT = ( # Log::Log4perl::Level notation
0 => 'fatal', 1 => 'fatal', 2 => 'fatal',
3 => 'error',
4 => 'warn',
5 => 'info', 6 => 'info',
7 => 'debug',
8 => 'trace',
);
my $ENCODING = find_encoding('UTF-8') or croak qq/Encoding "UTF-8" not found/;
sub new {
my $class = shift;
lib/Acme/Ghost/Log.pm view on Meta::CPAN
}
sub logger { shift->{logger} }
sub handle { shift->{handle} }
sub provider { shift->{provider} }
sub trace { shift->_log('trace', @_) }
sub debug { shift->_log('debug', @_) }
sub info { shift->_log('info', @_) }
sub notice { shift->_log('notice', @_) }
sub warn { shift->_log('warn', @_) }
sub error { shift->_log('error', @_) }
sub fatal { shift->_log('fatal', @_) }
sub crit { shift->_log('crit', @_) }
sub alert { shift->_log('alert', @_) }
sub emerg { shift->_log('emerg', @_) }
sub _log {
my ($self, $level, @msg) = @_;
my $req = $MAGIC{$self->level};
my $mag = $MAGIC{$level} // 7;
return 0 unless $mag <= $req;
lib/Acme/Ghost/Prefork.pm view on Meta::CPAN
sub finish { } # Emitted when the server shuts down
sub heartbeat { } # Emitted when a heartbeat message has been received from a spirit
sub reap { } # Emitted when a child process exited
sub spawn { } # Emitted when a spirit process is spawned
sub waitup { } # Emitted when the manager starts waiting for new heartbeat messages
sub spirit {
my $self = shift;
my $cb = $self->{spirit_cb};
return unless $cb;
return $self->$cb if ref($cb) eq 'CODE';
$self->log->error("Callback `spirit` is incorrect");
$self->tick(1);
}
# Internal methods
sub _increase { # Manager level
my $self = shift;
$self->log->debug(sprintf("> Increase spirit pool by one")) if DEBUG;
$self->{spirits} = $self->{spirits} + 1;
}
sub _decrease { # Manager level
lib/Acme/Ghost/Prefork.pm view on Meta::CPAN
}
sub _stopped { # Manager level (Calls when a child process exited)
my $self = shift;
my $pid = shift;
$self->log->debug(sprintf("> Reap %s", $pid)) if DEBUG;
$self->reap($pid);
return unless my $w = delete $self->{pool}{$pid};
$self->log->info("Spirit $pid stopped");
unless ($w->{healthy}) {
$self->log->error("Spirit $pid stopped too early, shutting down");
$self->_stop;
}
}
sub _manage { # Manager level
my $self = shift;
# Spawn more spirits if necessary
if (!$self->{finished}) { # No finished
my $graceful = grep { $_->{graceful} } values %{$self->{pool}}; # Number gracefuled spirits
my $spare = $self->{spare};
lib/Acme/Ghost/Prefork.pm view on Meta::CPAN
my $interval = $self->{heartbeat_interval};
my $hb_to = $self->{heartbeat_timeout};
my $gf_to = $self->{graceful_timeout};
my $now = Time::HiRes::time;
my $log = $self->log;
for my $pid (keys %{$self->{pool}}) {
next unless my $w = $self->{pool}{$pid}; # Get spirit struct
# No heartbeat (graceful stop)
if (!$w->{graceful} && ($w->{time} + $interval + $hb_to <= $now)) {
$log->error("Spirit $pid has no heartbeat ($hb_to seconds), restarting");
$w->{graceful} = $now;
}
# Graceful stop with timeout
my $graceful = $w->{graceful} ||= $self->{gracefully_stop} ? $now : undef;
if ($graceful && !$w->{attempt}) {
$w->{attempt}++;
$log->info("Stopping spirit $pid gracefully ($gf_to seconds)");
kill 'QUIT', $pid or $self->_stopped($pid);
}
use strict;
use utf8;
use Test::More;
use_ok qw/Acme::Ghost::Log/;
# Error message with debug loglevel
{
my $log = Acme::Ghost::Log->new();
is $log->level, 'debug', "Debug LogLevel";
ok $log->error("My test error message"), 'Error message';
}
# Info and fatal message with eror loglevel
{
my $log = Acme::Ghost::Log->new(level => 'error');
is $log->level, 'error', "Error LogLevel";
ok !$log->info("My test info message"), 'Info message not allowed';
ok $log->fatal("My test fatal message"), 'Fatal message';
#note explain $log;
}
# Fake Logger
{
my $fake = FakeLogger->new;
my $log = Acme::Ghost::Log->new(logger => $fake);
$log->error("Test error message") and ok 1, "Test error message to STDOUT";
#ok $log->debug("Test debug message");
$log->info("Test info message") and ok 1, "Test info message to STDOUT";
#note explain $log;
}
# File
{
my $log = Acme::Ghost::Log->new(file => 'log.tmp');
$log->error("Test error message") and ok 1, "Test error message to file";
$log->warn("ТеÑÑовое ÑообÑение") and ok 1, "Test error message to file (RU)";
$log->info("Test info message") and ok 1, "Test info message to file";
}
done_testing;
1;
package FakeLogger;
sub new { bless {}, shift }
sub info { printf "# Info[$$] %s\n", pop @_ }
sub error { printf "# Error[$$] %s\n", pop @_ }
1;
__END__
prove -lv t/04-log.t
( run in 0.764 second using v1.01-cache-2.11-cpan-65fba6d93b7 )