March 07, 2012
On Tuesday, March 06, 2012 19:27:35 Andrei Alexandrescu wrote:
> On 3/6/12 7:04 PM, Jonathan M Davis wrote:
> > On Tuesday, March 06, 2012 18:19:23 Jose Armando Garcia wrote:
> >> Fatal and Critical are exactly these continence functions... To reiterate. fatal will always assert and critical will always throw. It is impossible for the user to disable these things.
> > 
> > No, because they affect the log level. The concept of throwing and the log level should be _completely_ separate.
> 
> Why?

Because the level that you log something at and what you want to do in terms of exceptions aren't necessarily related at all. It could easily be that you want to log something and then do some series of operations before throwing - even if the log level is the most severe level, and you intend to throw an Error to kill the program. And as others have pointed out, you might want to log a series of messages. Having std.log throw on the first one makes it so that you can't log any others.

> > std.log shouldn't be declaring _any_
> > exception types unless they're related to setting up the logging (_none_
> > which relate to functions which log).
> 
> Why?

Because as others have asserted, logging should not affect program flow. It's printing out messages to a log, which doesn't necessarily have _anything_ to do with throwing exceptions. It's merely for providing information about what the program is doing. If you have it throwing exceptions - _especially_ exceptions which are specific to it - you're conflating two separate concepts: logging to a log file and having the program report errors. They _can_ be related, but they often aren't.

And when you _do_ throw an exception, why on earth would anyone want a LoggingException (or whatever std.log would call its exception type)? You want an exception which relates to what went wrong, not what threw it. The fact that you logged a message before throwing is incidental. Nothing that catches the exception is going to care.

- Jonathan M Davis
March 07, 2012
On 7 March 2012 16:43, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> On Tuesday, March 06, 2012 19:27:35 Andrei Alexandrescu wrote:
>> On 3/6/12 7:04 PM, Jonathan M Davis wrote:
>> > On Tuesday, March 06, 2012 18:19:23 Jose Armando Garcia wrote:
>> >> Fatal and Critical are exactly these continence functions... To reiterate. fatal will always assert and critical will always throw. It is impossible for the user to disable these things.
>> >
>> > No, because they affect the log level. The concept of throwing and the log level should be _completely_ separate.
>>
>> Why?
>
> Because the level that you log something at and what you want to do in terms of exceptions aren't necessarily related at all. It could easily be that you want to log something and then do some series of operations before throwing - even if the log level is the most severe level, and you intend to throw an Error to kill the program. And as others have pointed out, you might want to log a series of messages. Having std.log throw on the first one makes it so that you can't log any others.
>
>> > std.log shouldn't be declaring _any_
>> > exception types unless they're related to setting up the logging (_none_
>> > which relate to functions which log).
>>
>> Why?
>
> Because as others have asserted, logging should not affect program flow. It's printing out messages to a log, which doesn't necessarily have _anything_ to do with throwing exceptions. It's merely for providing information about what the program is doing. If you have it throwing exceptions - _especially_ exceptions which are specific to it - you're conflating two separate concepts: logging to a log file and having the program report errors. They _can_ be related, but they often aren't.
>
> And when you _do_ throw an exception, why on earth would anyone want a LoggingException (or whatever std.log would call its exception type)? You want an exception which relates to what went wrong, not what threw it. The fact that you logged a message before throwing is incidental. Nothing that catches the exception is going to care.
>
> - Jonathan M Davis

Surprisingly, I agree with the idea that fatal and critical shouldn't throw, or at least shouldn't throw by default, maybe a configuration option would allow for that functionality. Logging probably shouldn't affect program flow.

Its possible that I may need to log a "critical" error, then do some graceful shutdown.

In my opinion, critical and error should /not/ throw by default, however they should be able to get an optional Exception to throw, if that is appropriate behavior.

--
James Miller
March 07, 2012
On 3/6/12 7:43 PM, Jonathan M Davis wrote:
> Because the level that you log something at and what you want to do in terms
> of exceptions aren't necessarily related at all. It could easily be that you
> want to log something and then do some series of operations before throwing -
> even if the log level is the most severe level, and you intend to throw an
> Error to kill the program. And as others have pointed out, you might want to
> log a series of messages. Having std.log throw on the first one makes it so
> that you can't log any others.

Then I guess you'd be well advised to use the function that "logs to the error log", not the one that "logs to the error log and then throws". No? I mean you're explaining how a screwdriver is not appropriate for pounding nails.

>>> std.log shouldn't be declaring _any_
>>> exception types unless they're related to setting up the logging (_none_
>>> which relate to functions which log).
>>
>> Why?
>
> Because as others have asserted, logging should not affect program flow.
> It's
> printing out messages to a log, which doesn't necessarily have _anything_ to
> do with throwing exceptions.

It does, because, as I mentioned, frequently one is interested in logging erroneous events that are followed by a breakage of flow.

> It's merely for providing information about what
> the program is doing. If you have it throwing exceptions - _especially_
> exceptions which are specific to it - you're conflating two separate concepts:
> logging to a log file and having the program report errors. They _can_ be
> related, but they often aren't.

Well all I can say is they are in my experience. Some logging libraries have primitives that throw, some don't. I prefer a library that has the option in it because I use it all the time with glog.

> And when you _do_ throw an exception, why on earth would anyone want a
> LoggingException (or whatever std.log would call its exception type)? You want
> an exception which relates to what went wrong, not what threw it. The fact
> that you logged a message before throwing is incidental. Nothing that catches
> the exception is going to care.

That I do agree with.


Andrei
March 07, 2012
On Tuesday, March 06, 2012 20:10:58 Andrei Alexandrescu wrote:
> On 3/6/12 7:43 PM, Jonathan M Davis wrote:
> > Because the level that you log something at and what you want to do in terms of exceptions aren't necessarily related at all. It could easily be that you want to log something and then do some series of operations before throwing - even if the log level is the most severe level, and you intend to throw an Error to kill the program. And as others have pointed out, you might want to log a series of messages. Having std.log throw on the first one makes it so that you can't log any others.
> 
> Then I guess you'd be well advised to use the function that "logs to the error log", not the one that "logs to the error log and then throws". No? I mean you're explaining how a screwdriver is not appropriate for pounding nails.

Sure, but std.log appears to have built in whether an exception is thrown or not into the log level itself. The log level and whether an exception is thrown are not necessarily related. Having one log function which throws and another which doesn't is fine. It's the fact that this proposal ties the exceptions to the log levels which is the problem.

> >>> std.log shouldn't be declaring _any_
> >>> exception types unless they're related to setting up the logging (_none_
> >>> which relate to functions which log).
> >> 
> >> Why?
> > 
> > Because as others have asserted, logging should not affect program flow.
> > It's
> > printing out messages to a log, which doesn't necessarily have _anything_
> > to do with throwing exceptions.
> 
> It does, because, as I mentioned, frequently one is interested in logging erroneous events that are followed by a breakage of flow.

Sure, you _may_ want to throw when you log, and having a way to do that in one command is fine. The problem is that you don't _always_ want to throw or not at a particular log level, and this std.log proposal ties to the two together.

> > And when you _do_ throw an exception, why on earth would anyone want a LoggingException (or whatever std.log would call its exception type)? You want an exception which relates to what went wrong, not what threw it. The fact that you logged a message before throwing is incidental. Nothing that catches the exception is going to care.
> 
> That I do agree with.

And the current proposal declares a CriticalException type which is thrown when logging at the critical level. So, while we may agree on this, the current proposal does not.

- Jonathan M Davis
March 07, 2012
On 07.03.2012 7:54, James Miller wrote:
> On 7 March 2012 16:43, Jonathan M Davis<jmdavisProg@gmx.com>  wrote:
>> On Tuesday, March 06, 2012 19:27:35 Andrei Alexandrescu wrote:
>>> On 3/6/12 7:04 PM, Jonathan M Davis wrote:
>>>> On Tuesday, March 06, 2012 18:19:23 Jose Armando Garcia wrote:
>>>>> Fatal and Critical are exactly these continence functions... To
>>>>> reiterate. fatal will always assert and critical will always throw. It
>>>>> is impossible for the user to disable these things.
>>>>
>>>> No, because they affect the log level. The concept of throwing and the log
>>>> level should be _completely_ separate.
>>>
>>> Why?
>>
>> Because the level that you log something at and what you want to do in terms
>> of exceptions aren't necessarily related at all. It could easily be that you
>> want to log something and then do some series of operations before throwing -
>> even if the log level is the most severe level, and you intend to throw an
>> Error to kill the program. And as others have pointed out, you might want to
>> log a series of messages. Having std.log throw on the first one makes it so
>> that you can't log any others.
>>
>>>> std.log shouldn't be declaring _any_
>>>> exception types unless they're related to setting up the logging (_none_
>>>> which relate to functions which log).
>>>
>>> Why?
>>
>> Because as others have asserted, logging should not affect program flow. It's
>> printing out messages to a log, which doesn't necessarily have _anything_ to
>> do with throwing exceptions. It's merely for providing information about what
>> the program is doing. If you have it throwing exceptions - _especially_
>> exceptions which are specific to it - you're conflating two separate concepts:
>> logging to a log file and having the program report errors. They _can_ be
>> related, but they often aren't.
>>
>> And when you _do_ throw an exception, why on earth would anyone want a
>> LoggingException (or whatever std.log would call its exception type)? You want
>> an exception which relates to what went wrong, not what threw it. The fact
>> that you logged a message before throwing is incidental. Nothing that catches
>> the exception is going to care.
>>
>> - Jonathan M Davis
>
> Surprisingly, I agree with the idea that fatal and critical shouldn't
> throw, or at least shouldn't throw by default, maybe a configuration
> option would allow for that functionality. Logging probably shouldn't
> affect program flow.
>
> Its possible that I may need to log a "critical" error, then do some
> graceful shutdown.

Exception is a graceful shutdown, as it calls destructors & finally blocks while unrolling the stack.

>
> In my opinion, critical and error should /not/ throw by default,
> however they should be able to get an optional Exception to throw, if
> that is appropriate behavior.
>
> --
> James Miller


-- 
Dmitry Olshansky
March 07, 2012
On Tue, 06 Mar 2012 14:32:37 -0500, Jose Armando Garcia <jsancio@gmail.com> wrote:

> On Mon, Mar 5, 2012 at 5:30 PM, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>>
>> Except 'info', 'error', 'warning' are all common names, likely to be a very
>> attractive name for something that has nothing to do with (or cares about)
>> logging.  cout is not a common name or even an english word, so it's
>> unlikely someone has or wants to create a cout member.
>>
> Actually, I see this more as argument as to why cout is a horrible
> name for a symbol in std.stdio. I suspect that the only reason that it
> is there is to keep C developer migrating to D happy. It should
> probably just be "out" like Java (System.out) and C# (Console.Out).

Like Robert says, cout is not in std.stdio. (maybe you were thinking of stdout?)

And right in your counter-argument, you specify how Java and C# use namespaces to clarify common words (*System*.out and *Console*.Out).

These only work because they must be identified by the fully qualified name.

-Steve
March 07, 2012
On Tue, 06 Mar 2012 14:39:28 -0500, Jose Armando Garcia <jsancio@gmail.com> wrote:

> On Tue, Mar 6, 2012 at 12:25 AM, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
>> On Tuesday, March 06, 2012 09:14:16 so wrote:
>>> On Tuesday, 6 March 2012 at 07:46:14 UTC, Jacob Carlborg wrote:
>>> > On 2012-03-06 02:32, Jonathan M Davis wrote:
>>> >
>>> > The user can then alias "log!info" to "info" if he/she wants to.
>>>
>>> Again, you are now forcing 2 common names instead of one as it is
>>> now.
>>> When you instantiate log!info where do you get "info" from?
>>
>> Yes. My mistake - probably because the time stuff typicall takes such a
>> template argument as string, which would make this log!"info"(msg). However,
>> adding _log_ isn't necessarily bad, given that this is std.log that we're
>> talking about. It's info and the rest that are the problem.
>>
> Seriously everyone. What are we spending some much effort on this?

Because naming is important.  It's very difficult to change names once something is released.  I seriously could care less about implementation (didn't even look at it).  That can be fixed.  Naming can't.

> What is wrong with:
>
> import log = std.log;
> log.info("cool");

What is wrong with

import std.log;
log.info("cool");

-Steve
March 07, 2012
On Tue, 06 Mar 2012 19:59:09 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 3/6/12 4:31 PM, Geoffrey Biggs wrote:
>> On 07/03/12 09:25, Jonathan M Davis wrote:
>>> On Tuesday, March 06, 2012 13:08:42 Brad Roberts wrote:
>>>> On Tue, 6 Mar 2012, Jose Armando Garcia wrote:
>>>>> Fix now:
>>>>>
>>>>> 1. Add thread name attribute to the default logger
>>>>> 2. Check that the example compile
>>>>> 3. Come up with a better name for Rich and rich template
>>>>> 4. Add @safe pure nothrow const to as many methods as possible
>>>>> 5. Remove check when setting Configuration.logger
>>>>
>>>> I still believe pretty strongly that the logger must not affect
>>>> application flow, ie, not throw or exit the app. From the feed back,
>>>> I am
>>>> not alone in thinking that. I don't believe that "well, don't use those
>>>> log levels" is a workaround if for no other reason that there will be
>>>> libraries that contain them and that becomes a "dont use those libraries
>>>> either" response.
>>>
>>> Agreed. The logging functions should _not_ throw.
>>
>> +1.
>>
>> Is std.log a logging library or a complete error management library?
>>
>> If it's a logging library, the last thing it should be doing is
>> affecting program flow. In the case of fatal, in particular, the program
>> may need to do something else after logging the fatal error before
>> terminating. You could argue that the program should do that before
>> logging the fatal error, but I don't think that's nice semantics.
>>
>> On the other hand, if it's a complete error management library, it
>> probably shouldn't be called std.log.
>
> I don't see why the agitation around this particular matter. It's a matter of convenience, much like writeln (as opposed to just write). Let's admit that it often happens that you want to log some troublesome stuff just before throwing an exception with essentially the same message, so the thing is shown on the screen and also persisted in the log. Without a critical level, the pattern would be:
>
> string message = stuff() + ": " + moreStuff();
> log.error(message);
> throw new Exception(message);
>
> It's nice to encapsulate this frequent pattern, hence:
>
> log.critical(stuff() + ": " + moreStuff());
>
> If you want to log but not throw, use log.error. I don't think the response "dont use those libraries either" is meaningful.

I think access to the fatal/critical logging level should not be coupled with throwing exceptions.  I have no problem with having critical or fatal convenience functions that throw and log, but it should not be a *requirement*, and it should not be the default.

I'd combine these two levels into one (there is no semantic difference except one throws an exception and one throws an error), and if I got my way, log.critical(...) would log a message and logAndThrow!(E = Exception)(...) (name to be determined) would log a critical message and throw the given exception/error with the same message.

-Steve
March 07, 2012
On Tue, 06 Mar 2012 23:10:58 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 3/6/12 7:43 PM, Jonathan M Davis wrote:
>> Because the level that you log something at and what you want to do in terms
>> of exceptions aren't necessarily related at all. It could easily be that you
>> want to log something and then do some series of operations before throwing -
>> even if the log level is the most severe level, and you intend to throw an
>> Error to kill the program. And as others have pointed out, you might want to
>> log a series of messages. Having std.log throw on the first one makes it so
>> that you can't log any others.
>
> Then I guess you'd be well advised to use the function that "logs to the error log", not the one that "logs to the error log and then throws". No? I mean you're explaining how a screwdriver is not appropriate for pounding nails.

As has been repeatedly pointed out, the fatal and critical levels are different levels than the error level.  Also, they cannot be turned off.  Access to those levels should not be coupled with throwing an exception.  Convenience functions to log at those levels and then throw an exception are perfectly acceptable.  They just shouldn't be the only/default mechanism.

A screwdriver is not appropriate for driving nails, but neither is an explosive-tipped hammer.

-Steve
March 07, 2012
On Tue, 06 Mar 2012 22:54:39 -0500, James Miller <james@aatch.net> wrote:

> Surprisingly, I agree with the idea that fatal and critical shouldn't
> throw, or at least shouldn't throw by default, maybe a configuration
> option would allow for that functionality. Logging probably shouldn't
> affect program flow.
>
> Its possible that I may need to log a "critical" error, then do some
> graceful shutdown.

I see this pattern emerging:

try
{
   critical("Connection aborted!");
}
catch(LoggingException e)
{
}

// do graceful shutdown
...

throw SomeError("Connection aborted!");

-Steve