July 22, 2014
On Tuesday, 22 July 2014 at 10:24:33 UTC, linkrope wrote:

>
> Not the need is artificial. For example, I have the need to measure the performance of an application with and without logging.
>
> I think, the solution is artificial. The obvious solution would be to register no logger at all.

that solution can also be unpractical and unworkable
July 22, 2014
On Tuesday, 22 July 2014 at 10:24:33 UTC, linkrope wrote:
> On Tuesday, 22 July 2014 at 09:51:24 UTC, ponce wrote:
>> On Tuesday, 22 July 2014 at 08:44:06 UTC, linkrope wrote:
>>> On Tuesday, 22 July 2014 at 07:27:38 UTC, ponce wrote:
>>>> On Sunday, 20 July 2014 at 16:15:53 UTC, linkrope wrote:
>>>>> 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.
>>>>
>>>> I asked for it. And I use it, because I write libraries that log warnings but don't forcefully require the users to provide a Logger if they don't want to.
>>>> And that way, I can still write "logger.warningf" without "if" everywhere.
>>>
>>> But then it's better to provide no logger (or at least no logger for level warning) than an artificial NullLogger.
>>
>> My need is not "artificial", at least in my view.
>> Your opinion is different from mine. That's fine.
>> That's why we need someone to try to reconcile the many, many opinions about this.
>
> Not the need is artificial. For example, I have the need to measure the performance of an application with and without logging.
>
> I think, the solution is artificial. The obvious solution would be to register no logger at all.

This only works if you have a global set of registered loggers (like your solution https://github.com/linkrope/log/blob/master/src/log.d#L133).
The obvious flaw is that prevents to have a different logging sink for different parts of a program. Now is it ever needed? Yes, for topical logging (eg. "I want only log messages from subsystem A", again real use case).

std.logger use the Composite pattern http://en.wikipedia.org/wiki/Composite_pattern instead to allow for a hierarchy.
July 22, 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?
July 22, 2014
On Tuesday, 22 July 2014 at 21:52:09 UTC, linkrope wrote:
> 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.)

There was no need, even read that, for implementing a FileRotateLogger

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

I did and I had to mod it quite a bit to make it work with default logging instance logging and an more elaborate api

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

passing a bool as a delegate that is the first thing that gets evaluated does not sound right.

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

In your last post you said that you wrote it on a weekend, which is it?

That api may work for your requirements, but this should go into phobos and allow anybody to get satisfied.
July 23, 2014
> That api may work for your requirements, but this should go into phobos and allow anybody to get satisfied.

I think a hardcoded fast version is preferable, then you simply use a modified version of phobos/runtime if you want low overhead in-memory logging.

The most important thing, besides being fast, is that everybody use the same syntax, making it grep'able.

July 23, 2014
On Wednesday, 23 July 2014 at 06:10:22 UTC, Ola Fosheim Grøstad wrote:
>
> The most important thing, besides being fast, is that everybody use the same syntax, making it grep'able.

It is easily grepable
July 23, 2014
On Tuesday, 22 July 2014 at 23:43:59 UTC, Robert burner Schadek wrote:
> On Tuesday, 22 July 2014 at 21:52:09 UTC, linkrope wrote:
>> 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.
>
> passing a bool as a delegate that is the first thing that gets evaluated does not sound right.

Indeed: that's why the lazy condition should be evaluated last!

Your "will log" condition is very simple and efficient: only >= and !=
But you cannot know, how long the evaluation of the user-provided condition will take.

BTW: with 'globalLogLevel', 'defaultLogger.logLevel'and 'LogLevel.off' the usual 'willLog' predicate will come in handy to avoid code duplication.

While the lazy evaluation of the condition would be the only advantage over

    if (condtion) log(...);

I haven't encountered a single opportunity for conditional logging in the code of our company.

'if'/'log' is always followed by 'return', 'break', 'continue', ...

    if (condition)
    {
        log("will do something else because condition passed");
        return;
    }
July 23, 2014
On Wednesday, 23 July 2014 at 09:15:26 UTC, Robert burner Schadek wrote:
> On Wednesday, 23 July 2014 at 06:10:22 UTC, Ola Fosheim Grøstad wrote:
>>
>> The most important thing, besides being fast, is that everybody use the same syntax, making it grep'able.
>
> It is easily grepable

I mean in the sense that you can write a regexp that catch all cases for automated search and replace.
July 23, 2014
On Wednesday, 23 July 2014 at 22:07:16 UTC, Ola Fosheim Grøstad wrote:
>>
>> It is easily grepable
>
> I mean in the sense that you can write a regexp that catch all cases for automated search and replace.

that is no problem.
July 24, 2014
On Wednesday, 23 July 2014 at 17:56:52 UTC, linkrope wrote:
>
> Indeed: that's why the lazy condition should be evaluated last!
>
> Your "will log" condition is very simple and efficient: only >= and !=
> But you cannot know, how long the evaluation of the user-provided condition will take.

I believe this is a matter of opinion. From what I have seen the condition is usually very easy to computer and does not justify turning it into a delegate.

>
> BTW: with 'globalLogLevel', 'defaultLogger.logLevel'and 'LogLevel.off' the usual 'willLog' predicate will come in handy to avoid code duplication.

Why didn't you start your review with something useful like this?

>
> While the lazy evaluation of the condition would be the only advantage over
>
>     if (condtion) log(...);
>
> I haven't encountered a single opportunity for conditional logging in the code of our company.
>

All swans are white!