March 07, 2012
On Tue, Mar 6, 2012 at 6:05 PM, Geoffrey Biggs <geoffrey.biggs@aist.go.jp> wrote:
> On 07/03/12 09:59, Andrei Alexandrescu wrote:
>>
>> 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.
>
>
> That approach means that if I actually do have a fatal error, I can't mark it as such. It gets conflated with the non-fatal errors, both in the source and in the logging output (which may, for example, be filtering for fatal errors to phone someone in the middle of the night while errors just send an email). Another point worth considering is that I cannot use the fatal level and be able to re-compile my software with logging disabled, because that will change the program flow.
>
Fatal and Critical severity cannot be disabled either at runtime or at compile. In other words fatal log messages will always assert(true) and critical log messages will always throw.

> Forcing the two semantic concepts (logging and error handling) together needlessly restricts the ways the library can be used. It is nice to encapsulate frequent patterns, but providing a convenience function that throws an error, or a new level ("terminate" or something) is a better approach than forcing that pattern on everyone. Based on the name, it's a *logging* library, not an error handling library.
>
Fatal and critical are these convenience functions.

> Geoff
March 07, 2012
On Tue, Mar 6, 2012 at 5:07 PM, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> On Tuesday, March 06, 2012 16:59:09 Andrei Alexandrescu wrote:
>> 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 that it would be far more meaningful to have a logging function which you pass the exception to throw.
This exactly how I am thinking of extending critical. I don't have an API yet but the intent is that you can pass an exception to critical and the module will throw it for you. Very similar to how enforce works.

> That way, you can throw whatever is
> appropriate for your program, not have std.log throw a logging exception of
> some kind. Such a function would be a convenience function where the
> programmer is explicitly saying that they want to log and then throw rather
> than having logging functions throw as a matter of course if the logging level
> is critical enough. What if you want to log such a message _without_ throwing?
>
I think we are going around in circles. If you don't want to abort or throw then use error.

> As it stands, std.log is conflating two separate concepts - logging and and error handling. Providing a convenience function to make that easier is fine, but making it so that the normal logging functions deal with error handling is not.
>
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.

Thanks,
-Jose
> - Jonathan M Davis
March 07, 2012
On 07/03/12 11:12, Jose Armando Garcia wrote:
> On Tue, Mar 6, 2012 at 6:05 PM, Geoffrey Biggs
> <geoffrey.biggs@aist.go.jp>  wrote:
>> That approach means that if I actually do have a fatal error, I can't mark
>> it as such. It gets conflated with the non-fatal errors, both in the source
>> and in the logging output (which may, for example, be filtering for fatal
>> errors to phone someone in the middle of the night while errors just send an
>> email). Another point worth considering is that I cannot use the fatal level
>> and be able to re-compile my software with logging disabled, because that
>> will change the program flow.
>>
> Fatal and Critical severity cannot be disabled either at runtime or at
> compile. In other words fatal log messages will always assert(true)
> and critical log messages will always throw.
>
>> Forcing the two semantic concepts (logging and error handling) together
>> needlessly restricts the ways the library can be used. It is nice to
>> encapsulate frequent patterns, but providing a convenience function that
>> throws an error, or a new level ("terminate" or something) is a better
>> approach than forcing that pattern on everyone. Based on the name, it's a
>> *logging* library, not an error handling library.
>>
> Fatal and critical are these convenience functions.

OK, I took another look at the docs. I see your point now. Sorry for making a fuss.

However, I think that in this case, the log levels usable without killing the program are too coarse. This may be a matter of taste, though.

Geoff
March 07, 2012
On Tue, 06 Mar 2012 17:20:29 -0600, Jose Armando Garcia <jsancio@gmail.com> wrote:
> On Tue, Mar 6, 2012 at 2:03 PM, Robert Jacques <sandford@jhu.edu> wrote:
>> On Tue, 06 Mar 2012 13:41:32 -0600, Jose Armando Garcia <jsancio@gmail.com>
>> wrote:
>>>
>>> On Tue, Mar 6, 2012 at 10:11 AM, Robert Jacques <sandford@jhu.edu> wrote:
>>>>
>>>> On Tue, 06 Mar 2012 11:44:13 -0600, Jose Armando Garcia
>>>> <jsancio@gmail.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> On Tue, Mar 6, 2012 at 9:32 AM, Robert Jacques <sandford@jhu.edu> wrote:
>>>>>>
>>>>>>
>>>>>> On Tue, 06 Mar 2012 11:01:19 -0600, Jose Armando Garcia
>>>>>> <jsancio@gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> On Wed, Feb 29, 2012 at 4:13 PM, Richard van Scheijen
>>>>>>> <dlang@mesadu.net>
>>>>>>> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> When logging the severity level should convey a certain insight that
>>>>>>>> the
>>>>>>>> developer has about the code. This can be done with a 3 bit field.
>>>>>>>> These
>>>>>>>> are: known-cause, known-effect and breaks-flow.
>>>>>>>>
>>>>>>>> This creates the following matrix:
>>>>>>>>
>>>>>>>> KC KE BF Severity
>>>>>>>> =================
>>>>>>>> 1  1  0  Trace
>>>>>>>> 0  1  0  Info
>>>>>>>> 1  0  0  Notice
>>>>>>>> 0  0  0  Warning
>>>>>>>> 1  1  1  Error
>>>>>>>> 0  1  1  Critical
>>>>>>>> 1  0  1  Severe
>>>>>>>> 0  0  1  Fatal
>>>>>>>>
>>>>>>>> A known cause is when the developer knows why a log event is made.
>>>>>>>> e.g.:
>>>>>>>> if
>>>>>>>> you cannot open a file, you do not know why.
>>>>>>>> A known effect is when he/she knows what happens after. Basically,
>>>>>>>> you
>>>>>>>> can
>>>>>>>> tell if it is a catch-all by this flag.
>>>>>>>>
>>>>>>>> When a severity should only be handled by a debugger, the normal
>>>>>>>> debug
>>>>>>>> statement should be used. This is in essence a 4th bit.
>>>>>>>>
>>>>>>>> I hope this helpful in the search for a good level system.
>>>>>>>>
>>>>>>>
>>>>>>> Interesting observation on logging. I like your theoretical
>>>>>>> observation and explanation. To me the most important thing is
>>>>>>> usability and unfortunately people are used to log levels as a order
>>>>>>> concept. Meaning error is higher severity than info so if I am logging
>>>>>>> info events I should probably also log error events.
>>>>>>>
>>>>>>> If we go with a mechanism like the one you describe above there is no
>>>>>>> order so the configuration is a little more complicated or verbose I
>>>>>>> should say. Instead of saying we should log everything "greater" than
>>>>>>> warning the user needs to say that they want to log known-cause,
>>>>>>> known-effect, breaks-flow events. This mean that there are 27 (= 3^3)
>>>>>>> configuration combinations. To implement this we need 3 configuration
>>>>>>> nobs with 3 values (on, off, both).
>>>>>>>
>>>>>>> Thoughts?
>>>>>>> -Jose
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> There are only 8 possible configurations and they are nicely ordered in
>>>>>> terms of severity. So I don't see this as a problem. Also, if you went
>>>>>> with
>>>>>> a combinatorial approach, shouldn't it be 2^8 = 256, not 3^3 = 27
>>>>>> values?
>>>>>
>>>>>
>>>>>
>>>>> Yes. If you want to enable and disable each individual "level" then
>>>>> you need 8 configuration options which leads to 2^8.
>>>>>
>>>>> I suggested 3^3 as a more reasonable options that matches how the
>>>>> developer is logging but doesn't give you as much expressiveness as
>>>>> the 2^8 option.
>>>>
>>>>
>>>>
>>>> In practice, all you'd need to take is a flag with the desired levels.
>>>> i.e.
>>>>
>>>> // Automatically set logging levels using the standard severity ordering
>>>> config.minSeverity(Severity.Warning);
>>>>
>>>> // Manually set the logging levels
>>>> config.setSeverities(Severity.Warning|
>>>>                    Severity.Error|
>>>>                    Severity.Critical|
>>>>                    Severity.Severe|
>>>>                    Severity.Fatal);
>>>>
>>>> I don't see the problem with including both methods and a large advantage
>>>> to
>>>> having a standardized severity framework.
>>>
>>>
>>> Interesting. If you find this useful, I think we can add this in a
>>> future release as it shouldn't break existing modules that maybe using
>>> the library.
>>
>>
>> This began as a discussion regarding Richard's organization of logging
>> severity. That organization isn't something that can be trivially included
>> at a later date.
>
> Please be explicit in what you are requesting... If you have an API even better.
>

Richard proposed an organizational framework for logging severity, which many people including myself and yourself found valuable. In another thread I proposed a logical extension of this framework to make the logging call self documenting. You criticized both as having being overly complex with regard to setting the runtime severity level, to which I proposed a reasonable counter argument. Your response to the counter argument is to suggest making one aspect of it a possible future extension. ?? This seemed to ignore Richard's original suggestion and the extreme value of having a set of well-documented and standardized logging guidelines so that multiple code monkeys / projects 'play well' together. Better yet, by specifying those levels in terms of their conceptual components at the call site, code reviewers can instantly see the nature of what is being logged, without having to remember what each of those log levels mean. And not having to remember the classifications reduces the radius of comprehension needed to use / review the library (http://pragprog.com/magazines/2010-04/tangled-up-in-tools).

P.S.
You could also set config options using method chaining / fluent interfaces:

config.logWarnings.logErrors.logCriticals.logSevers.logFatals;
March 07, 2012
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. 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).

Adding an extra function which logs and then throws the exception that it's given is fine, but that should have _nothing_ to do with the log level. Asserting or throwing unconditionally like std.log does now is completely unacceptable IMHO. Logging should _not_ affect program flow.

- Jonathan M Davis
March 07, 2012
Sorry. For some reason I thought info was callable directly without the leading log portion.

On Mar 6, 2012, at 1:37 PM, Jose Armando Garcia <jsancio@gmail.com> wrote:

> On Tue, Mar 6, 2012 at 12:43 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>> On Mar 6, 2012, at 11:39 AM, Jose Armando Garcia wrote:
>> 
>>> Seriously everyone. What are we spending some much effort on this? What is wrong with:
>>> 
>>> import log = std.log;
>>> log.info("cool");
>> 
>> Why should the default be unqualified names?  Is this simply a desire to not change std.log so we can just get it in already?
> 
> What are you proposing?
> 
> struct Log {
>  static alias log!Severity.info info;
>  ...
> }
> 
> I am not exactly sure when this idiom became popular. I don't know if this is an C++ idiom or a Java idiom but I do know that it is a broken hack. C++ developers use it because the name-spacing facility is limiting. Java uses it because everything is a class and they don't have the concept of compile time objects. We don't need this hack in D. D's module mechanism make this C++/Java idiom unnecessary.
> 
> 
> Thanks,
> -Jose
March 07, 2012
On Tuesday, March 06, 2012 19:10:14 Sean Kelly wrote:
> Sorry. For some reason I thought info was callable directly without the leading log portion.

It _is_ callable that way. Just look at the example at the top:

http://jsancio.github.com/phobos/phobos/std_log.html

- Jonathan M Davis
March 07, 2012
On 3/6/12 5:07 PM, Jonathan M Davis wrote:
> As it stands, std.log is conflating two separate concepts - logging and and
> error handling.

Throwing an exception is not error handling. If it were, so many entities would do so, the whole concept would become devoid of meaning.

Andrei


March 07, 2012
On 3/6/12 6:05 PM, Geoffrey Biggs wrote:
> That approach means that if I actually do have a fatal error, I can't
> mark it as such.

Use log.fatal for those.

Andrei
March 07, 2012
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?

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

> Adding an extra function which logs and then throws the exception that it's
> given is fine, but that should have _nothing_ to do with the log level.

Why?

> Asserting or throwing unconditionally like std.log does now is completely
> unacceptable IMHO.

Why?

> Logging should _not_ affect program flow.

Why?

Once again, the fallacy police finds you in violation of asserting the hypothesis as its own sustaining argument.


Andrei