March 06, 2012
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.
March 06, 2012
On 3/7/12, Jose Armando Garcia <jsancio@gmail.com> wrote:
> http://jsancio.github.com/phobos/phobos/std_log.html#fileNamePrefixes

That was what I was looking for but I didn't find it at first. I think that info about what the defaults are ([program].[hostname].[user].[severity].log.[datetime].[pid]) might be useful somewhere at the top. But that's just my 2 cents..
March 06, 2012
On Tue, Mar 6, 2012 at 3:27 PM, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
> On 3/7/12, Jose Armando Garcia <jsancio@gmail.com> wrote:
>> http://jsancio.github.com/phobos/phobos/std_log.html#fileNamePrefixes
>
> That was what I was looking for but I didn't find it at first. I think that info about what the defaults are ([program].[hostname].[user].[severity].log.[datetime].[pid]) might be useful somewhere at the top. But that's just my 2 cents..

Yeah. I am not terribly happy how ddoc and dlang.org organizes the documentation. I have been thinking of duplicating a lot of the documentation to the top but I have two main objections that are holding back. 1. document duplication and 2. overloading the user with information. I would like the top of the document with enough information and example for the regular user to just start logging. They can later come back and read the whole thing when they want to configure and tweak things. Thoughts?

Thanks,
-Jose
March 07, 2012
On Tuesday, March 06, 2012 13:08:42 Brad Roberts wrote:
> On Tue, 6 Mar 2012, Jose Armando Garcia wrote:
> > Future:
> > 
> > 1. Allowing filtering of regular log messages (like info, warning, etc) based on the module. Similar to how vlog works.
> 
> Once you add module filtering to the regularly log messages, what's the point of having the separate vlog? I think this needs to be figured out before merge, not at some vague future.
> 
> > 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.

- Jonathan M Davis
March 07, 2012
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.

That's my opinion as an uninformed user.

Geoff
March 07, 2012
On Tue, Mar 06, 2012 at 03:52:56PM -0800, Jose Armando Garcia wrote:
> On Tue, Mar 6, 2012 at 3:27 PM, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
> > On 3/7/12, Jose Armando Garcia <jsancio@gmail.com> wrote:
> >> http://jsancio.github.com/phobos/phobos/std_log.html#fileNamePrefixes
> >
> > That was what I was looking for but I didn't find it at first. I think that info about what the defaults are ([program].[hostname].[user].[severity].log.[datetime].[pid]) might be useful somewhere at the top. But that's just my 2 cents..
> 
> Yeah. I am not terribly happy how ddoc and dlang.org organizes the documentation. I have been thinking of duplicating a lot of the documentation to the top but I have two main objections that are holding back. 1. document duplication and 2. overloading the user with information. I would like the top of the document with enough information and example for the regular user to just start logging. They can later come back and read the whole thing when they want to configure and tweak things. Thoughts?
[...]

I don't like the current state of dlang.org docs either. There is little or no intro paragraph to explain what on earth the module is used for or why you should bother reading the rest of the page for the next hour or so. It also doesn't give any motivating examples (I'm thinking of std.range here) why this module is useful.

I think a good intro is a must to good documentation. Include some code snippets to show typical usage of the module. How to change common settings. Some explanation of why the user might find the module helpful. It's OK to duplicate some docs here, within reason. It should also be concise without being uninformative.

For example (from std.range):

	This module defines the notion of range (by the membership tests
	isInputRange, isForwardRange, isBidirectionalRange,
	isRandomAccessRange), range capability tests (such as hasLength
	or hasSlicing), and a few useful range incarnations.

is concise, but not very informative. Why should the user care what a range is anyway? No explanation is given. Something like this may be a beginning to better documentation:

	This module defines the notion of a range. Ranges generalize the
	concept of arrays, lists, or anything that involves sequential
	access. This abstraction enables the same set of algorithms (see
	std.algorithm) to be used with a vast variety of different
	concrete types. For example, a linear search algorithm such as
	std.find works not just for arrays, but for linked-lists, input
	files, incoming network data, etc..

	This module defines several templates (<!--insert list
	here-->)for testing whether a given object is a range, and what
	kind of range it is.

	It also lets you construct new ranges out of existing ranges.
	For example, retro lets you access a bidirectional range in
	reverse, cycle creates a range that is an endless repetition of
	the original range. ...

	...

Basically, you're writing an overview to the module, so highlight its main components, give some insight into why it's useful, etc., so that the user can make sense of the long list of declarations that follow.

As it stands, std.range's page consists of a giant list of range-related declarations that gives no hint to the user as to how they all fit together. You basically have to wade through it until it somehow all "clicks" together. That is poor documentation. The overview should give some general categories of stuff that's found in the module (e.g. range tests, constructing new ranges, etc., as I've tried to do above in my one-shot attempt to improve std.range's docs). Include some examples of really clever stuff that you can do with the help of the module. Such examples are usually a very good way to get the user up-to-speed with what the module has to offer.


T

-- 
What doesn't kill me makes me stranger.
March 07, 2012
On Tue, Mar 06, 2012 at 07:25:23PM -0500, Jonathan M Davis wrote:
> On Tuesday, March 06, 2012 13:08:42 Brad Roberts wrote:
[...]
> > 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. Logging functions are for ... logging? Not for managing errors. Leave it up to the user to define something like the following, if they're so inclined:

	void logAndAbort(T...)(string fmt, T args) {
		critical(fmt.format(args));
		throw new CriticalError(...);
	}

One shouldn't have to write a try/catch block just so one can log a critical message and continue doing whatever it is needs to be done. (For example, you might want to log a critical condition immediately to ensure the sysadmin sees the message, before proceeding to attempt self-recovery that *may* fail and crash the system, and thereby lose the ability to log a message.) Let the user decide whether or not to throw.


T

-- 
Why have vacation when you can work?? -- EC
March 07, 2012
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.


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

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.

- Jonathan M Davis
March 07, 2012
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.

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.

Geoff