Review: std.logger

Johannes Pfau via Digitalmars-d digitalmars-d at puremagic.com
Sat Jul 12 02:17:50 PDT 2014


Am Fri, 11 Jul 2014 21:26:25 +0000
schrieb "Robert burner Schadek" <rburners at gmail.com>:

> On Friday, 11 July 2014 at 18:02:58 UTC, Marc Schütz wrote:
> > Some logging backends (e.g. systemd journal) support structured 
> > logging. Should support for this be included (as a subclass, 
> > presumably)?
> 
> Well, the idea behind std.logger is that I can not and should not 
> even try to give you implementations for all your backend needs. 
> It will just fall short and you will tell me that is it bad and 
> should not go into phobos. And even if I manage it is going to be 
> curl all over again.
> 
> So subclass Logger, implement writeLogMsg to your needs and be 
> happy.

Yes, but for structured loggers the frontend log* functions do not
provide enough information to the backend. Of course you can use the
systemd journal as a normal backend, but you lose features.


I think we can provide structured logging support as a non-breaking API
extension, so we should not make this part of this review. But here's
how I'd imagine such an API to work:

Frontend:

* log* get new overloads which accept (T...) as the last parameter (or
  if T... is already the last parameter that's fine).

* Add a new struct to logger.core: struct MsgID which is just a
  strong typedef for UUID
* Add a templated type, KeyValue, which can be used like this:
  KeyValue("user", "nobody") //string key / string value
  KeyValue("age", 42); //string key / T value
  KeyValue("msg", "Hello %s! %s", "World", 42); //string key/fmt val
* KeyValue stores it's parameters, no string processing yet
* Multivalue parameters handled by many KeyValue with same key? Might
  complicate backend. Or don't support multivalue at all? Or
  KeyValue("key", MultiValue(a, ,b, c)) (MultiValue == Tuple?)
* Structured loggers do not use msg, instead they use a KeyValue with
  "msg" key. This is cause you usually want different messages with
  structured loggers. We still keep everything in one function, so the
  user doesn't have to do "if(structuredlogger) logstruct() else log()"
  for every log message.
* MsgID marks the end of normal format parameters. See example below.
  This is also the reason why we can't use UUID directly

Usage:
string error;
logf("Something bad happened: %s", error,
     MsgID("abcd-valid-uuid"),       //MsgID--> end of fmt params
     KeyValue("msg", "Something bad happend"),
     KeyValue("error-code", error));

output:
normal backend: test.d:42 Something bad happened: out of memory
structured backend: (only example, exact format backend specific)
{
  "msg": "Something bad happened",
  "error": "out of memory",
  "file": "test.d",
  "line": 42
}




The next part is an efficient Backend Layer:

class StructuredLogger : Logger
{
    logHeader;
    writeLogMsg; //Not used
    finishLogMsg;

    void logKey(string key);
    void valuePart(const(char)[] part);
    void finishValue(bool last); //Last only if we support multivalue
}

Usage:

auto slog = new StructuredLogger();
slog.logHeader(...);
foreach(KeyValue kv; T...)
{
    slog.logKey(kv.key);
    //Need slog -> outputrange adapter: map put<>valuePart
    //see https://github.com/burner/logger/pull/9
    formattedWrite(wrap(slog), kv.formatstring, kv.args);
    slog.finishValue(true);
}
finishLogMsg();



More information about the Digitalmars-d mailing list