October 15, 2013
On 10/15/2013 09:32 AM, Sönke Ludwig wrote:
> Am 15.10.2013 09:08, schrieb Jacob Carlborg:
>> On 2013-10-14 23:22, Dicebot wrote:
>>
>>> If we need to care about that, D module system is a failure. But I don't think it is a valid concern.
>>
>> People already complain about conflict function names in Phobos.
>>
>
> And I'd agree with them. At least inside of a library, care IMO should
> be taken to minimize overlap (of course functionally equivalent ones
> in different overload sets are fine, though). But in case of "logXXX"
> this seems to be very unlikely, much in contrast to "log" (std.math.log).
yes and no. Of course does logXXX create less conflict, but I like to simply write log and don't care about the LogLevel. So again pros and cons
October 15, 2013
On Monday, 14 October 2013 at 18:29:09 UTC, ilya-stromberg wrote:
> On Monday, 14 October 2013 at 18:00:12 UTC, Robert Schadek wrote:
>
> If you disagree, please tell why.

If you want a logger with a particular feature, this module will allow to create a custom logger.
It would be a mistake to include something that specific.

eg: - we can create new RNG on top of std.random, which follow the same interface
    - we can use any such std.random-compatible RNG to feed random distributions algorithms


October 15, 2013
What are the philosophy behind errors vs fatal errors vs critical errors?
When should we use each of these?
October 15, 2013
On 10/15/2013 09:40 AM, Sönke Ludwig wrote:
> Am 14.10.2013 20:24, schrieb Robert Schadek:
>> On 10/14/2013 04:44 PM, Sönke Ludwig wrote:
>>> The same could be said about the global "log" functions, which are tightly coupled to that state. I think this is already nicely grouped together by the logger module itself, since there is not much else in it.
>>>
>>> Basically, I just wouldn't consider this style to be particularly
>>> idiomatic D code, but of course that's just personal
>>> perception/preference (there is also some precedence using "struct"
>>> instead of "class" in Druntime). However, if it ends up like this in
>>> the
>>> final version, it should get a "@disable this();" to prevent misuse.
>> It is for ment for phobos not druntime. Anyway structs would mean all templates and people will scream template bloat. And this would break the design, which I find to be a valid use of classes and polymorphisms.  The StdIOLogger can have its default constructor called IMO.
>
> No no, I was talking about the JobManager, not the Logger classes. No templates involved.
Than I'm not sure what you're referring to.

> Maybe I miscommunicated what I want to show by that example. The (d|D)
>> part is the rename to enum lower case.
>> The debug log level is given through the LogLevel.Debug, which will be
>> renamed to LogLevel.debug. I would call the developer the user of the
>> logger. Maybe log messages can be communicated to the user of the
>> applicaiton and the developer of the application through a MultiLogger
>> class.
>
> But the statement of mine that you quoted was about debug levels (the case issue is clear)... Also right now there is no "(D|d)ebug" level, so I'm actually not sure about the statement that you want to make. But my example of having different levels for the application user and the developer is mostly important when the application user enables verbose log output to see where things go wrong. In that case things like system error codes and the like would make sense, but a repeated printout of some kind of internal buffer state would hardly help the user - it could, however, help the developer.
>
maybe something like:
auto devLogger = new StdIOLogger(LogLevel.info);
auto appLogger = new FencySelfWrittenGuiLogger(LogLevel.Warning);
auto multiLogger = new MultiLogger(devLogger, appLogger);

multiLogger.log("...");

otherwise, I think I don't follow you

October 15, 2013
On 10/15/2013 09:44 AM, ilya-stromberg wrote:
> On Monday, 14 October 2013 at 12:48:14 UTC, Martin Drasar wrote:
>> 1) MultiLogger class that takes references to other loggers and just forwards the call to the log function.
>
> +1
> Also, we should support a few loggers whith same type. For example, I
> can use 2 file loggers: the 1-st only for debug messages and the 2-nd
> for all other messages. It can help for message sorting.
>
> Also, we should support a reserve loggers. For example, I can use a file logger as a default and a syslog as a reserve logger (it will be used if the file logger fails). It increases logger reliability.
>
> Also, it will be perfect to have logger failed notifications. For example, the file logger failed can indicate that we haven't got free disc space, a file system problems or hard disk problems. So, we should inform admin about this problems. We can do it via stderr (for local computer only), via syslog network logger or via e-mail logger.
I think File will throw anyway. What if stderr is piped to file? Again, I don't think you can please everybody's needs. So we should not try, but rather provide the tools to help yourself.
October 15, 2013
On 10/15/2013 10:49 AM, ponce wrote:
> What are the philosophy behind errors vs fatal errors vs critical errors? When should we use each of these?
fatal = the application is going down, I'm just letting you know
critical = the application is maybe going down, I'm not sure yet, but
this is a problem
error = something went wrong, I'm sure if this is problem
October 15, 2013
On Tuesday, 15 October 2013 at 07:33:15 UTC, Sönke Ludwig wrote:
> Am 15.10.2013 09:08, schrieb Jacob Carlborg:
>> On 2013-10-14 23:22, Dicebot wrote:
>>
>>> If we need to care about that, D module system is a failure.
>>> But I don't think it is a valid concern.
>>
>> People already complain about conflict function names in Phobos.
>>
>
> And I'd agree with them. At least inside of a library, care IMO should be taken to minimize overlap (of course functionally equivalent ones in different overload sets are fine, though). But in case of "logXXX" this seems to be very unlikely, much in contrast to "log" (std.math.log).

I disagree. People complain because they try to use imports in a straightforward way, similar to includes. That should be discouraged as a bad style in D. Imports should always be either with explicit mention of exported symbol or aliased static imports. And global module imports should be discouraged too.
October 15, 2013
Am 15.10.2013 10:54, schrieb Robert Schadek:
> On 10/15/2013 09:40 AM, Sönke Ludwig wrote:
>> Am 14.10.2013 20:24, schrieb Robert Schadek:
>>> On 10/14/2013 04:44 PM, Sönke Ludwig wrote:
>>>> The same could be said about the global "log" functions, which are
>>>> tightly coupled to that state. I think this is already nicely grouped
>>>> together by the logger module itself, since there is not much else
>>>> in it.
>>>>
>>>> Basically, I just wouldn't consider this style to be particularly
>>>> idiomatic D code, but of course that's just personal
>>>> perception/preference (there is also some precedence using "struct"
>>>> instead of "class" in Druntime). However, if it ends up like this in
>>>> the
>>>> final version, it should get a "@disable this();" to prevent misuse.
>>> It is for ment for phobos not druntime. Anyway structs would mean all
>>> templates and people will scream template bloat. And this would break
>>> the design, which I find to be a valid use of classes and
>>> polymorphisms.  The StdIOLogger can have its default constructor
>>> called IMO.
>>
>> No no, I was talking about the JobManager, not the Logger classes. No
>> templates involved.
> Than I'm not sure what you're referring to.
>

What I meant is just that in Druntime there is something like this:

struct LogManager {
  static void somefunc();
}

instead of

class LogManager {
  static void someFunc();
}

In any case, such a struct/class should also have a member "@disable this();" so that it cannot be uselessly instantiated.

>> Maybe I miscommunicated what I want to show by that example. The (d|D)
>>> part is the rename to enum lower case.
>>> The debug log level is given through the LogLevel.Debug, which will be
>>> renamed to LogLevel.debug. I would call the developer the user of the
>>> logger. Maybe log messages can be communicated to the user of the
>>> applicaiton and the developer of the application through a MultiLogger
>>> class.
>>
>> But the statement of mine that you quoted was about debug levels (the
>> case issue is clear)... Also right now there is no "(D|d)ebug" level,
>> so I'm actually not sure about the statement that you want to make.
>> But my example of having different levels for the application user and
>> the developer is mostly important when the application user enables
>> verbose log output to see where things go wrong. In that case things
>> like system error codes and the like would make sense, but a repeated
>> printout of some kind of internal buffer state would hardly help the
>> user - it could, however, help the developer.
>>
> maybe something like:
> auto devLogger = new StdIOLogger(LogLevel.info);
> auto appLogger = new FencySelfWrittenGuiLogger(LogLevel.Warning);
> auto multiLogger = new MultiLogger(devLogger, appLogger);
>
> multiLogger.log("...");
>
> otherwise, I think I don't follow you
>

The log messages are supposed to go to the same destination, just filtered by log level. A totally artificial example:

---
void main(string[] args)
{
    logDiagnostic("Application called as %s", args[0]);
    ubyte[] contents;
    try {
        logTrace("Going to read file");
        contents = read("somefile.dat");
        logTrace("Done reading file");
    } catch (Exception e) {
        logError("Failed to read input file.");
        logDiagnostic("Reported error: %s", e.msg);
        logDebug("Full exception: %s", e.toString());
	return 1;
    }
    logInfo("Input file is %d bytes", contents.length);
    logTrace("Computing sum");
    auto sum = sum(contents);
    logTrace("Removing file");
    remove("somefile.dat");
}

ulong sum(ubyte[] arr)
{
	ulong ret = 0;
	foreach (b; arr) {
		logDebugV("Adding %d", b);
		ret += b;
	}
	logDebugV("Sum result: %d", b);
	return ret;
}
---

A typical mode in my projects now is to output any level starting with "info" to the console by default, but allow to log "diagnostic" messages using "-v" (useful to the end user) and lower levels using other switches (useful for me to diagnose bugs).

At the same time there may be a log file or a network based remote logger that captures all levels regardless of command line switches, but needs to be able to filter based on the actual log level later in a GUI. Having multiple loggers for the different diagnostic/debug/trace levels would not really help there (except with some extensive hacking).

BTW you stated that there is a "debug" level in your implementation, but neither the docs, nor the pull request have it.
October 15, 2013
On 10/15/2013 02:54 PM, Sönke Ludwig wrote:
> What I meant is just that in Druntime there is something like this:
>
> struct LogManager {
>   static void somefunc();
> }
>
> instead of
>
> class LogManager {
>   static void someFunc();
> }
>
> In any case, such a struct/class should also have a member "@disable this();" so that it cannot be uselessly instantiated.
I see what you mean, good point.
>
> The log messages are supposed to go to the same destination, just filtered by log level. A totally artificial example:
>
> ---
> void main(string[] args)
> {
>     logDiagnostic("Application called as %s", args[0]);
>     ubyte[] contents;
>     try {
>         logTrace("Going to read file");
>         contents = read("somefile.dat");
>         logTrace("Done reading file");
>     } catch (Exception e) {
>         logError("Failed to read input file.");
>         logDiagnostic("Reported error: %s", e.msg);
>         logDebug("Full exception: %s", e.toString());
>     return 1;
>     }
>     logInfo("Input file is %d bytes", contents.length);
>     logTrace("Computing sum");
>     auto sum = sum(contents);
>     logTrace("Removing file");
>     remove("somefile.dat");
> }
>
> ulong sum(ubyte[] arr)
> {
>     ulong ret = 0;
>     foreach (b; arr) {
>         logDebugV("Adding %d", b);
>         ret += b;
>     }
>     logDebugV("Sum result: %d", b);
>     return ret;
> }
> ---
Ok, you can achieve this by assigning a LogLevel to the defaultLogger. Than all message with a LogLevel >= to the assigned LogLevel will be logged. Or do you want to, for example, filter all message with critical and debug LogLevel, but don't display the messages with LogLevels in between?
>
> BTW you stated that there is a "debug" level in your implementation, but neither the docs, nor the pull request have it.
You're right I was reading Debug and thought Info, my bad!
October 15, 2013
Am 15.10.2013 10:41, schrieb Robert Schadek:
> On 10/15/2013 02:44 AM, Kapps wrote:
>> The simple act of logging a message is very verbose right now:
>> log(LogLevel.trace, "Creating new pool") is a lot of boiler plate. I'd
>> prefer something like log.trace("Creating new pool") and log("Creating
>> new pool") where the latter would use opCall to forward to the default
>> log level. If it's intentional that you can assign the result of log,
>> this also helps that because log = new StdIOLogger would be possible
>> (log being a property that returns a Logger, and so a setter could be
>> made), but log("Creating new pool") = new StdIOLogger() would not be.
> The LogLevel is optional. And always writing log.trace might become more
> typing work and assigning a LogLevel and than calling log("..."). Both
> have pros and cons

What happens when a called function alters the default log level?

---
void func1() {
	log.logLevel = LogLevel.debug;
	log("This is a debug message");
	func2();
	log("This is supposed to be a debug message");
}

void func2() {
	log.logLevel = LogLevel.warning;
	log("This is a warning");
}
---


I don't think it's a good idea to use such kind of global state, especially for a logging framework that is supposed to be shared between libraries, so that it is difficult to predict what a particular function does. With a logger that is shared between threads, things get worse of course.

A related question: It seems like Logger.logLevel at the same time means the minimum log level that is output and the default log level, is that right?