Suggestion/proposal regarding std.logger candidate

David Osborne via Digitalmars-d digitalmars-d at puremagic.com
Sat May 24 02:57:33 PDT 2014


Yesterday, in the livestream chat, there was some discussion about
the current std.logger proposal.  The consensus seemed to be that
it's probably fine for most use cases and it's the best proposal
we've had so far, it doesn't look suitable for heavy duty,
enterprise-grade logging.  In an ideal world, the std.logger 
module
in phobos would be flexible enough to build an enterprise-grade
logging library on top of, but it seems the current proposal isn't
there yet.  I got to thinking about how to make the logging system
more flexible, and this is what I came up with.

I identified three key parts to a logger: the filter, the format,
and the sink.

     log.trace(...) ----> Filter ----> Format ----> Sink

The filter determines which log messages to act on, and which to
ignore.  Usually this is done based on the log level, but one use
case that was brought up was automatically sending an email to the
developers only when a particular exception occurred (i.e. filter
based on Exception type).  Currently, std.logger implements the
filtering for you, based on the global log level, and the log 
level
of that particular logger.

The format determines what the output of the log message looks 
like.
Usually, this winds up being some sort of call to formattedWrite,
though there is no need to assume that the output must be a 
string,
or even human-readable.  Currently, formatting tends to be
implemented as part of Logger.writeLogMsg, and is specific to each
Logger implementation.

The sink is where the log message goes.  Popular destinations
include stderr, /dev/null, some text file somewhere, or a central
logging facility provided by your OS.  Currently, the sink is
specified when implementing Logger.writeLogMsg.

Based on these three parameters, I came up with the following 
proof
of concept implementation, built on top of the current proposal:

public class Clogger(alias format, alias filter, Sink) : Logger
     if( isLogFilter!filter && isLogFormat!format && 
isLogSink!(Sink, format) )
{

     public this(Sink sink, string newName, LogLevel lv) {
         super(newName, lv);
         this.sink = sink;
     }

     override
     public void writeLogMsg(ref LoggerPayload payload) {
         import std.algorithm : copy;

         if( this.filterFun(payload) ) {
             sink = formatFun(payload).copy(sink);
         }
     }

     private:
     Sink sink;
     alias formatFun = unaryFun!format;
     alias filterFun = unaryFun!filter;

}

The full code is at https://github.com/krendil/clogger.

As you can see, there are three template parameters, that 
correspond
to the three components identified above.

- filter is a predicate that takes a LoggerPayload and returns 
true
     if that log message should be logged.

- format takes a LoggerPayload and converts it to something
     suitable for output, in the form of a Range.

- Sink is an OutputRange that has the same element type as the
     the output of format.

I think there are a few benefits to this setup:
   - Separation of format from sink
   - Custom, arbitrary filtering
   - Simpler to implement new Loggers

With this setup, multiplexing can either be done at the Logger
level, as it is currently, or at the Sink level. Log messages can
still be converted to any sort of output. Aliases or convenience
functions could be created for common Logger types (I currently 
have
convenience functions for a multiplexer, a stdout logger and a
generic dchar range logger).

Destroy :)


More information about the Digitalmars-d mailing list