### ----------------------------------------------------
### -- Log::ZMQ
### -- Licenses: Artistic-2.0
### -- Authors: Gabriel Ash, github:gabrielash
### -- File: README.md
### ----------------------------------------------------
# Log::ZMQ

## SYNOPSIS

Log::ZMQ is a Perl6 Logger using zeromq to log over tcp/ip

## Introduction

A decoupled remote logger. The client sends log meesages from the application to a
LogCatcher listening on a tcp port.

The backend uses a publisher-subscriber pattern, suitable for debugging asynchronous
apps (by sheer luck, the purpose of writing it) but not much else. A more general 
framework would require changing the pattern.

Setup is minimal: A single application-wide call to Logging::instance with arguments, 
plus no-parameters call for each additinal place in the code that wants to hold its own logger. 
Enumerated arguments can be entered with colon notation and it is possible to set defaults 
and log with no extra arguments. A global .set-supress-level( :level)  can turn all
logging off. When suppressed, log calls incur only the cost of argument checking.

Format is a choice of json, yaml and a raw format based on ZMQ frames. It can be 
extended on both sides with user-provided functions. On the backend,
there is currently no distinction between adding parsers and adding handlers. The 
built-in parsers-handelrs all write the logged message in multiline to STDOUT 
(useful for debugging, not so much for monitoring.)

#### Status

In development. This is my learning process of perl6 and ZMQ. I have a lot to learn so use with care.


#### Portability
  depends on Net::ZMQ:auth('github:gabrielash');

## Example Code

#### A (minimal)
    my $l = Logging::instance( 'example' ).logger;
    $l.log( 'an important message');

    my $l2 = Logging::instance.logger;
    $l2.log( 'another important message');

#### B ( more elaborate )
    my $logger = Logging::instance('example', 'tcp://78.78.1.7:3301'\
                                , :default-level( :warning )\
                                , :domain-list( < database engine front-end nativecall > )\
                                , :format( :json ))\
                          .logger;      

    $logger.log( 'a very important message', :critical, :front-end );

    my $db-logger = Logging::instance.logger.domain( :database );
    $db-logger.log( 'meh');

#### C (the log catcher on the other side )
    # on the command line:
    log-catcher.pl --uri 'tcp://78.78.1.7:3301' --prefix example \
                        	       --level debug database front-end

## Documentation

#### Log::ZMQ::Logging

  The logging framework based on ZMQ. Usually a singleton summoned with

    my $log-system = Logging::instance('prefix', ['tcp://127.127.8.17:8022', ... ]) ;
        ;  only supply parameters the first time

    The default uri is 'tcp://127.0.0.1:3999'

    Attributes
    prefix;   required
    default-level; (default = info)
    format;   default yaml
    domain-list; default ('--')            ;if left blank no domain is required below

  Methods
    logger()  ; returns a Logger
    set-supress-level(:level)               ;silences logging globally
    unset-supress-level()
    add-formatter()                         ;see below
    set-format()                            ;must use this after adding a formatter


#### Log::ZMQ::Logger

The logger

    Methods
      log( log-message, :level, :domain )
        ; level is optional.
        ; domain is optional only if a domain list is not set


The logging uses a publisher socket. All protocols send 5 frames
  1. prefix
  2. domain
  3. level
  4. format [ zmq | yaml | json | ... ]
  5. empty frame

followed with frames that depend on the format.
For zmq:
  6. content
  7. timestamp
  8. target

for yaml/json:
  6. yaml/json formatted  

To add custom formatter, use instance.add-formatter.  
  :(MsgBuilder :builder, :prefix, :timestamp, :level, :domain,  :content
                        --> MsgBuilder ) {
  ... your code here ...
  return $builder;
  }
    the builder should be returned unfinalized.
  then set the format:
    set-format('name');

#### Log::ZMQ::catcher

handles the logging backend, listening to zmq messages.

The wrapper script log-catcher.pl can be invoked from the cli. to see options:
    log-catcher.pl --help

This is the body of the MAIN sub

    my $c = $uri.defined ?? LogCatcher::instance(:$uri, :debug )
                          !! LogCatcher::instance( :$debug );
    $c.set-level-filter( $level);
    $c.set-domains-filter(| @domains) if @domains;
    $c.run($prefix);

current implemention print the received messaged to stdout. other backends can be added
with the following methods:

  * add-zmq-handler( &f:(:$content, :$timestamp, :$level, :$domain, :$prefix) )
  * add-handler( Str $format,  &f:(Str:D $content) )


## LICENSE

All files (unless noted otherwise) can be used, modified and redistributed
under the terms of the Artistic License Version 2. Examples (in the
documentation, in tests or distributed as separate files) can be considered
public domain.

ⓒ 2017 Gabriel Ash


