The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Log::Any::Adapter::Daemontools - Logging adapter suitable for use in a Daemontools-style logging chain

VERSION

version 0.090000_001

SYNOPSIS

  # No "bonus features" are enabled by default, but this gets you the most
  # common Unixy behavior.
  use Log::Any::Adapter 'Daemontools',
    config => { argv => 1, env => 1, handle_signals => ['USR1','USR2'] };
  
  # Above is equivalent to:
  use Log::Any::Adapter 'Daemontools',
    config => {
      log_level  => 'info',
      argv => {
        verbose => [ '-v', '--verbose' ], quiet => [ '-q', '--quiet' ],
        bundle => 1, stop => '--'
      },
      env  => { debug => 'DEBUG' },
      handle_signals => { verbose => 'USR1', quiet => 'USR2' }
    };
  
  # Above is equivalent to:
  use Log::Any::Adapter::Daemontools 'global_log_level', 'global_debug_level', 'parse_log_level_opts';
  use Log::Any::Adapter;
  if ($ENV{DEBUG}) { global_debug_level($ENV{DEBUG}); }
  if (@ARGV) {
    global_log_level(
      parse_log_level_opts(
        array => \@ARGV,
        verbose => [ '-v', '--verbose' ],
        quiet => [ '-q', '--quiet' ],
        bundle => 1,
        stop => '--'
      )
    );
  }
  $SIG{USR1}= sub { global_log_level('+= 1'); };
  $SIG{USR2}= sub { global_log_level('-= 1'); };
  Log::Any::Adapter->set('Daemontools');
  
  # Example of a differing point of view:
  #
  # (Beware: 'argv', 'env', and 'handle_signals' are a special once-only
  #  startup behavior, so this code must be the *first* created
  #  Log::Any::Adapter::Daemontools instance.)
  #
  use Log::Any::Adapter 'Daemontools',
    argv => {
      bundle  => 1,
      verbose => '-v',  # none of that silly long-option stuff for us!
      quiet   => '-q',
      stop    => qr/^[^-]/, # Stop at the first non-option argument
    };
  # Now use our own signal handler to reload a config file that specifies
  # a log level:
  $SIG{HUP}= sub {
    MyApp->load_my_config_file();
    Log::Any::Adapter::Daemontools->global_log_level( MyApp->config->{log_level} );
  };

DESCRIPTION

In the daemontools way of thinking, a daemon writes all its logging output to STDOUT/STDERR, which is a pipe to a logger process. Doing this instead of other logging alternatives keeps your program simple and allows you to capture errors generated by deeper libraries (like libc) which write debugging info to STDOUT/STDERR.

When logging to a pipe, you lose the log level information. An elegantly simple way to preserve this information is to prefix each line with "error:" or etc. prefixes, which can be re-parsed later.

Another frequent desire is to request that a long-lived daemon change its logging level on the fly. One way this is handled is by sending SIGUSR1/SIGUSR2 to tell the daemon to raise/lower the logging level. Likewise people often want to use "-v" or "-q" command line options to the same effect when running it from the command line.

This module provides a convenient way for you to configure all of that from a single "use" line.

VERSION NOTICE

NOTE: Version 0.1 lost some of the features of version 0.002 when the internals of Log::Any changed in a way that made them impossible. I don't know if anyone was using them anyway, but pay close attention if you are upgrading. This new version adheres more closely to the specification for a logging adapter.

ATTRIBUTES

config

This package can only be configured once. If 'config' is passed as an argument to the adapter constuctor it will only be processed when the first adapter is created. The purpose is to allow you to say

  use Log::Any::Adapter 'Daemontools', config => { initial_options... };

and not have to call a bunch of package methods.

env
  env => $name_or_args

Convenient passthrough to process_env package method.

If env is a hashref, it is passed directly. If it is a scalar, it is interpreted as a pre-defined "profile" of arguments.

Profiles:

  1.   { debug => 'DEBUG' }
argv
  argv => $name_or_args

Convenient passthrough to process_argv package method.

If argv is a hashref, it is passed directly. If it is a scalar, it is interpreted as a pre-defined "profile" of arguments.

Profiles:

  1.   {
        bundle  => 1,
        verbose => qr/^(--verbose|-v)$/,
        quiet   => qr/^(--quiet|-q)$/,
        stop    => '--'
      }
signals
  signals => [ $v, $q ],
  signals => { verbose => $v, quiet => $q },

Convenient passthrough to handle_signals package method.

If handle_signals is an arrayref of length 2, they are used as the verbose and quiet parameters, respectively. If it is a hashref, it is passed directly. No other type of value is supported, currently.

level
  global_log_level => $name
  global_log_level => $number

Sets the initial value of the global log level, which can be altered later and apply to all adapters that don't have log_level overridden. See global_log_level

level_min

Initial value for global_log_level_min

level_max

Initial value for global_log_level_max

category_level

Hashref of category names to the initial log level for each. Note that categories with a specified log level will no longer be controlled by changing the global log level.

See category_log_level package method.

category_min

Sets the category_log_level_min initial value. This could be used to prevent important logging streams from getting silenced even on the most extreme 'quiet' level.

category_max

Sets the category_log_level_max initial value. This could be used to prevent certain modules form getting too noisy at a level of 'trace'.

PACKAGE METHODS

process_env

  $class->process_env( debug => $ENV_VAR_NAME );
  # and/or
  $class->process_env( log_level => $ENV_VAR_NAME );

Request that this package check for the named variable, and if it exists, interpret it either as a debug level or a log level, and then set the global log level used by this adapter.

A "debug level" refers to the typical Unix practice of a environment variable named DEBUG where increasing integer values results in more debugging output. This results in the following mapping: 2=trace, 1=debug 0=info -1=notice and so on. Larger numbers are clamped to 'trace'.

The other "log level" interpretation of numbers is that they represent the numeric Log::Any level (identical to numeric syslog levels) which you want to see. So the mapping for a LOG_LEVEL variable would be 8=trace, 7=debug, 6=info, etc.

Either type of variable can also be a named log level or alias, in which case it doesn't matter which type of variable it is. These are according to "numeric_level" in Log::Any::Adapter::Util.

process_argv

  $class->process_argv( bundle => ..., verbose => ..., quiet => ..., stop => ..., remove => ... )

Scans (and optionally modifies) @ARGV using method parse_log_level_opts, with the supplied options, and updates the global log level accordingly.

parse_log_level_opts

  $level_offset= $class->parse_log_level_opts(
    array   => $arrayref, # required
    verbose => $strings_or_regexes,
    quiet   => $strings_or_regexes,
    stop    => $strings_or_regexes,
    bundle  => $bool, # defaults to false
    remove  => $bool, # defaults to false
  );

Scans the elements of 'array' looking for patterns listed in 'verbose', 'quiet', or 'stop'. Each match of a pattern in 'quiet' subtracts one from the return value, and each match of a pattern in 'verbose' adds one. Stops iterating the array if any pattern in 'stop' matches.

If 'bundle' is true, then this routine will also split apart "bundled options", so for example

  --foo -wbmvrcd --bar

is processed as if it were

  --foo -w -b -m -v -r -c -d --bar

If 'remove' is true, then this routine will alter the array to remove matching elements for 'quiet' and 'verbose' patterns. It can also remove the bundled arguments if bundling is enabled:

  @array= ( '--foo', '-qvvqlkj', '--verbose' );
  my $n= parse_log_level_opts(
    array => \@array,
    quiet => [ '-q', '--quiet' ],
    verbose => [ '-v', '--verbose' ],
    bundle => 1,
    remove => 1
  );
  # $n = -1
  # @array = ( '--foo', '-lkj' );

handle_signals

  $class->handle_signals( verbose => $signal_name, quiet => $signal_name );

Install signal handlers (probably USR1, USR2) which increase or decrease the log level.

Basically:

  $SIG{ $verbose_name }= sub { Log::Any::Adapter::Daemontools->global_log_level('+= 1'); }
    if $verbose_name;
  
  $SIG{ $quiet_name   }= sub { Log::Any::Adapter::Daemontools->global_log_level('-= 1'); }
    if $quiet_name;

global_log_level

  $class->global_log_level            # returns level number
  $class->global_log_level( 'info' ); # 6
  $class->global_log_level( 3 );      # 3
  $class->global_log_level( 99 );     # 8 (clamped to max)
  $class->global_log_level( '+= 1' ); # 4
  $class->global_log_level( '-= 9' ); # -1 (clamped to min)
  $class->global_log_level( -1 );     # disable all logging

Log::Any::Adapter::Daemontools has a global variable that determines the logging level. This method gets or sets the default level. Level names are converted to numbers by "numeric_level" in Log::Any::Adapter::Util. If the level has a + or - prefix it will be added to the current level.

global_log_level_min

  # Our app should never have 'fatal' squelched no matter how many '-q' the user gives us
  use Log::Any::Adapter 'Daemontools' log_level_min => 'fatal';
  # or
  Log::Any::Adapter::Daemontools->global_log_level_min(2);

This accessor lets you get/set the minimum log level used to clamp the values of global_log_level.

global_log_level_max

  # We've hacked around on our logging infrastructure and actually have trace2..trace5
  Log::Any::Adapter::Daemontools->global_log_level_max(12);

Get/Sets the value used for clamping global_log_level.

category_log_level

  $class->category_log_level($name);           # returns level number
  $class->category_log_level($name => 1)       # 1
  $class->category_log_level($name => 'info'); # 6
  $class->category_log_level($name => undef);  # back to global default
  $class->category_log_level($name => 
  
  # And the API wouldn't be complete if you couldn't set your own
  # upper/lower bounds on the logging level...
  $class->category_log_level_min($name => $min)
  $class->category_log_level_max($name => $max)

Log::Any::Adapter::Daemontools can override the global logging level on a per-category basis. Once set to a value, this category will no longer see changes to the default global level. You can restore it to the default by setting the category level to undef.

category_log_level_min

See category_log_level

category_log_level_max

See category_log_level

METHODS

Adapter instances support all the standard logging methods of Log::Any::Adapter

See Log::Any::Adapter

AUTHOR

Michael Conrad <mike@nrdvana.net>

COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by Michael Conrad.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.