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);
        }

t/04-log.t  view on Meta::CPAN

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 )