Review: std.logger
linkrope via Digitalmars-d
digitalmars-d at puremagic.com
Tue Jul 22 14:52:07 PDT 2014
Sorry, but at the first contact with the implementation (the one
with 'genDocComment' and 'buildLogFunction' from months ago) I
was scared away. (It's better now.)
I feared that if I criticize the 'mixin' sequences you would ask
something like "suggestions?" ;-)
So, I made this experiment to provide an answer. Now, I can
suggest: try something like this:
alias trace = log!(LogLevel.trace);
alias info = log!(LogLevel.info);
alias warn = log!(LogLevel.warn);
alias error = log!(LogLevel.error);
alias fatal = log!(LogLevel.fatal);
(see https://github.com/linkrope/log/blob/master/src/log.d)
Controversial conditional logging
----------------------------
The only advantage of
tracec(condition, "passed");
over
if (condition) trace("passed");
would be, that a costly evaluation of the condition is omitted
when there is no trace logger.
That's why the std.log proposal had 'when(lazy bool now)'.
First, I was puzzled about your argument that LOG_FIRST_N or
LOG_EVERY_N would be no problem with the '...c' functions. But a
look at the implementation confirmed that the std.logger has no
lazy evaluation of the condition; discarding the only advantage.
Sets of log levels
--------------
No!
Of course, I can log (trace | info) to stdout, warn.orHigher to
stderr, and for instance info.orHigher to some file.
Simplicity
--------
"The simplest way to achieve simplicity is through thoughtful
reduction."
We started with tango.util.log (best described as log4j for D).
We are happier now with a lot less functionality, but on the
other hand with the simplest API that works.
On Monday, 21 July 2014 at 22:53:27 UTC, Robert burner Schadek
wrote:
> On Sunday, 20 July 2014 at 16:15:53 UTC, linkrope wrote:
>> Pros
>> ----
>> The lighning talk about the std.logger proposal at DConf 2014
>> had a positive impact.
>> We were able to change the "Current D Use" entry of our
>> company from
>> "Uses D2 / Phobos, Tango (log, xml)"
>> to
>> "Uses D2 / Phobos, Tango (xml)".
>> (We got rid of tango.util.log; we still rely on the fast
>> tango.text.xml.)
>
> I didn't expect to hear from you about this, after you did not
> reply to my email about this topic.
>
> If xml is problem for you, where is the PR?
>
>>
>> Cons
>> ----
>> 1. I am not happy with the (reverse) hungarian-style naming
>>
>> At least in the code of our company, logging a formatted
>> string is the basic use case.
>> The function for the basic use case should not require a
>> suffix letter.
>> The consistency argument, that 'infof' is like 'writef', does
>> not fully apply:
>> neither 'infoln' nor 'infofln' make sense.
>> (In my opinion, "half consistent" is inconsistent.)
>
> so we have gone full circle now ....
>
>>
>> Currently, suffix 'c' is used for conditional logging.
>> But, how will future extensions like glog's LOG_EVERY_N or
>> LOG_FIRST_N be named?
>
> That's an easy one.
>
> ```
> auto a = LOG_FIRST_N(1337);
>
> logc(a, "Hello world");
>
> auto b = WHAT_EVERY_THE(....);
> logc(b, "Hello world again");
> ```
>
>> With suffix 'e'? Suffix 'f' is already assigned!
>
> what is 'e'?
>
>> The suffix letter sequence seems to be the road to confusion.
>>
>> I would prefer the explicit naming of the previous std.log
>> proposal:
>> log.when(condition)(...)
>> However, there is only a small advantage over
>> if (condition) log(...)
>
> ...
>
>>
>> 2. No support for 24/7 (server) applications
>>
>> In my opinion, I really need logging for applications that
>> possibly run forever.
>> With the FileLogger, the file will grow forever.
>> That's why most other frameworks provide something like a
>> RollingFileLogger or some "logrotate-aware" FileLogger.
>>
>> By the way: Instead of what I really need, I get a NullLogger.
>> I have no clue, why I never ever missed such an oddity.
>
> That was a user request, through github. Where I asked you to
> submit PRs and issues.
>
> Have you tried subclassing Logger? I asked for PRs in the email
> I wrote to you at least twice.
>
>>
>> 3. Implementation is hidden behind 'mixin' expressions
>>
>> When I tried to look at the implementation, I found long
>> sequences of lines like this:
>> mixin(buildLogFunction(true, false, false, LogLevel.info));
>>
>> Nowadays, this changed into:
>> mixin(freeLog.format(
>> "info", "info", "info", "info",
>> "info", "info", "info", "info",
>> "info", "info", "info", "info",
>> "info", "info", "info", "info"));
>>
>> This is much better, but I still think, it's a complicated
>> solution for a simple problem.
>> And it would be a shame for D, if there is no simple solution.
>
> Yes please, suggestions?
>
>>
>> Small stuff
>> -----------
>> 4. FileLogger needs flush
>>
>> It's annoying when the events that caused a crash have been
>> logged, but they never have been written to the file.
>
> I will fix that in the next session.
>
>>
>> 5. Suspect use of '__gshared'
>>
>> The FileLogger has a field
>> private __gshared File file_;
>>
>> In this case, "__gshared is equivalent to static".
>> This means that all FileLogger instances share the same file!
>
> I missed that, thank you
>
>>
>> 6. Bad naming of "StdIOLogger"
>>
>> Like 'std.stdio.StdioException', the 'io' should be lower case.
>> If the 'StdIOLogger' logs to 'stdout', 'StdoutLogger' would be
>> preferable.
>
> easy fix
>
>>
>> 7. No need for StdIOLogger
>>
>> 'stdout' (and 'stderr') are Files, so a FileLogger should be
>> able to handle them.
>> A second constructor should do the trick.
>
> It is a special file, I wanted to have that clear. two
> different classes does the trick for me.
>
>>
>> 8. Log levels
>>
>> Many frameworks mix the types "log level" and "set of log
>> levels" (for filtering).
>> While 'trace', ..., 'fatal' are log levels, 'all' and 'off'
>> (better: 'none'?) are sets of log levels.
>> (I have no idea about the type of 'unspecific'.)
>>
>> A clean separation would avoid confusion:
>> why is there 'info(...)' but not 'all(...)'?
>
> unspecific is about to be removed, all and off are pretty easy
> to understand but than ....
>
>>
>> Also, it would be easier to log for example 'trace' and 'info'
>> to 'stdout'.
>
> not if you want to have that logged somewhere else.
>
>>
>> 9. Bad naming of "std.logger"
>>
>> The focus of this proposal is on the log/logging API; the
>> loggers are only examples.
>>
>> The recommended use should be
>> import log = std.logger;
>
> you got that wrong, you can do it like that, nobody will force
> you and properly people will do it different.
> You can also create a module wide global logger and use that
>
> Again, std.logger is not the solution that works for every
> special case anybody comes up with out of the box. It is a set
> of ideas that enable you to have the logging tailored to your
> needs easily. On top of that, it allows you very fast access to
> basic logging that can be extend later on easily and seamlessly.
>
>>
>> Then, the name "std.log" (of the previous proposal) would be
>> more appropriate.
>>
>> Counter Proposal
>> ----------------
>> As a consequence of these issues, I once decided to spend a
>> weekend to prepare a counter proposal:
>>
>> http://code.dlang.org/packages/log
>>
>> The design goal was simplicity. So:
>> - conditional logging is not supported
>> - no suffix letter sequences
>
> So you deleted code and functionality, hm?
>
>> - there is no NullLogger
>
> Same point
>
>> - there is no MultiLogger (this functionality is implicit)
>
> It is not, you can't remove Loggers individual and you can't
> build trees.
>
>> - there is no need to provide a name for a logger
>
> Because, you have no MultiLogger
>
>>
>> I prefer 'alias' over 'mixin':
>> 'info' is just an alias for 'log(arg)' as well as for
>> 'log(fmt, args)' at log level 'info'.
>>
>> Sets of log levels are implemented as (bit) sets of log levels.
>> A helper function lets you select the traditional >= filtering:
>> LogLevel.info.orHigher
>
> So I need nine LogLevels, how do I add one between info and
> warn?
>
>>
>> For convenience, 'stdoutLogger' and 'stderrLogger' are factory
>> functions that create 'FileLogger' instances.
>
> Object.factory
>
>>
>> Of course, a RollingFileLogger as well a a
>> "RotatingFileLogger" (that reopens the log file on SIGHUP) are
>> provided.
>
> Subclassing Logger should get the job done in under 30 lines.
>
>>
>> By now, this simple solution is in use in tens of thousands
>> lines of commercial code.
>> (Where it outperforms the previously used tango.util.log
>> implementation.)
>
> So what are the numbers for std.logger and where is the
> benchmark file to test it?
>
> From the source it looks like you kept parts of the design of
> std.logger and pulled out everything you didn't agree with.
> Also you only have global logging, to cite Brian: "Why?"
>
> You added the parts, that you described are missing in
> std.logger. Of course your counter proposal will meet your
> needs then. I mean what would be the point of coding it up
> anyway else?
More information about the Digitalmars-d
mailing list