LogLevel [was std.experimental.logger formal review round 3]

Rikki Cattermole via Digitalmars-d digitalmars-d at puremagic.com
Wed Dec 3 18:41:42 PST 2014


On 4/12/2014 2:10 p.m., Martin Nowak wrote:
> I just found a very compelling alternative solution to the LogLevel
> disabling problem. This was one of the reasons for the delay of std.log
> and the current solution still isn't great [1].
>
> This idea here achieves
> - fine grained control over log levels (per module/package)
> - zero overhead for statically disabled log levels
> - zero (almost) boilerplate (works even with the global stdlog)
> - and it establishes a nice convention to enable logging for a library
>    `pkg.logLevel = LogLevel.info;`
>
> Basically it works by declaring a logLevel in a module or package.
> Then the std.logger module does a reverse lookup of that declaration.
>
> ```d
> module mymod;
>
> import std.logger;
>
> // can be anything that converts to a LogLevel
> // calls with lower log level can be optimized away if this is a compile
> time constant
> // runtime values also work nicely but incur a small overhead
> // if this declaration is not present, logLevel from the package is used
> or the default LogLevel.
>
> enum logLevel = LogLevel.critical;
>
> void foo()
> {
>      info("information from foo"); // optimized out
>      fatal("error");               // logs
> }
> ```
>
> https://gist.github.com/MartinNowak/443f11aa017d14007c35
>
> There is a tiny gotcha, this works because the logger module imports the
> calling module. So we'd need to avoid module constructors in the logger
> module or use a workaround [2].
>
> Also it can lead to additional imports when the whole package is
> imported to resolve logLevel.  This can be avoided though by declaring
> logLevel in a separate module and making it available in every module of
> a package.
>
> ```d
> module mypkg.loglevel;
>
> LogLevel logLevel;
> ```
> ```d
> module mypkg.foo.bar;
>
> public import mypkg.logLevel;
>
> void baz(T)(T t)
> {
>      info("baz");
> }
> ```
>
> [1]: http://forum.dlang.org/post/m2p3r6$148j$1@digitalmars.com
> [2]:
> https://github.com/D-Programming-Language/phobos/blob/9a46840c2b36beb6711244ec4340c117fc91a0f1/std/stdiobase.d

I also just wrote some test code based upon this idea.

$ cat a.d
module a;

enum level = "test";

void main() {
         test("hi");
}

void test(T, string module_ = __MODULE__)(T t) {
         import std.stdio;
         mixin("static import mod = " ~ module_ ~ ";");
         writeln(mod.level, ": ", t);
}

$ cat b.d
module b;
import a;

enum level = "BModule";

void afunc() {
         test("something");
}

static this() {
         import std.stdio;
         writeln("static this on b");
}


shared static this() {
         import std.stdio;
         writeln("shared static this on b");
}

$ rdmd b.d
shared static this on b
static this on b
test: hi

Okay its a lot simpler and far less context specific.
One thing I think should be addressed is at runtime, to override these 
defaults.
At both function/method level and at module level.

I'm not sure much can be done about module constructors, but static 
imports would probably be the best place to begin.
Now theoretically it should also be a good place to look at using UDAS 
to override per function/method. Same goes for with statements for 
runtime changes.

But in any case this is suddenly going into terms of frameworks not just 
logging libraries and making me go *shudder* not for phobos.


More information about the Digitalmars-d mailing list