Perl6 logging library (port of Perl5 Log::Any)
Perl6
Switch branches/tags
Nothing to show
Latest commit bc21d94 Apr 18, 2017 Julien Simonet Bump version
Permalink
Failed to load latest commit information.
doc Improve documentation Apr 9, 2017
lib/Log Rename adapter STDOUT -> Stdout Apr 13, 2017
t Fix test message Apr 8, 2017
.gitignore Add gitignore Mar 4, 2017
.travis.yml Travis integration. Apr 2, 2017
LICENSE Licence file Mar 6, 2017
META6.json Bump version Apr 18, 2017
readme.md Rename adapter STDOUT -> Stdout Apr 13, 2017

readme.md

Build Status

NAME

Log::Any

SYNOPSIS

use Log::Any;
use Log::Any::Adapter::File;
Log::Any.add( Log::Any::Adapter::File.new( '/path/to/file.log' ) );

Log::Any.info( 'yolo' );
Log::Any.error( :category('security'), 'oups' );
Log::Any.log( :msg('msg from app'), :category( 'network' ), :severity( 'info' ) );

DESCRIPTION

Log::Any is a library to generate and handle application logs. A log is a message indicating an application status at a given time. It has attributes, like a severity (error, warning, debug, …), a category, a date and a message.

These attributes are used by the Formatter to format the log and can also be used to filter logs and to choose where the log will be handled (via Adapters).

Like the Perl5 implementation (https://metacpan.org/pod/Log::Any), the idea is to split log generation and log management.

SEVERITY

The severity is the level of urgence of a log. It can take the following values (based on Syslog):

  • trace
  • debug
  • info
  • notice
  • warning
  • error
  • critical
  • alert
  • emergency

CATEGORY

The category can be seen as a group identifier.

Ex:

  • security ;
  • database ;
  • ...

Default value : the package name where the log is generated.

DATE

The date is generated by Log::Any, and its values is the current date and time (ISO 8601).

MESSAGE

A message is a string passed to Log::Any defined by the user. Newlines in message are escaped to prevent logging multi-line.

ADAPTERS

An adapter handles a log by storing it, or sending it elsewhere. If no adapters are defined, or no one meets the filtering, the message will not be logged.

A few examples:

  • Log::Any::Adapter::File
  • Log::Any::Adapter::Database::SQL
  • Log::Any::Adapter::Stdout

Provided adapters

File

use Log::Any::Adapter::File;
Log::Any.add( Log::Any::Adapter::File.new( path => '/path/to/file.log' ) );

Stdout

use Log::Any::Adapter::Stdout;
Log::Any.add( Log::Any::Adapter::Stdout.new );

Stderr

use Log::Any::Adapter::Stderr;
Log::Any.add( Log::Any::Adapter::Stderr.new );

FORMATTERS

Often, logs need to be formatted to simplify the storage (time-series databases), or the analysis (grep, log parser).

Formatters will use the attributes of a Log.

Symbol Signification Description Default value
\d Date (UTC) The date on which the log was generated Current date time
\c Category Can be any anything specified by the user The current package/module
\s Severity Indicates if it's an information, or an error none
\m Message Payload, explains what is going on none
use Log::Any::Adapter::Stdout( :formatter( '\d \c \m' ) );

You can of course use variables in the formatter, but since \ is already used in Perl6 strings interpolation, you have to escape them.

my $prefix = 'myapp ';
use Log::Any::Adapter::Stdout( :format( "$prefix \\d \\c \\s \\m" ) );

A formatter can be more complex than the default one by extending the class Formatter.

use Log::Any::Formatter;

class MyOwnFormatter is Log::Any::Formatter {
	method format( :$dateTime!, :$msg!, :$category!, :$severity! ) {
		# Returns an Str
	}
}

FILTERS

Filters can be used to allow a log to be handled by an adapter. Many fields can be filtered, like the category, the severity or the message.

The easiest way to define a filter is by using the built-in filter giving an array to filter parameter:

Log::Any.add( Adapter.new, :filter( [ <filters fields goes here>] ) );

Filtering on category or message

# Matching by String
Log::Any.add( Adapter.new, :filter( ['category' => 'My::Wonderfull::Lib' ] ) );
# Matching by Regex
Log::Any.add( Adapter.new, :filter( ['category' => /My::*::Lib/ ] ) );

# Matching msg by Regex
Log::Any.add( Adapter.new, :filter( [ 'msg' => /a regex/ ] );

Filtering on severity

The severity can be considered as levels, so can be traited as numbers.

  1. trace
  2. debug
  3. info
  4. notice
  5. warning
  6. error
  7. critical
  8. alert
  9. emergency

Filtering on severity can be done by specifying an operator in front of the severity:

filter => [ 'severity' => '>warning' ] # Above
filter => [ 'severity' => '==debug'  ] # Equality
filter => [ 'severity' => '<notice'  ] # Beside

Matching only several severities is also possible:

filter => [ 'severity' => [ 'notice', 'warning' ] ]

Several filters

If many filters are specified, all must be valid:

# Use this adapter only if the category is My::Wonderfull::Lib and if the severity is warning or error
[ 'severity' => '>=warning', 'category' => /My::Wonderfull::Lib/ ]

Write your own filter

If a more complex filtering is necessary, a class inheriting Log::Any::Filter can be created:

# Use home-made filters
class MyOwnFilter is Log::Any::Filter {
	method filter( :$msg, :$severity, :$category ) returns Bool {
		# Write some complicated tests
		return True;
	}
}

Log::Any.add( Some::Adapter.new, :filter( MyOwnFilter.new ) );

Filters acting like barrier

A filter generally authorize a log to be sent to an Adapter. If there is no defined Adapter, the log will end up in a black hole.

Log::Any.add( :filter( [ severity => '>warning' ] );
# Logs with severity above "warning" does not continue through the pipeline

PIPELINES

A pipeline is a set of adapters and can be used to define alternatives paths (a set of adapters, filters, formatters and options (asynchronicity) ). This allows to handle differently some logs (for example, for security or realtime). If a log is produced with a specific pipeline which is not defined in the log consumers, the default pipeline is used.

Pipelines can be specified when an Adapter is added.

Log::Any.add( :pipeline('security'), Log::Any::Adapter::Example.new );

Log::Any.error( :pipeline('security'), :msg('security error!' ) );

ASYNCHRONICITY

By default, a pipeline is synchronous, but it can be asynchronous:

Log::Any.add( :pipeline( 'async pipeline'  ), Log::Any::Pipeline.new( :asynchronous ) );

If a pipeline of the specified name already exists, an exception will be throwed. The overwrite parameter can be specified to force adding a new pipeline:

# Overwrite the default pipeline
Log::Any.add( Log::Any::Pipeline.new( :asynchronous ), :overwrite );
# Overwrite the "other" pipeline
Log::Any.add( Log::Any::Pipeline.new( :asynchronous ), :pipeline('other'), :overwrite );

** Asynchronous pipelines can contains messages to handle when a program reaches its end, so theses messages will not be logged. **

INTROSPECTION

Check if a log will be handled (to prevent computation of log). It is usefull if you want to log a dump of a complex object which can take time.

will-log method

This method can takes in parameters the category, the severity and the pipeline to use. Theses parameters are then used to check if the message could pass through the pipelines and will be handled.

** !!! msg parameter cannot be tested, so if a filter acts on it, the results will differ between will-log() and log(). !!! **

if Log::Any.will-log( :severity('debug') ) {
	Log::Any.debug( serialize( $some-complex-object ) );
}

will-log aliases

Some aliases are defined and provide the severity.

  • will-emergency()
  • will-trace()
Log::Any.will-debug(); # Alias to will-log( :severity('debug') )

DEBUGGING

gist method

gist method prints a string represention of the internal Log::Any state (defined pipelines with their adapters, filters and formatters). Since many attributes are not public, you cannot recreate a Log::Any stack based on this representation.

TODO

TODO page