A Perl6 remote logger with ZMQ
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
bin encoding fixed for Net::ZMQ Nov 27, 2017
lib/Log/ZMQ encoding fixed for Net::ZMQ Nov 27, 2017
t test bugfix Dec 12, 2017
.gitignore encoding fixed for Net::ZMQ Nov 27, 2017
.travis.yml Update .travis.yml Sep 19, 2017
LICENSE.md initial Sep 10, 2017
META6.json Meta updated Dec 7, 2017
README.md bugfixed Nov 20, 2017




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


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.)


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


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://'\
                            , :default-level( :warning )\
                            , :domain-list( < database engine front-end nativecall > )\
                            , :format( :json ))\

$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://' --prefix example \
                    	       --level debug database front-end



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

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

The default uri is 'tcp://'

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


The logger

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


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;

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


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