Acrux

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

t/00-pod.t                              Checking all POD documents
t/00-trailingspace.t                    Style test: TrailingSpace
t/01-use.t                              Test script
t/02-util.t                             Utilities
t/03-refutil.t                          RefUtil test
t/04-pointer.t                          Pointer test
t/05-filepid.t                          FilePid interface
t/06-digest-m11r.t                      M11R Digest test
t/07-digest-fnv32a.t                    FNV32a Digest test
t/08-config.t                           Config test
t/09-log.t                              Logger test
t/10-app-min.t                          App with a minimum number of args
t/11-strf.t                             The strf() function
t/test.conf                             Test file of config

# Examples
eg/acrux_lite.pl
eg/acrux_log.pl
eg/acrux_std.pl
eg/acrux_test.pl
META.yml                                 Module YAML meta-data (added by MakeMaker)
META.json                                Module JSON meta-data (added by MakeMaker)

META.json  view on Meta::CPAN

      },
      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : "0"
         }
      },
      "runtime" : {
         "requires" : {
            "Config::General" : "2",
            "Sub::Util" : "1.41",
            "Sys::Syslog" : "0",
            "perl" : "5.020"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "homepage" : "https://sourceforge.net/projects/acrux/",
      "license" : [
         "https://dev.perl.org/licenses"
      ],

META.yml  view on Meta::CPAN

  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: '1.4'
name: Acrux
no_index:
  directory:
    - t
    - inc
requires:
  Config::General: '2'
  Sub::Util: '1.41'
  Sys::Syslog: '0'
  perl: '5.020'
resources:
  homepage: https://sourceforge.net/projects/acrux/
  license: https://dev.perl.org/licenses
  repository: https://abalama@git.code.sf.net/p/acrux/code
version: '0.06'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

Makefile.PL  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;
use ExtUtils::MakeMaker;

my $build_requires = {
        'ExtUtils::MakeMaker'   => 6.60,
        'Test::More'            => 0.94,
    };

my $prereq_pm = {
        'Sys::Syslog'           => 0,
        'Config::General'       => 2.00,    # Ubuntu = libconfig-general-perl       RHEL = perl-Config-General
        'Sub::Util'             => 1.41,
    };

WriteMakefile(
    'NAME'              => 'Acrux',
    'MIN_PERL_VERSION'  => 5.020001,
    'VERSION_FROM'      => 'lib/Acrux.pm',
    'ABSTRACT_FROM'     => 'lib/Acrux.pm',
    'BUILD_REQUIRES'    => $build_requires,

Makefile.PL  view on Meta::CPAN

            homepage    => 'https://sourceforge.net/projects/acrux/',
            license     => 'https://dev.perl.org/licenses',
            repository      => {
                    type => 'git',
                    url  => 'https://abalama@git.code.sf.net/p/acrux/code',
                    web  => 'https://sourceforge.net/p/acrux/code/ci/master/tree/',
                  },
        },
    },
    clean => {
        FILES => '*.tmp *.log *.pid',
    },
);

1;

eg/acrux_log.pl  view on Meta::CPAN

#!/usr/bin/perl -w
use strict;
use utf8;

use Acrux::Log;
use IO::Handle;

my $log = Acrux::Log->new(
    handle => IO::Handle->new_from_fd(fileno(STDOUT), "w"),
    level  => 'trace',
    #short  => 1,
    color  => 1,
    #format => sub {
    #    my ($time, $level, @lines) = @_;
    #    return "[$time] [$level] " . join (' ', @lines) . "\n";
    #}
);

$log->trace('Whatever');
$log->debug('You screwed up, but that is ok');
$log->info('You are bad, but you prolly know already');
$log->notice('Normal, but significant, condition...');
$log->warn('Dont do that Dave...');
$log->error('You really screwed up this time');
$log->fatal('Its over...');
$log->crit('Its over...');
$log->alert('Action must be taken immediately');
$log->emerg('System is unusable');

__END__

eg/acrux_test.pl  view on Meta::CPAN

### CODE:
    my ($self, $meta, @args) = @_;
    $self->test; # Call created method

    #print dumper(
    #        "App:"  => $self,
    #        "Meta:" => $meta,
    #        "Args:" => \@args,
    #    );

    $self->log->debug(sprintf('Config value "/deep/foo/bar/test": >%s<',
        $self->config->get("/deep/foo/bar/test")));

    # Test log
    #$self->log->trace('Whatever');
    #$self->log->debug('You screwed up, but that is ok');
    #$self->log->info('You are bad, but you prolly know already');
    #$self->log->notice('Normal, but significant, condition...');
    #$self->log->warn('Dont do that Dave...');
    #$self->log->error('You really screwed up this time');
    #$self->log->fatal('Its over...');
    #$self->log->crit('Its over...');
    #$self->log->alert('Action must be taken immediately');
    #$self->log->emerg('System is unusable');

    return 1;
});

1;

package main;

use Getopt::Long;
use IO::Handle;

eg/acrux_test.pl  view on Meta::CPAN

    project     => 'MyApp',
    preload     => [qw/Config Log/], # disable preloading all system plugins
    options     => $options,
    root        => '.',
    configfile  => $options->{config} // 't/test.conf',
    verbose     => $options->{verbose},
    debug       => $options->{debug},
    test        => $options->{test},

    #config_noload => 1,
    loghandle   => IO::Handle->new_from_fd(fileno(STDOUT), "w"),
    logcolorize => 1

    # ($options->{datadir} ? (datadir => $options->{datadir}) : ()),

);

# Check command
unless (grep {$_ eq $command} (@{ $app->handlers(1) })) {
    die color("bright_red" => "No handler $command found") . "\n";
}

eg/acrux_test.pl  view on Meta::CPAN


use parent 'Acme::Crux::Plugin';

use Acrux::Util qw/color/;

sub register {
    my ($self, $app, $args) = @_;
    print sprintf(color(bright_magenta => "Registered %s plugin"), $self->name), "\n";

    $app->register_method(test => sub { print color(red => "This test method created in plugin register"), "\n" });
    $app->log->debug(sprintf("Registered %s plugin", $self->name));

    return sprintf 'Ok! I am %s plugin!', $self->name;
}

1;

__END__

lib/Acme/Crux.pm  view on Meta::CPAN

        options     => {foo => 'bar'},

        plugins     => { foo => 'MyApp::Foo', bar => 'MyApp::Bar' },
        preload     => 'Config, Log',

        cachedir    => '/var/cache/myapp',
        configfile  => '/etc/myapp/myapp.conf',
        datadir     => '/var/lib/myapp',
        docdir      => '/usr/share/doc/myapp',
        lockdir     => '/var/lock/myapp',
        logdir      => '/var/log/myapp',
        logfile     => '/var/log/myapp/myapp.log',
        pidfile     => '/var/run/myapp/myapp.pid',
        root        => '/etc/myapp',
        rundir      => '/var/run/myapp',
        sharedir    => '/usr/share/myapp',
        spooldir    => '/var/spool/myapp',
        tempdir     => '/tmp/myapp',
        webdir      => '/var/www/myapp',

        debug       => 0,
        test        => 0,

lib/Acme/Crux.pm  view on Meta::CPAN


    lockdir => '/var/lock/myapp'

Lock dir for project lock files

    $app = $app->lockdir( "/path/to/lock/dir" );
    my $lockdir = $app->lockdir;

Default: /var/lock/<MONIKER>

=head2 logdir

    logdir => '/var/log/myapp'

Log dir for project logging

    $app = $app->logdir( "/path/to/log/dir" );
    my $logdir = $app->logdir;

Default: /var/log/<MONIKER>

=head2 logfile

    logfile => '/var/log/myapp/myapp.log'

Path to the log file

    $app = $app->logfile( "/path/to/file.log" );
    my $logfile = $app->logfile;

Default: /var/log/<MONIKER>/<MONIKER>.log

=head2 moniker

    moniker => 'myapp'

This attribute sets moniker of project name.

Moniker B<SHOULD> contains only symbols: a-z, 0-9, '_', '-', '.'

    $app = $app->moniker( 'myapp' );

lib/Acme/Crux.pm  view on Meta::CPAN


    my $elapsed = $app->elapsed;

    my $timing_begin = [gettimeofday];
    # ... long operations ...
    my $elapsed = $app->elapsed( $timing_begin );

Return fractional amount of time in seconds since unnamed timstamp has been created while start application

    my $elapsed = $app->elapsed;
    $app->log->debug("Database stuff took $elapsed seconds");

For formatted output:

    $app->log->debug(sprintf("%+.*f sec", 4, $app->elapsed));

=head2 error

    my $error = $app->error;

Returns error string if occurred any errors while working with application

    $app = $app->error( "error text" );

Sets new error message and returns object

lib/Acme/Crux.pm  view on Meta::CPAN

=item L<Acme::Crux::Plugin::Config>

    Config => Acme::Crux::Plugin::Config

L<Acrux::Config> configuration plugin

=item L<Acme::Crux::Plugin::Log>

    Log => Acme::Crux::Plugin::Log

L<Acrux::Log> logging plugin

=back

=head1 TO DO

See C<TODO> file

=head1 SEE ALSO

L<CTK>, L<CTK::App>

lib/Acme/Crux.pm  view on Meta::CPAN

        debugmode   => 0,
        testmode    => 0,
        verbosemode => 0,

        # Dirs
        pwd         => $pwd,
        exedir      => $RealBin, # Script dir
        root        => $args->{root}, # Root dir of project. Default: /etc/moniker
        tempdir     => $args->{tempdir}, # Temp dir of project. Default: /tmp/moniker
        datadir     => $args->{datadir}, # Data dir of project. Defaut: /var/lib/moniker
        logdir      => $args->{logdir}, # Log dir of project. Default: /var/log/moniker
        sharedir    => $args->{sharedir}, # Share dir. Default: /usr/share/moniker
        docdir      => $args->{docdir}, # Share dir. Default: /usr/share/doc/moniker
        cachedir    => $args->{cachedir}, # Cache dir. Default: /var/cache/moniker
        spooldir    => $args->{spooldir}, # Spool dir. Default: /var/spool/moniker
        rundir      => $args->{rundir}, # Run dir. Default: /var/run/moniker
        lockdir     => $args->{lockdir}, # Lock dir. Default: /var/lock/moniker
        webdir      => $args->{webdir}, # Web dir. Default: /var/www/moniker

        # Files
        logfile     => $args->{logfile}, # Log file of project. Default: /var/log/moniker/moniker.log
        configfile  => $args->{configfile}, # Config file of project. Default: /etc/moniker/moniker.conf
        pidfile     => $args->{pidfile}, # PID file of project. Default: /var/run/moniker.pid

    }, $class;

    # Modes
    foreach my $mode ( @{(ALOWED_MODES)}) {
        $self->{$mode."mode"} = 1 if is_true_flag($args->{$mode});
    }

lib/Acme/Crux.pm  view on Meta::CPAN

        $temp = $self->{tempdir} = File::Spec->catdir(File::Spec->tmpdir(), $moniker);
    }

    # Data dir
    my $datadir = $self->{datadir};
    unless (defined($datadir) && length($datadir)) {
        $datadir = $self->{datadir} = File::Spec->catdir(SHAREDSTATEDIR, $moniker);
    }

    # Log dir
    my $logdir = $self->{logdir};
    unless (defined($logdir) && length($logdir)) {
        $logdir = $self->{logdir} = File::Spec->catdir(LOGDIR, $moniker);
    }

    # Share dir
    my $sharedir = $self->{sharedir};
    unless (defined($sharedir) && length($sharedir)) {
        $self->{sharedir} = File::Spec->catdir(DATADIR, $moniker);
    }

    # Doc dir
    my $docdir = $self->{docdir};

lib/Acme/Crux.pm  view on Meta::CPAN

        $self->{webdir} = File::Spec->catdir(WEBDIR, $moniker);
    }

    # Config file
    my $configfile = $self->{configfile};
    unless (defined($configfile) && length($configfile)) {
        $self->{configfile} = File::Spec->catfile(IS_ROOT ? $root : $pwd, sprintf("%s.conf", $moniker));
    }

    # Log file
    my $logfile = $self->{logfile};
    unless (defined($logfile) && length($logfile)) {
        $self->{logfile} = File::Spec->catfile(IS_ROOT ? $logdir : $pwd, sprintf("%s.log", $moniker));
    }

    # PID file
    my $pidfile = $self->{pidfile};
    unless (defined($pidfile) && length($pidfile)) {
        $self->{pidfile} = File::Spec->catfile(IS_ROOT ? $rundir : $pwd, sprintf("%s.pid", $moniker));
    }

    # Define plugins list to plugin map
    $self->plugins(as_hash_ref($args->{plugins}));

lib/Acme/Crux.pm  view on Meta::CPAN

    return $self->{tempdir};
}
sub datadir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{datadir} = shift;
        return $self;
    }
    return $self->{datadir};
}
sub logdir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{logdir} = shift;
        return $self;
    }
    return $self->{logdir};
}
sub sharedir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{sharedir} = shift;
        return $self;
    }
    return $self->{sharedir};
}
sub docdir {

lib/Acme/Crux.pm  view on Meta::CPAN

    return $self->{webdir};
}
sub configfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{configfile} = shift;
        return $self;
    }
    return $self->{configfile};
}
sub logfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{logfile} = shift;
        return $self;
    }
    return $self->{logfile};
}
sub pidfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{pidfile} = shift;
        return $self;
    }
    return $self->{pidfile};
}

lib/Acme/Crux/Plugin/Log.pm  view on Meta::CPAN

package Acme::Crux::Plugin::Log;
use warnings;
use strict;
use utf8;

=encoding utf-8

=head1 NAME

Acme::Crux::Plugin::Log - The Acme::Crux plugin for logging in your application

=head1 SYNOPSIS

    # In startup
    $app->plugin('Log');
    $app->plugin('Log', undef, { ... options ... });

    # In application
    $app->log->trace('Whatever');
    $app->log->debug('You screwed up, but that is ok');
    $app->log->info('You are bad, but you prolly know already');
    $app->log->notice('Normal, but significant, condition...');
    $app->log->warn('Dont do that Dave...');
    $app->log->error('You really screwed up this time');
    $app->log->fatal('Its over...');
    $app->log->crit('Its over...');
    $app->log->alert('Action must be taken immediately');
    $app->log->emerg('System is unusable');

=head1 DESCRIPTION

The Acme::Crux plugin for logging in your application

=head1 OPTIONS

This plugin supports the following options

=head2 autoclean

    $app->plugin(Log => undef, {autoclean => 1});

This option enables cleaning (closing handler or syslog) on DESTROY

Default: C<logautoclean> command line option or C<logautoclean> application argument
or C<LogAutoclean> configuration value or C<0> otherwise

=head2 color

    $app->plugin(Log => undef, {color => 1});

This option enables colorize log messages with the available levels using L<Term::ANSIColor>

Default: C<logcolorize> command line option or C<logcolorize> application argument
or C<LogColorize> configuration value or C<0> otherwise

=head2 facility

    $app->plugin(Log => undef, {facility => 'user'});

This option sets facility for logging

Available standard facilities: C<auth>, C<authpriv>, C<cron>, C<daemon>, C<ftp>,
C<kern>, C<local0>, C<local1>, C<local2>, C<local3>, C<local4>, C<local5>, C<local6>,
C<local7>, C<lpr>, C<mail>, C<news>, C<syslog>, C<user> and C<uucp>

Default: C<logfacility> command line option or C<logfacility> application argument
or C<LogFacility> configuration value or C<user> otherwise

=head2 file

    $app->plugin(Log => undef, {file => '/var/log/myapp.log'});

Log file path used by "handle"

Default: C<logfile> command line option or C<LogFile> configuration value
or C<logfile> application argument or C</var/log/$moniker/$moniker.log> otherwise

=head2 format

    $app->plugin(Log => undef, {format => sub {...}});

A callback function for formatting log messages. See L<Acrux::Log/format>

Default: C<logformat> application argument or C<undef> otherwise

=head2 handle

    $app->plugin(Log => undef, {
        handle => IO::Handle->new_from_fd(fileno(STDOUT), "w")
    });

Log filehandle, defaults to opening "file" or uses syslog if file not specified

Default: C<loghandle> application argument or C<undef> otherwise

=head2 ident

    $app->plugin(Log => undef, {ident => 'myapp'});

The B<ident> is prepended to every B<syslog> message

Default: C<logident> command line option or C<logident> application argument
or C<LogIdent> configuration value or script name C<basename($0)> otherwise

=head2 level

    $app->plugin(Log => undef, {level => 'debug'});

This option sets log level

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.

See also L<Acrux::Log/level>

Default: C<loglevel> command line option or C<loglevel> application argument
or C<LogLevel> configuration value or C<debug> otherwise

=head2 logger

    $app->plugin(Log => undef, {logger => Mojo::Log->new()});

This option sets predefined logger, eg. Mojo::Log

Default: C<logger> application argument or C<undef> otherwise

=head2 logopt

    $app->plugin(Log => undef, {logopt => 'ndelay,pid'});

This option contains zero or more of the options detailed in L<Sys::Syslog/openlog>

Default: C<logopt> command line option or C<logopt> application argument
or C<LogOpt> configuration value or C<'ndelay,pid'> otherwise

=head2 prefix

    $app->plugin(Log => undef, {prefix => '>>>'});

The B<prefix> is prepended to every C<handled> log message

Default: C<logprefix> command line option or C<logprefix> application argument
or C<LogPrefix> configuration value or C<null> otherwise

=head2 provider

    $app->plugin(Log => undef, {provider => 'syslog'});

This option select the provider of logging. Avalabled providers:
C<logger>, C<handler>, C<file> and C<syslog>.

Default: C<logprovider> command line option or C<logprovider> application argument
or C<LogProvider> configuration value or C<file> otherwise

=head2 short

    $app->plugin(Log => undef, {short => 1});

Generate short log messages without a timestamp but with log level prefix

Default: C<logshort> command line option or C<logshort> application argument
or C<LogShort> configuration value or C<0> otherwise

=head1 METHODS

This class inherits all methods from L<Acme::Crux::Plugin> and implements the following new ones

=head2 register

    $plugin->register($app, {file => '/var/log/app.log'});

Register plugin in Acme::Crux application

=head1 HELPERS

All helpers of this plugin are allows get access to logger object.
See L<Acrux::Log> for details

=head2 log

Returns L<Acrux::Log> object

=head1 TO DO

See C<TODO> file

=head1 SEE ALSO

L<Acme::Crux::Plugin>, L<Acrux::Log>

lib/Acme/Crux/Plugin/Log.pm  view on Meta::CPAN


use Carp qw/croak/;
use Acrux::RefUtil qw/as_array_ref as_hash_ref is_code_ref is_true_flag is_ref/;

sub register {
    my ($self, $app, $args) = @_;
    my $has_config = $app->can('config') ? 1 : 0;

    # Autoclean flag: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $autoclean = is_true_flag($args->{autoclean}) # From plugin arguments first
      || $app->getopt("logautoclean")               # From command line options
      || $app->orig->{"logautoclean"}               # From App arguments
      || ($has_config ? $app->config->get("/logautoclean") : 0); # From config file

    # Colorize flag: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $colorize = is_true_flag($args->{color}) # From plugin arguments first
      || $app->getopt("logcolorize")           # From command line options
      || $app->orig->{"logcolorize"}           # From App arguments
      || ($has_config ? $app->config->get("/logcolorize") : 0); # From config file

    # Log facility: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $facility = $args->{facility}  # From plugin arguments first
      || $app->getopt("logfacility") # From command line options
      || $app->orig->{"logfacility"} # From App arguments
      || ($has_config ? $app->config->get("/logfacility") : ''); # From config file

    # Log file: PLGARGS || OPTS || CONF || ORIG || DEFS
    my $file = $args->{file}  # From plugin arguments first
      || $app->getopt("logfile") # From command line options
      || ($has_config ? $app->config->get("/logfile") : '') # From config file
      || $app->logfile; # From App arguments

    # Format: PLGARGS || DEFS
    my $frmt = $args->{format} || $app->orig->{"logformat"};
    if (defined($frmt) && length($frmt)) {
        croak(qq{Invalid log format coderef}) unless is_code_ref($frmt);
    }

    # Handle: PLGARGS || DEFS
    my $handle = $args->{handle} || $app->orig->{"loghandle"};
    if (defined $handle) {
        croak(qq{Invalid log handle}) unless is_ref($handle);
    }

    # Log ident: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $ident = $args->{ident}  # From plugin arguments first
      || $app->getopt("logident") # From command line options
      || $app->orig->{"logident"} # From App arguments
      || ($has_config ? $app->config->get("/logident") : ''); # From config file

    # Log level: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $level = $args->{level}  # From plugin arguments first
      || $app->getopt("loglevel") # From command line options
      || $app->orig->{"loglevel"} # From App arguments
      || ($has_config ? $app->config->get("/loglevel") : ''); # From config file

    # Logger: PLGARGS || DEFS
    my $logger = $args->{logger} || $app->orig->{"logger"};
    if (defined $logger) {
        croak(qq{Invalid logger object}) unless is_ref($logger);
    }

    # Log options: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $logopt = $args->{logopt}  # From plugin arguments first
      || $app->getopt("logopt") # From command line options
      || $app->orig->{"logopt"} # From App arguments
      || ($has_config ? $app->config->get("/logopt") : ''); # From config file

    # Short flag: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $short = is_true_flag($args->{short}) # From plugin arguments first
      || $app->getopt("logshort") # From command line options
      || $app->orig->{"logshort"} # From App arguments
      || ($has_config ? $app->config->get("/logshort") : 0); # From config file

    # Log prefix: PLGARGS || OPTS || ORIG || CONF || DEFS
    my $prefix = $args->{prefix}  # From plugin arguments first
      || $app->getopt("logprefix") # From command line options
      || $app->orig->{"logprefix"} # From App arguments
      || ($has_config ? $app->config->get("/logprefix") : ''); # From config file

    # Correct provider rules
    my $provider = $args->{provider}  # From plugin arguments first
      || $app->getopt("logprovider") # From command line options
      || $app->orig->{"logprovider"} # From App arguments
      || ($has_config ? $app->config->get("/logprovider") : '') || ''; # From config file
    if ($provider eq 'syslog')    { $file = $handle = $logger = undef }
    elsif ($provider eq 'file')   { $logger = $handle = undef }
    elsif ($provider eq 'handle') { $logger = undef }

    # Create instance
    my $log = Acrux::Log->new(
        autoclean => $autoclean,
        color => $colorize, # !!
        facility => $facility,
        file => $file,
        format => $frmt, # !!
        handle => $handle,
        ident => $ident,
        level => $level,
        logger => $logger,
        logopt => $logopt,
        short => $short,
        prefix => $prefix,
    );

    # Set log helper (method)
    $app->register_method(log => sub { $log });

    return $log;
}

1;

__END__

lib/Acrux/Const.pm  view on Meta::CPAN

*BINDIR = sub { $bindir };                                                              # bindir    /usr/bin
*SBINDIR = sub { state $sbindir = File::Spec->catdir($prefix, 'sbin') };                # sbindir   /usr/sbin
*DATADIR = sub { state $datadir = File::Spec->catdir($prefix, 'share') };               # datadir   /usr/share
*DOCDIR = sub { state $docdir = File::Spec->catdir($prefix, 'share', 'doc') };          # docdir    /usr/share/doc
*LOCALEDIR = sub { state $localedir = File::Spec->catdir($prefix, 'share', 'locale') }; # localedir /usr/share/locale
*MANDIR = sub { state $mandir = File::Spec->catdir($prefix, 'share', 'man') };          # mandir    /usr/share/man
*LOCALBINDIR = sub { state $localbindir = File::Spec->catdir($prefix, 'local', 'bin') };# localbindir  /usr/local/bin

# Local State related Dirs
*CACHEDIR = sub { state $cachedir = File::Spec->catdir($localstatedir, 'cache') };      # cachedir  /var/cache
*LOGDIR = sub { state $logdir = File::Spec->catdir($localstatedir, 'log') };            # logdir    /var/log
*SPOOLDIR = sub { state $spooldir = File::Spec->catdir($localstatedir, 'spool') };      # spooldir  /var/spool
*RUNDIR = sub { state $rundir = File::Spec->catdir($localstatedir, 'run') };            # rundir    /var/run
*LOCKDIR = sub { state $lockdir = File::Spec->catdir($localstatedir, 'lock') };         # lockdir   /var/lock
*SHAREDSTATEDIR = sub { state $sharedstatedir = File::Spec->catdir($localstatedir, 'lib') }; # sharedstatedir  /var/lib
*WEBDIR = sub { state $webdir =  File::Spec->catdir($localstatedir, 'www') };           # webdir    /var/www

1;

__END__

lib/Acrux/Log.pm  view on Meta::CPAN

package Acrux::Log;
use strict;
use utf8;

=encoding utf-8

=head1 NAME

Acrux::Log - Acrux logger

=head1 SYNOPSIS

    use Acrux::Log;

    # Using syslog
    my $log = Acrux::Log->new();
       $log->error("My test error message to syslog")

    # Using file
    my $log = Acrux::Log->new(file => '/tmp/test.log');
       $log->error("My test error message to /tmp/test.log")

    # Using STDOUT (handle)
    my $log = Acrux::Log->new(
            handle => IO::Handle->new_from_fd(fileno(STDOUT), "w")
        );
    $log->error("My test error message to STDOUT")

    # Customize minimum log level
    my $log = Acrux::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->notice('Normal, but significant, condition...');
    $log->warn('This might be a problem');
    $log->error('Garden variety error');
    $log->fatal('Boom');
    $log->crit('Its over...');
    $log->alert('Action must be taken immediately');
    $log->emerg('System is unusable');

=head1 DESCRIPTION

Acrux::Log is a simple logger for Acrux logging

=head2 new

    my $log = Acrux::Log->new(
        logopt      => 'ndelay,pid',
        facility    => 'user',
        level       => 'debug',
        ident       => 'test.pl',
        autoclean   => 1,
        logopt      => 'ndelay,pid',
    );

With default attributes

    use Mojo::Log;
    my $log = Acrux::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 autoclean

    autoclean => 1

This attribute enables cleaning (closing handler or syslog) on DESTROY

=head2 color

    color => 1

Colorize log messages with the available levels using L<Term::ANSIColor>, defaults to C<0>

=head2 facility

    facility => 'user'

This attribute sets facility for logging

Available standard facilities: C<auth>, C<authpriv>, C<cron>, C<daemon>, C<ftp>,
C<kern>, C<local0>, C<local1>, C<local2>, C<local3>, C<local4>, C<local5>, C<local6>,
C<local7>, C<lpr>, C<mail>, C<news>, C<syslog>, C<user> and C<uucp>

Default: C<user> (Sys::Syslog::LOG_USER)

See also L<Sys::Syslog/Facilities>

=head2 file

    file => '/var/log/myapp.log'

Log file path used by "handle"

=head2 format

    format => sub {...}

A callback function for formatting log messages

    format => sub {
        my ($time, $level, @lines) = @_;
        return "[$time] [$level] " . join (' ', @lines) . "\n";
    }

This callback routine must return formatted string for the log line

=head2 handle

    handle => IO::Handle->new_from_fd(fileno(STDOUT), "w")

Log filehandle, defaults to opening "file" or uses syslog if file not specified

=head2 ident

    ident => 'myapp'

The B<ident> is prepended to every B<syslog> message

Default: script name C<basename($0)>

=head2 level

    level => 'debug'

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

    logger => Mojo::Log->new()

This attribute perfoms to set predefined logger, eg. Mojo::Log

Default: C<undef>

=head2 logopt

    logopt => 'ndelay,pid'

This attribute contains zero or more of the options detailed in L<Sys::Syslog/openlog>

Default: C<'ndelay,pid'>

=head2 prefix

    prefix => '>>>'

The B<prefix> is prepended to every C<handled> log message

Default: null

=head2 short

    short => 1

Generate short log messages without a timestamp but with log level prefix, defaults to C<0>

=head1 METHODS

This class implements the following methods

=head2 alert

    $log->alert('Action must be taken immediately');
    $log->alert('Real', 'problem');

Log C<alert> message

=head2 crit

    $log->crit('Its over...');
    $log->crit('Bye', 'bye');

Log C<crit> message (See L</fatal> method)

=head2 debug

    $log->debug('You screwed up, but that is ok');
    $log->debug('All', 'cool');

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

    $log->info('You are bad, but you prolly know already');
    $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

    $log->notice('Normal, but significant, condition...');
    $log->notice('Ok', 'then');

Log C<notice> message

=head2 provider

    print $log->provider;

Returns provider name (C<external>, C<handle>, C<file> or C<syslog>)

=head2 trace

    $log->trace('Whatever');
    $log->trace('Who', 'cares');

Log C<trace> message

=head2 warn

    $log->warn('Dont do that Dave...');
    $log->warn('No', 'really');

Log C<warn> message

=head1 HISTORY

See C<Changes> file

=head1 TO DO

See C<TODO> file

=head1 SEE ALSO

L<Sys::Syslog>

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2024 D&D Corporation. All Rights Reserved

=head1 LICENSE

lib/Acrux/Log.pm  view on Meta::CPAN

modify it under the same terms as Perl itself.

See C<LICENSE> file and L<https://dev.perl.org/licenses/>

=cut

our $VERSION = '0.01';

use Carp qw/carp croak/;
use Scalar::Util qw/blessed/;
use Sys::Syslog qw//;
use File::Basename qw/basename/;
use IO::File qw//;
use Fcntl qw/:flock/;
use Encode qw/find_encoding/;
use Time::HiRes qw/time/;
use Acrux::Util qw/color/;

use constant {
    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,

lib/Acrux/Log.pm  view on Meta::CPAN

    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;
    my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};
    $args->{facility}   ||= Sys::Syslog::LOG_USER;
    $args->{ident}      ||= basename($0);
    $args->{logopt}     ||= LOGOPTS;
    $args->{logger}     ||= undef;
    $args->{level}      ||= 'debug';
    $args->{file}       ||= undef;
    $args->{handle}     ||= undef;
    $args->{provider}   = 'unknown';
    $args->{autoclean}  ||= 0;
    $args->{prefix}     ||= '';
    $args->{format}     ||= undef;
    $args->{color}      ||= 0;

    # Check level
    $args->{level} = lc($args->{level});
    unless (exists $MAGIC{$args->{level}}) {
        carp "Incorrect log level specified. Well be used debug log level by default";
        $args->{level} = 'debug';
    }

    # Instance
    my $self = bless {%$args}, $class;

    # Set formatter
    $self->{format} ||= $self->{short} ? \&_short : $self->{color} ? \&_color : \&_default;

    # Open sys log socket
    if ($args->{logger}) {
        croak "Blessed reference expected in logger attribute" unless blessed($args->{logger});
        $self->{provider} = "external";
    } elsif ($args->{handle}) {
        $self->{provider} = "handle";
        return $self;
    } elsif ($args->{file}) {
        my $file = $args->{file};
        $self->{handle} = IO::File->new($file, ">>");
        croak qq/Can't open log file "$file": $!/ unless defined $self->{handle};
        $self->{provider} = "file";
    } else {
        Sys::Syslog::openlog($args->{ident}, $args->{logopt}, $args->{facility});
        $self->{provider} = "syslog";
    }

    return $self;
}
sub level {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{level} = shift;
        return $self;
    }
    return $self->{level};
}
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;

    # External logger
    if (my $logger = $self->logger) {
        my $name = $SHORT{$mag};
        if (my $code = $logger->can($name)) {
            return $logger->$code(@msg);
        } else {
            carp(sprintf("Can't found '%s' method in '%s' package", $name, ref($logger)));
        }
        return 0;
    }

    # Handle
    if (my $handle = $self->handle) {
        # Set message
        my $pfx = (defined($self->{prefix}) && length($self->{prefix})) ? $self->{prefix} : '';
        my $_msg = $ENCODING->encode($pfx . $self->{format}->(time, $level, @msg), 0);

        # Flush
        if ($self->{provider} eq "file") { # Flush to file
            flock $handle, LOCK_EX;
            $handle->print($_msg) or croak "Can't write to log file: $!";
            flock $handle, LOCK_UN;
        } elsif ($self->{provider} eq "handle") { # Flush to handle
            print $handle $_msg;
        } else {
            return 0;
        }
        return 1;
    }

    # Syslog
    return 0 if $self->provider ne "syslog";
    my $lvl = $LOGLEVELS{$level} // Sys::Syslog::LOG_DEBUG;
    Sys::Syslog::syslog($lvl, LOGFORMAT, join(SEPARATOR, @msg));
}

sub _default {
    my ($tm, $l, @msg) = @_;
    my ($s, $m, $h, $day, $month, $year) = localtime $tm;
    my $time = sprintf '%04d-%02d-%02d %02d:%02d:%08.5f', $year + 1900, $month + 1, $day, $h, $m,
       "$s." . ((split /\./, $tm)[1] // 0);
    return "[$time] [$$] [$l] " . join(SEPARATOR, @msg) . "\n";
}
sub _short {

lib/Acrux/Log.pm  view on Meta::CPAN

    my $msg = _default(shift, my $level = shift, @_);
    return $msg unless $COLORS{$level};
    chomp $msg;
    return color($COLORS{$level}, $msg) . "\n";
}

DESTROY {
    my $self = shift;
    if ($self->{autoclean}) {
        undef $self->{handle} if $self->{file};
        Sys::Syslog::closelog() if $self->{provider} eq "syslog";
    }
}

1;

__END__

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

# This is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
#
#########################################################################
use strict;
use utf8;
use Test::More;

use_ok qw/Acrux::Log/;

# Error message with debug loglevel
{
    my $log = Acrux::Log->new();
    is $log->level, 'debug', "Debug LogLevel";
    ok $log->error("My test error message"), 'Error message to syslog';
}

# Info and fatal message with eror loglevel
{
    my $log = Acrux::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 to syslog';
    #note explain $log;
}

# Fake Logger
{
    my $fake = FakeLogger->new;
    my $log = Acrux::Log->new(logger => $fake);
    $log->error("Test error message") and ok 1, "Test error message to STDOUT via FakeLogger";
    #ok $log->debug("Test debug message");
    $log->info("Test info message") and ok 1, "Test info message to STDOUT via FakeLogger";
    #note explain $log;
}

# File
{
    my $log = Acrux::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";
}

# STDOUT
{
    use IO::Handle;
    my $log = Acrux::Log->new(
        handle => IO::Handle->new_from_fd(fileno(STDOUT), "w"),
        prefix => '# ',
    );
    ok $log->error("My test error message"), 'Error message to handler STDOUT';
}

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/09-log.t



( run in 0.594 second using v1.01-cache-2.11-cpan-49f99fa48dc )