www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.logger

reply Robert Schadek <realburner gmx.de> writes:
I'm still missing a logging facility in D and as the last attempt seam
to have stopped I want to throw
in my version. After reading through the std.log thread I made my
conclusions and created my own
logger. People seamed to be unhappy with the naming and the way of
configuration.
Additionally when to throw or not to throw seamed to be an argument.

My attempt is to provide a very small functional interface to logging.
IMO it is impossible to fulfill
all requirements a D developer can have through configuration classes
and such, I designed the
a abstract Logger class that can be easily implemented to one's own needs.

As a quick start feature I created a Stdio- and File-Logger. If no
Logger is provided to the log
function a defaultLogger will be used.

Docu:
http://burner.github.io/phobos/phobos-prerelease/std_logger.html

Pull Request:
https://github.com/D-Programming-Language/phobos/pull/1500


I hope this will lead to some progress in phobos, when it comes to
message logging.
Aug 22 2013
next sibling parent reply Johannes Pfau <nospam example.com> writes:
Am Thu, 22 Aug 2013 16:00:06 +0200
schrieb Robert Schadek <realburner gmx.de>:

 I'm still missing a logging facility in D and as the last attempt seam
 to have stopped I want to throw
 in my version. After reading through the std.log thread I made my
 conclusions and created my own
 logger. People seamed to be unhappy with the naming and the way of
 configuration.
 Additionally when to throw or not to throw seamed to be an argument.
 
 My attempt is to provide a very small functional interface to logging.
 IMO it is impossible to fulfill
 all requirements a D developer can have through configuration classes
 and such, I designed the
 a abstract Logger class that can be easily implemented to one's own
 needs.
 
 As a quick start feature I created a Stdio- and File-Logger. If no
 Logger is provided to the log
 function a defaultLogger will be used.
 
 Docu:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html
 
 Pull Request:
 https://github.com/D-Programming-Language/phobos/pull/1500
 
 
 I hope this will lead to some progress in phobos, when it comes to
 message logging.
 
It would be nice to finally have logging in phobos. Two small nit-picks: The API doesn't follow the phobos naming conventions: functions (error, fatal, ...) need to start with a lowercase letter. Same for enum members. See http://dlang.org/dstyle.html I guess the function string for writeLogMsg is fully-qualified? I'd like to see module and function split into two parts. Or fully-qualified function + module so the fully qualified function name can be sliced to strip the module name: auto fn = func[module.length .. $]
Aug 22 2013
next sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:06 PM, Johannes Pfau wrote:
 It would be nice to finally have logging in phobos.
 Two small nit-picks:

 The API doesn't follow the phobos naming conventions: functions (error,
 fatal, ...) need to start with a lowercase letter. Same for enum
 members. See http://dlang.org/dstyle.html
I know. I did this because with the last logger, people where complaining that log, info, warning ... where to common names. I'm very open for suggestion here. lowercase or otherwise.
 I guess the function string for writeLogMsg is fully-qualified? I'd
 like to see module and function split into two parts. Or fully-qualified
 function + module so the fully qualified function name can be sliced to
 strip the module name: auto fn = func[module.length .. $]
This is what is done inside of StdIOLogger and FileLogger. I did not want to do this splitting in Logger.logMessage as the user could rewrite that to fit its need. Maybe I can add two static method that return the modulename and the function name.
Aug 22 2013
next sibling parent reply "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Thursday, 22 August 2013 at 15:17:16 UTC, Robert Schadek wrote:
 On 08/22/2013 05:06 PM, Johannes Pfau wrote:
 It would be nice to finally have logging in phobos.
 Two small nit-picks:

 The API doesn't follow the phobos naming conventions: 
 functions (error,
 fatal, ...) need to start with a lowercase letter. Same for 
 enum
 members. See http://dlang.org/dstyle.html
I know. I did this because with the last logger, people where complaining that log, info, warning ... where to common names. I'm very open for suggestion here. lowercase or otherwise.
Would logWarning(), logInfo(), logError() work? You already have a logMessage() in there. It might be a bit redundant in some instances ( Logger.logError() ), but in other cases it be clearer (eg., logError("Oh no! Something went wrong!"). Craig
Aug 22 2013
next sibling parent Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:32 PM, Craig Dillabaugh wrote:
 On Thursday, 22 August 2013 at 15:17:16 UTC, Robert Schadek wrote:
 On 08/22/2013 05:06 PM, Johannes Pfau wrote:
 It would be nice to finally have logging in phobos.
 Two small nit-picks:

 The API doesn't follow the phobos naming conventions: functions (error,
 fatal, ...) need to start with a lowercase letter. Same for enum
 members. See http://dlang.org/dstyle.html
I know. I did this because with the last logger, people where complaining that log, info, warning ... where to common names. I'm very open for suggestion here. lowercase or otherwise.
Would logWarning(), logInfo(), logError() work? You already have a logMessage() in there. It might be a bit redundant in some instances ( Logger.logError() ), but in other cases it be clearer (eg., logError("Oh no! Something went wrong!").
logMessage is meant to do the actual logging. And IMO it is to much to long. But keep the ideas coming there must be a sweet spot somewhere.
Aug 22 2013
prev sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:38 PM, Robert Schadek wrote:
 logMessage is meant to do the actual logging. And IMO it is to much to long.
 But keep the ideas coming there must be a sweet spot somewhere.
sry, logMessage is meant to figure out if the LogLevel of the message makes it worthy to be printed and to disable the logging via version(DisableLogger). writeLogMsg does the actual logging
Aug 22 2013
parent reply "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Thursday, 22 August 2013 at 15:41:40 UTC, Robert Schadek wrote:
 On 08/22/2013 05:38 PM, Robert Schadek wrote:
 logMessage is meant to do the actual logging. And IMO it is to 
 much to long.
 But keep the ideas coming there must be a sweet spot somewhere.
sry, logMessage is meant to figure out if the LogLevel of the message makes it worthy to be printed and to disable the logging via version(DisableLogger). writeLogMsg does the actual logging
Do you really find the three extra characters a big problem. Log() vs. logLog() //OK, that is kind of ugly. Info() vs logInfo() Warning() vs logWarning() Error() vs logError() Its only three extra characters and they are all in the sweet spot on my QWERTY keyboard :o) I guess my concern would be that if you want to use camelCase and start with a lower case letter, there are not a tonne of options.
Aug 22 2013
next sibling parent "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Thursday, 22 August 2013 at 15:51:53 UTC, Craig Dillabaugh
wrote:
 On Thursday, 22 August 2013 at 15:41:40 UTC, Robert Schadek 
 wrote:
 On 08/22/2013 05:38 PM, Robert Schadek wrote:
 logMessage is meant to do the actual logging. And IMO it is 
 to much to long.
 But keep the ideas coming there must be a sweet spot 
 somewhere.
sry, logMessage is meant to figure out if the LogLevel of the message makes it worthy to be printed and to disable the logging via version(DisableLogger). writeLogMsg does the actual logging
Do you really find the three extra characters a big problem. Log() vs. logLog() //OK, that is kind of ugly. Info() vs logInfo() Warning() vs logWarning() Error() vs logError() Its only three extra characters and they are all in the sweet spot on my QWERTY keyboard :o) I guess my concern would be that if you want to use camelCase and start with a lower case letter, there are not a tonne of options.
One other advantage of this naming convention is that when the plain logging fuctions are used: Critical("I don't like your naming convention!"); vs. logCritical("Yeah, well yours isn't any better."); It is a bit more obvious in the 2nd case what is happening (IMO anyway, or imoAnyway).
Aug 22 2013
prev sibling next sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:51 PM, Craig Dillabaugh wrote:
 Do you really find the three extra characters a big problem.

 Log() vs. logLog()  //OK, that is kind of ugly.
 Info() vs logInfo()
 Warning() vs logWarning()
 Error() vs logError()

 Its only three extra characters and they are all in the sweet
 spot on my QWERTY keyboard :o)

 I guess my concern would be that if you want to use camelCase and
 start with a lower case letter, there are not a tonne of options.
You are properly right, but ultimately I would like to write log(), warning(), ... but that did not fly last time. log!Warning() would also be fine but that leads to other problems. Anyway, changing to lower, logLog or something else isn't really a problem IMO when a consensus has been found.
Aug 22 2013
next sibling parent "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Thursday, 22 August 2013 at 16:03:01 UTC, Robert Schadek wrote:
 On 08/22/2013 05:51 PM, Craig Dillabaugh wrote:
 Do you really find the three extra characters a big problem.

 Log() vs. logLog()  //OK, that is kind of ugly.
 Info() vs logInfo()
 Warning() vs logWarning()
 Error() vs logError()

 Its only three extra characters and they are all in the sweet
 spot on my QWERTY keyboard :o)

 I guess my concern would be that if you want to use camelCase 
 and
 start with a lower case letter, there are not a tonne of 
 options.
You are properly right, but ultimately I would like to write log(), warning(), ... but that did not fly last time. log!Warning() would also be fine but that leads to other problems. Anyway, changing to lower, logLog or something else isn't really a problem IMO when a consensus has been found.
It seems like Jacob's suggestion (if you like that) solves both of our problems.
Aug 22 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-22 18:02, Robert Schadek wrote:

 You are properly right, but ultimately I would like to write log(),
 warning(), ... but that did
 not fly last time. log!Warning() would also be fine but that leads to
 other problems.
 Anyway, changing to lower, logLog or something else isn't really a
 problem IMO when
 a consensus has been found.
With my suggestion you can use the with-statement as well if you are performing a lot of logging: with (log) { error("bar"); info("foo"); } -- /Jacob Carlborg
Aug 22 2013
parent Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 06:17 PM, Jacob Carlborg wrote:
 On 2013-08-22 18:02, Robert Schadek wrote:

 With my suggestion you can use the with-statement as well if you are
 performing a lot of logging:

 with (log)
 {
     error("bar");
     info("foo");
 }
yes, I believe you're right, but I have not coded/tested it yet
Aug 22 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Thursday, 22 August 2013 at 15:51:53 UTC, Craig Dillabaugh 
wrote:
 Do you really find the three extra characters a big problem.
They are unnecessary. If you want to make clear you are dealing with logging, you can just write something along the lines of: import log = std.logger; log.error("123"); David
Aug 22 2013
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, August 22, 2013 23:36:48 David Nadlinger wrote:
 On Thursday, 22 August 2013 at 15:51:53 UTC, Craig Dillabaugh
 
 wrote:
 Do you really find the three extra characters a big problem.
They are unnecessary. If you want to make clear you are dealing with logging, you can just write something along the lines of: import log = std.logger; log.error("123");
I'd oppose warning, critical, error, etc. because they're not verbs like functions are supposed to be. It's variables or properties which are nouns. So, while I agree that the module system makes it so that the name clashes shouldn't be a big deal, I disagree with the names anyway and would still prefer logWarning to warning. - Jonathan M Davis
Aug 22 2013
parent reply "SomeDude" <lovelydear mailmetrash.com> writes:
On Thursday, 22 August 2013 at 22:01:09 UTC, Jonathan M Davis 
wrote:
 On Thursday, August 22, 2013 23:36:48 David Nadlinger wrote:
 On Thursday, 22 August 2013 at 15:51:53 UTC, Craig Dillabaugh
 
 wrote:
 Do you really find the three extra characters a big problem.
They are unnecessary. If you want to make clear you are dealing with logging, you can just write something along the lines of: import log = std.logger; log.error("123");
I'd oppose warning, critical, error, etc. because they're not verbs like functions are supposed to be. It's variables or properties which are nouns. So, while I agree that the module system makes it so that the name clashes shouldn't be a big deal, I disagree with the names anyway and would still prefer logWarning to warning. - Jonathan M Davis
OTOH, they are used in just about every logging API ever devised. Everybody knows what they mean. I just don't see the need to reinvent these words and make them longer by adding log. I seriously hate it. It clutters code (once you've read your thousand's log instruction, it hurts), adds ugly and useless redundancy and nothing else to its understanding. This alone is far worse than not using verbs.
Aug 24 2013
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, August 24, 2013 16:49:24 SomeDude wrote:
 OTOH, they are used in just about every logging API ever devised.
 Everybody knows what they mean. I just don't see the need to
 reinvent these words and make them longer by adding log. I
 seriously hate it.
 
 It clutters code (once you've read your thousand's log
 instruction, it hurts), adds ugly and useless redundancy and
 nothing else to its understanding. This alone is far worse than
 not using verbs.
Well, we're going to have to agree to disagree on that point. Functions are supposed to be verbs. They're only nouns if they're properties, in which case, they're emulating variables, which are nouns. And I'd consider following the proper naming conventions like that to be far more important than saving a few characters. - Jonathan M Davis
Aug 24 2013
prev sibling parent Robert Schadek <realburner gmx.de> writes:
On 08/24/2013 08:01 PM, Jonathan M Davis wrote:
 On Saturday, August 24, 2013 16:49:24 SomeDude wrote:
 OTOH, they are used in just about every logging API ever devised.
 Everybody knows what they mean. I just don't see the need to
 reinvent these words and make them longer by adding log. I
 seriously hate it.

 It clutters code (once you've read your thousand's log
 instruction, it hurts), adds ugly and useless redundancy and
 nothing else to its understanding. This alone is far worse than
 not using verbs.
Well, we're going to have to agree to disagree on that point. Functions are supposed to be verbs. They're only nouns if they're properties, in which case, they're emulating variables, which are nouns. And I'd consider following the proper naming conventions like that to be far more important than saving a few characters. - Jonathan M Davis
luckily all log functions are called log and logf now.
Aug 24 2013
prev sibling parent reply "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Thursday, 22 August 2013 at 21:36:50 UTC, David Nadlinger
wrote:
 On Thursday, 22 August 2013 at 15:51:53 UTC, Craig Dillabaugh 
 wrote:
 Do you really find the three extra characters a big problem.
They are unnecessary. If you want to make clear you are dealing with logging, you can just write something along the lines of: import log = std.logger; log.error("123"); David
I think the two solutions are pretty close in terms of readability. Of course the solution here uses convention to ensure clarity in what the log functions are doing, while logError() enforces clarity. I could do: import px482374203 = std.logger; .... //much later px482374203.error("What the heck is this!"); Not that I would ever do that ... Also just to be nit-picky you are typing 4 extra characters which is 25% worse than my suggestion :o) [ In fairness having to use the shift key to type logError() likely makes log.error() just a bit easier to type.]
Aug 22 2013
parent "BS" <bumnutbarry gmail.com> writes:
I hope this gets up, I've been waiting for a std.logger for a 
while.

My custom one works fine but it's extra sideline code to compile, 
maintain and lug around.

As for naming, I like the following

log.whisper("low priority log message")
log.say("default log level");
log.shout("Warning level");
log.scream("**ERROR**");

:D

To be honest I don't really care about the naming, but if pressed 
I'd go for

log("default")
log.warn("a warning")
log.error("an error")

...or something similar. I just find it easier to read than 
logWarning, logError etc.


/BS
Aug 22 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-22 17:17, Robert Schadek wrote:

 I know. I did this because with the last logger, people where
 complaining that log, info, warning ... where to common names.
 I'm very open for suggestion here. lowercase or otherwise.
My suggestion is you define a single function "log". This returns an instance of the current/default logger. The logger, be it a struct or class, have one method for each logging level. The default logging level would use opCall, resulting in this API: log("asd"); // log with default level log.warning("foo"); // log with warning level log.error("asd"); // And so on. Then you would only have one function at the module level. -- /Jacob Carlborg
Aug 22 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:59 PM, Jacob Carlborg wrote:
 My suggestion is you define a single function "log". This returns an
 instance of the current/default logger. The logger, be it a struct or
 class, have one method for each logging level. The default logging
 level would use opCall, resulting in this API:

 log("asd"); // log with default level
 log.warning("foo"); // log with warning level
 log.error("asd"); //

 And so on. Then you would only have one function at the module level.
I like this part. But you will still need two more module level function to set and get the global LogLevel. And log would have to return by ref to assign a new defaultLogger. But still, I like that very much, even though this is still in conflict with std.math.log.
Aug 22 2013
parent "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Thursday, 22 August 2013 at 17:09:17 UTC, Robert Schadek wrote:
 On 08/22/2013 05:59 PM, Jacob Carlborg wrote:
 But still, I like that very much, even though this is still in 
 conflict
 with std.math.log.
We should avoid name conflicts as much as possible. So, I vote to the Craig Dillabaugh's idea: logLog() //OK, that is kind of ugly. logInfo() logWarning() logError() Note that we can use logDefault(), not logLog(). I think it will be better.
Aug 22 2013
prev sibling parent Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 05:06 PM, Johannes Pfau wrote:

I'm getting wired auto-tester erros

setting up remote topull -> https://github.com/burner/phobos.git

fetching contents of https://github.com/burner/phobos.git
From https://github.com/burner/phobos
* [new branch] 10472 -> topull/10472 * [new branch] 2.061 -> topull/2.061 * [new branch] bigintpure -> topull/bigintpure * [new branch] candidate -> topull/candidate * [new branch] convpure -> topull/convpure * [new branch] deque -> topull/deque * [new branch] devel -> topull/devel * [new branch] gh-pages -> topull/gh-pages * [new branch] indexOfIdx -> topull/indexOfIdx * [new branch] lastIndexOf -> topull/lastIndexOf * [new branch] logger -> topull/logger * [new branch] master -> topull/master * [new branch] phobos-1.x -> topull/phobos-1.x * [new branch] staging -> topull/staging * [new branch] website -> topull/website merging topull/logger error: Your local changes to the following files would be overwritten by merge: win64.mak Please, commit your changes or stash them before you can merge. Aborting Updating e723325..621af63 from the error message I would say somebody did modify the win64.mak on the test machine without committing it?
Sep 07 2013
prev sibling next sibling parent reply "develop32" <develop32 gmail.com> writes:
On Thursday, 22 August 2013 at 14:13:29 UTC, Robert Schadek wrote:
 I'm still missing a logging facility in D and as the last 
 attempt seam
 to have stopped I want to throw
 in my version. After reading through the std.log thread I made 
 my
 conclusions and created my own
 logger. People seamed to be unhappy with the naming and the way 
 of
 configuration.
 Additionally when to throw or not to throw seamed to be an 
 argument.

 My attempt is to provide a very small functional interface to 
 logging.
 IMO it is impossible to fulfill
 all requirements a D developer can have through configuration 
 classes
 and such, I designed the
 a abstract Logger class that can be easily implemented to one's 
 own needs.

 As a quick start feature I created a Stdio- and File-Logger. If 
 no
 Logger is provided to the log
 function a defaultLogger will be used.

 Docu:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html

 Pull Request:
 https://github.com/D-Programming-Language/phobos/pull/1500


 I hope this will lead to some progress in phobos, when it comes 
 to
 message logging.
Why logging functions accept only a string? I would expect it to behave as std.stdio with its variadic parameters. It would be more straightforward to write logging code: log("Moving ", data, " to ", destination); Where 'data' and 'destination' are any variables. I use such setup in my projects and it helps greatly to identify what went wrong when not using a debugger.
Aug 22 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-22 18:01, develop32 wrote:

 Why logging functions accept only a string? I would expect it to behave
 as std.stdio with its variadic parameters.

 It would be more straightforward to write logging code:

 log("Moving ", data, " to ", destination);

 Where 'data' and 'destination' are any variables. I use such setup in my
 projects and it helps greatly to identify what went wrong when not using
 a debugger.
Good point, and formatted output as well: logf("Moving % to %", data, destination); -- /Jacob Carlborg
Aug 22 2013
parent "evilrat" <evilrat666 gmail.com> writes:
On Thursday, 22 August 2013 at 16:16:37 UTC, Jacob Carlborg wrote:
 On 2013-08-22 18:01, develop32 wrote:

 Why logging functions accept only a string? I would expect it 
 to behave
 as std.stdio with its variadic parameters.

 It would be more straightforward to write logging code:

 log("Moving ", data, " to ", destination);

 Where 'data' and 'destination' are any variables. I use such 
 setup in my
 projects and it helps greatly to identify what went wrong when 
 not using
 a debugger.
Good point, and formatted output as well: logf("Moving % to %", data, destination);
maybe it is bad idea, but string.format can be used too. log(format("Moving %s to %s", data, destination);
Aug 22 2013
prev sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 06:01 PM, develop32 wrote:
 Why logging functions accept only a string? I would expect it to
 behave as std.stdio with its variadic parameters.

 It would be more straightforward to write logging code:

 log("Moving ", data, " to ", destination);

 Where 'data' and 'destination' are any variables. I use such setup in
 my projects and it helps greatly to identify what went wrong when not
 using a debugger.
I thought about that. IMO this would create very heavy template bloat, because you would also have to pass __LINE__, __FUNCTION__ as template arguments. Whats so wrong with log("%s %s %s %s".format("Moving", "data", "to", destination)); This way you don't have to remember to place the spaces left and right.
Aug 22 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Thu, 22 Aug 2013 19:13:05 +0200
schrieb Robert Schadek <realburner gmx.de>:

 On 08/22/2013 06:01 PM, develop32 wrote:
 Why logging functions accept only a string? I would expect it to
 behave as std.stdio with its variadic parameters.

 It would be more straightforward to write logging code:

 log("Moving ", data, " to ", destination);

 Where 'data' and 'destination' are any variables. I use such setup
 in my projects and it helps greatly to identify what went wrong
 when not using a debugger.
I thought about that. IMO this would create very heavy template bloat, because you would also have to pass __LINE__, __FUNCTION__ as template arguments. Whats so wrong with log("%s %s %s %s".format("Moving", "data", "to", destination)); This way you don't have to remember to place the spaces left and right.
The problem is that format allocates using the GC. Functions like writefln can be much more efficient so if the backend is a FileLogger it could take advantage of that. But IIRC it's not simple to implement this 'forwarding' with classes / interfaces as you'll need templated functions...
Aug 22 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 07:48 PM, Johannes Pfau wrote:
 The problem is that format allocates using the GC. Functions like
 writefln can be much more efficient so if the backend is a FileLogger it
 could take advantage of that. But IIRC it's not simple to implement
 this 'forwarding' with classes / interfaces as you'll need templated
 functions...
yes, but as you said it is not simple. If it where a template the assignable default logger concept would not work, as it would instantiate the template of the base class. I don't think that this is possible (plz proof me wrong). IMO the default logger concept is more important.
Aug 22 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Thu, 22 Aug 2013 20:04:16 +0200
schrieb Robert Schadek <realburner gmx.de>:

 On 08/22/2013 07:48 PM, Johannes Pfau wrote:
 The problem is that format allocates using the GC. Functions like
 writefln can be much more efficient so if the backend is a
 FileLogger it could take advantage of that. But IIRC it's not
 simple to implement this 'forwarding' with classes / interfaces as
 you'll need templated functions...
yes, but as you said it is not simple. If it where a template the assignable default logger concept would not work, as it would instantiate the template of the base class. I don't think that this is possible (plz proof me wrong). IMO the default logger concept is more important.
I've implemented a small logger some time ago and IIRC I couldn't come up with a satisfying solution. The last std.log proposal made a compromise to deal with this: It accepted formatted log input but it did call sformat internally before passing it to the Logger implementation. The benefit of this was that it could reuse the same buffer. In (pseudo)code: void logf(Args...)(string fmt, Args args) { ubyte[BUFSIZE] buffer; _logger.log(sformat(buffer, fmt, args)); }
Aug 22 2013
parent Robert Schadek <realburner gmx.de> writes:
On 08/22/2013 08:17 PM, Johannes Pfau wrote:
 Am Thu, 22 Aug 2013 20:04:16 +0200
 schrieb Robert Schadek <realburner gmx.de>:

 On 08/22/2013 07:48 PM, Johannes Pfau wrote:
 The problem is that format allocates using the GC. Functions like
 writefln can be much more efficient so if the backend is a
 FileLogger it could take advantage of that. But IIRC it's not
 simple to implement this 'forwarding' with classes / interfaces as
 you'll need templated functions...
yes, but as you said it is not simple. If it where a template the assignable default logger concept would not work, as it would instantiate the template of the base class. I don't think that this is possible (plz proof me wrong). IMO the default logger concept is more important.
I've implemented a small logger some time ago and IIRC I couldn't come up with a satisfying solution. The last std.log proposal made a compromise to deal with this: It accepted formatted log input but it did call sformat internally before passing it to the Logger implementation. The benefit of this was that it could reuse the same buffer. In (pseudo)code: void logf(Args...)(string fmt, Args args) { ubyte[BUFSIZE] buffer; _logger.log(sformat(buffer, fmt, args)); }
What happens if this throws because of an overflow and you're only indirectly responsable for the call to logf? I would rather pass my own static string buffer to log if I fear performance problems, than to hope that the buffer in logf is big enough.
Aug 22 2013
prev sibling next sibling parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
W dniu 22.08.2013 16:00, Robert Schadek pisze:
 Docu:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html
What about specifying log level at runtime, e.g.: // set by the user at runtime LogLevel userLogLevelForX; log(userLogLevelForX, "X"); ?
Aug 22 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 02:07 AM, Piotr Szturmaj wrote:
 W dniu 22.08.2013 16:00, Robert Schadek pisze:
 Docu:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html
What about specifying log level at runtime, e.g.: // set by the user at runtime LogLevel userLogLevelForX; log(userLogLevelForX, "X"); ?
Any other Logger: auto myCoolLogger = new XXXXLogger(); myCoolLogger.logLevel = LogLevel.Critical; For the default Logger: log.logLevel = LogLevel.Info;
Aug 23 2013
parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
W dniu 23.08.2013 09:44, Robert Schadek pisze:
 On 08/23/2013 02:07 AM, Piotr Szturmaj wrote:
 W dniu 22.08.2013 16:00, Robert Schadek pisze:
 Docu:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html
What about specifying log level at runtime, e.g.: // set by the user at runtime LogLevel userLogLevelForX; log(userLogLevelForX, "X"); ?
Any other Logger: auto myCoolLogger = new XXXXLogger(); myCoolLogger.logLevel = LogLevel.Critical; For the default Logger: log.logLevel = LogLevel.Info;
I see, but isn't separate class instance for every log level a bit too much? Setting log level before calling log() isn't (IMHO) good either (think of multi-threaded logging). My opinion is that LogLevel should not be encapsulated within Logger class. Instead function overloading should be used (again IMO): void log(string message); void log(LogLevel level, string message); So having separate classes for different loggers (sinks) is very nice, but LogLevel should be specifiable for each message, no matter which Logger class is used. Then, each _compile-time_ specifiers like .Warning() or .Error() should be just a syntactic sugar over log().
Aug 23 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 10:30 AM, Piotr Szturmaj wrote:
 ?
Any other Logger: auto myCoolLogger = new XXXXLogger(); myCoolLogger.logLevel = LogLevel.Critical; For the default Logger: log.logLevel = LogLevel.Info;
I see, but isn't separate class instance for every log level a bit too much? Setting log level before calling log() isn't (IMHO) good either (think of multi-threaded logging).
There isn't. Every Logger has a LogLevel, which can be changed. Every log message also has a LogLevel hence .info() .error() ...
 My opinion is that LogLevel should not be encapsulated within Logger
 class.

 Instead function overloading should be used (again IMO):

 void log(string message);
 void log(LogLevel level, string message);

 So having separate classes for different loggers (sinks) is very nice,
 but LogLevel should be specifiable for each message, no matter which
 Logger class is used.
It is.
 Then, each _compile-time_ specifiers like .Warning() or .Error()
 should be just a syntactic sugar over log().
Aug 23 2013
next sibling parent reply "growler" <growlercab gmail.com> writes:
Is there any plan to support remote logging? This would be great 
for embedded stuff we're playing with.

G.
Aug 23 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 12:00 PM, growler wrote:
 Is there any plan to support remote logging? This would be great for
 embedded stuff we're playing with.

 G.
Not buildin, but the logger is designed to be easily extended. So if needed, roll your own.
Aug 23 2013
parent "growler" <growlercab gmail.com> writes:
On Friday, 23 August 2013 at 10:09:42 UTC, Robert Schadek wrote:
 On 08/23/2013 12:00 PM, growler wrote:
 Is there any plan to support remote logging? This would be 
 great for
 embedded stuff we're playing with.

 G.
Not buildin, but the logger is designed to be easily extended. So if needed, roll your own.
will do, thanks :D
Aug 23 2013
prev sibling parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
W dniu 23.08.2013 11:01, Robert Schadek pisze:
 On 08/23/2013 10:30 AM, Piotr Szturmaj wrote:
 ?
Any other Logger: auto myCoolLogger = new XXXXLogger(); myCoolLogger.logLevel = LogLevel.Critical; For the default Logger: log.logLevel = LogLevel.Info;
I see, but isn't separate class instance for every log level a bit too much? Setting log level before calling log() isn't (IMHO) good either (think of multi-threaded logging).
There isn't. Every Logger has a LogLevel, which can be changed. Every log message also has a LogLevel hence .info() .error() ...
 My opinion is that LogLevel should not be encapsulated within Logger
 class.

 Instead function overloading should be used (again IMO):

 void log(string message);
 void log(LogLevel level, string message);

 So having separate classes for different loggers (sinks) is very nice,
 but LogLevel should be specifiable for each message, no matter which
 Logger class is used.
It is.
so if I have a LogLevel variable I want to use: LogLevel myLogLevel; I need to write this: switch (myLogLevel) { case LogLevel.Info: log.Info("..."); break; case LogLevel.Warning: log.Warning("..."); break; case LogLevel.Error: log.Error("..."); break; case LogLevel.Critical: log.Critical("..."); break; case LogLevel.Fatal: log.Fatal("..."); break; } #alternative 2 log.logLevel = myLogLevel; // not thread-safe (shared state) log("..."); I mean sure, it is possible to specify LogLevel for each message, but only at compile time (by the use of function names .Info, .Error, etc.). This is not what I meant in my previous post. I rather meant the ability to specify LogLevel using a runtime variable.
 Then, each _compile-time_ specifiers like .Warning() or .Error()
 should be just a syntactic sugar over log().
Just thought that the ability to add user defined log levels may be useful too.
Aug 23 2013
next sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 01:10 PM, Piotr Szturmaj wrote:
 so if I have a LogLevel variable I want to use:

 LogLevel myLogLevel;

 I need to write this:


 switch (myLogLevel)
 {
     case LogLevel.Info: log.Info("..."); break;
     case LogLevel.Warning: log.Warning("..."); break;
     case LogLevel.Error: log.Error("..."); break;
     case LogLevel.Critical: log.Critical("..."); break;
     case LogLevel.Fatal: log.Fatal("..."); break;
 }

 #alternative 2
 log.logLevel = myLogLevel; // not thread-safe (shared state)
 log("...");
the default logger is not shared. so no thread problems.
 I mean sure, it is possible to specify LogLevel for each message, but
 only at compile time (by the use of function names .Info, .Error, etc.).

 This is not what I meant in my previous post. I rather meant the
 ability to specify LogLevel using a runtime variable.
auto myLogger = new MyLogger(myLogLevel); mylogger("my log msg");
 Then, each _compile-time_ specifiers like .Warning() or .Error()
 should be just a syntactic sugar over log().
Just thought that the ability to add user defined log levels may be useful too.
You can implement the abstract logger class and create new log functions, but the design is intended to simple and user defined log level makes it complex.
Aug 23 2013
parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
W dniu 23.08.2013 14:16, Robert Schadek pisze:
 On 08/23/2013 01:10 PM, Piotr Szturmaj wrote:
 so if I have a LogLevel variable I want to use:

 LogLevel myLogLevel;

 I need to write this:


 switch (myLogLevel)
 {
      case LogLevel.Info: log.Info("..."); break;
      case LogLevel.Warning: log.Warning("..."); break;
      case LogLevel.Error: log.Error("..."); break;
      case LogLevel.Critical: log.Critical("..."); break;
      case LogLevel.Fatal: log.Fatal("..."); break;
 }

 #alternative 2
 log.logLevel = myLogLevel; // not thread-safe (shared state)
 log("...");
the default logger is not shared. so no thread problems.
But the problems remain with non-default loggers?
 I mean sure, it is possible to specify LogLevel for each message, but
 only at compile time (by the use of function names .Info, .Error, etc.).

 This is not what I meant in my previous post. I rather meant the
 ability to specify LogLevel using a runtime variable.
auto myLogger = new MyLogger(myLogLevel); mylogger("my log msg");
So, we're at the start point. You must either create instances for every log level (and for every logger class) or set a log level before calling the log() - and this is not thread safe for custom loggers. This also doesn't look as a good design: auto myLogger = new MyLogger(myLogLevel); myLogger("my log msg"); myLogger.logLevel = anotherMyLogLevel; myLogger("another log msg"); myLogger.logLevel = yetAnotherMyLogLevel; myLogger("yet another log msg"); Why encapsulate a logLevel which is immediately used in the function? Why store it in a class? It should be a function parameter instead. Don't get me wrong. I just want to help you. I think that default logger concept should be separated to default logger and default log level (default log level should be independent from default logger)
 Then, each _compile-time_ specifiers like .Warning() or .Error()
 should be just a syntactic sugar over log().
Just thought that the ability to add user defined log levels may be useful too.
You can implement the abstract logger class and create new log functions, but the design is intended to simple and user defined log level makes it complex.
Aug 23 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 02:56 PM, Piotr Szturmaj wrote:
 W dniu 23.08.2013 14:16, Robert Schadek pisze:
 On 08/23/2013 01:10 PM, Piotr Szturmaj wrote:
 so if I have a LogLevel variable I want to use:

 LogLevel myLogLevel;

 I need to write this:


 switch (myLogLevel)
 {
      case LogLevel.Info: log.Info("..."); break;
      case LogLevel.Warning: log.Warning("..."); break;
      case LogLevel.Error: log.Error("..."); break;
      case LogLevel.Critical: log.Critical("..."); break;
      case LogLevel.Fatal: log.Fatal("..."); break;
 }

 #alternative 2
 log.logLevel = myLogLevel; // not thread-safe (shared state)
 log("...");
the default logger is not shared. so no thread problems.
But the problems remain with non-default loggers?
I don't see your point. Currently there are two trivial logger. An stdio and a file logger. Both are thread local. There are only these two, because I believe that you cannot create a logging library that fulfills everybody requirements and trying will lead to a logger library nobody can use anymore. So I took the opposite direction and made it simple merely defining a common interface to make logging to defined types of loggers uniform. So if you need a logger that is thread safe. Please created on and use it.
 I mean sure, it is possible to specify LogLevel for each message, but
 only at compile time (by the use of function names .Info, .Error,
 etc.).

 This is not what I meant in my previous post. I rather meant the
 ability to specify LogLevel using a runtime variable.
auto myLogger = new MyLogger(myLogLevel); mylogger("my log msg");
So, we're at the start point. You must either create instances for every log level (and for every logger class) or set a log level before calling the log() - and this is not thread safe for custom loggers. This also doesn't look as a good design: auto myLogger = new MyLogger(myLogLevel); myLogger("my log msg"); myLogger.logLevel = anotherMyLogLevel; myLogger("another log msg"); myLogger.logLevel = yetAnotherMyLogLevel; myLogger("yet another log msg");
If the LogLevel you are using are not const and are computed during the lifetime of the program. Than yes, that is the way you have to use it. But if anotherMyLogLevel and yetAnotherMyLogLevel are mere const, why not write myLogger.warning("...."); myLogger.error("...")?
 Why encapsulate a logLevel which is immediately used in the function?
 Why store it in a class? It should be a function parameter instead.
I'm not sure if I understand your first sentence! Having a LogLevel stored in a logger allows to disable the logging of some of the messages send to a specific logger. Example: auto myLogger = new Logger(); myLogger.warning("Something is fishy"); myLogger.warning("Something else is fishy"); next round I want to disable warning messages logged by myLogger. I don't want to touch all the .warning("..") lines. I want to write myLogger.logLevel = LogLevel.Fatal; and disable all warning messages this way.
 Don't get me wrong. I just want to help you.

 I think that default logger concept should be separated to default
 logger and default log level (default log level should be independent
 from default logger)
It is separated! LogManager.globalLogLevel = LogLevel.XXXXX; sets the global LogLevel. The defaultLogger is just another logger which can be found at a special place.
Aug 23 2013
parent Gambler <fake feather.org.ru> writes:
On 8/23/2013 9:48 AM, Robert Schadek wrote:
 On 08/23/2013 02:56 PM, Piotr Szturmaj wrote:
 W dniu 23.08.2013 14:16, Robert Schadek pisze:
 On 08/23/2013 01:10 PM, Piotr Szturmaj wrote:
 so if I have a LogLevel variable I want to use:

 LogLevel myLogLevel;

 I need to write this:


 switch (myLogLevel)
 {
      case LogLevel.Info: log.Info("..."); break;
      case LogLevel.Warning: log.Warning("..."); break;
      case LogLevel.Error: log.Error("..."); break;
      case LogLevel.Critical: log.Critical("..."); break;
      case LogLevel.Fatal: log.Fatal("..."); break;
 }

 #alternative 2
 log.logLevel = myLogLevel; // not thread-safe (shared state)
 log("...");
the default logger is not shared. so no thread problems.
But the problems remain with non-default loggers?
I don't see your point. Currently there are two trivial logger. An stdio and a file logger. Both are thread local. There are only these two, because I believe that you cannot create a logging library that fulfills everybody requirements and trying will lead to a logger library nobody can use anymore. So I took the opposite direction and made it simple merely defining a common interface to make logging to defined types of loggers uniform. So if you need a logger that is thread safe. Please created on and use it.
 I mean sure, it is possible to specify LogLevel for each message, but
 only at compile time (by the use of function names .Info, .Error,
 etc.).

 This is not what I meant in my previous post. I rather meant the
 ability to specify LogLevel using a runtime variable.
auto myLogger = new MyLogger(myLogLevel); mylogger("my log msg");
So, we're at the start point. You must either create instances for every log level (and for every logger class) or set a log level before calling the log() - and this is not thread safe for custom loggers. This also doesn't look as a good design: auto myLogger = new MyLogger(myLogLevel); myLogger("my log msg"); myLogger.logLevel = anotherMyLogLevel; myLogger("another log msg"); myLogger.logLevel = yetAnotherMyLogLevel; myLogger("yet another log msg");
If the LogLevel you are using are not const and are computed during the lifetime of the program. Than yes, that is the way you have to use it. But if anotherMyLogLevel and yetAnotherMyLogLevel are mere const, why not write myLogger.warning("...."); myLogger.error("...")?
 Why encapsulate a logLevel which is immediately used in the function?
 Why store it in a class? It should be a function parameter instead.
I'm not sure if I understand your first sentence! Having a LogLevel stored in a logger allows to disable the logging of some of the messages send to a specific logger. Example: auto myLogger = new Logger(); myLogger.warning("Something is fishy"); myLogger.warning("Something else is fishy"); next round I want to disable warning messages logged by myLogger. I don't want to touch all the .warning("..") lines. I want to write myLogger.logLevel = LogLevel.Fatal; and disable all warning messages this way.
 Don't get me wrong. I just want to help you.

 I think that default logger concept should be separated to default
 logger and default log level (default log level should be independent
 from default logger)
It is separated! LogManager.globalLogLevel = LogLevel.XXXXX; sets the global LogLevel. The defaultLogger is just another logger which can be found at a special place.
Piotr brings up good very points and I would say your answers don't fully address them. Why should there be 5 differently named methods for something that does *the same thing* except for logging level? What is the advantage over having just one with level passed as a parameter? Also, if I understand it correctly, the class has a method that will log *the same message* at different levels based on some centralized configuration. That doesn't sound like a good idea. Severity of an error depends on what it is. It doesn't sound right to change it based on some centralized config setting in an unrelated class. I.e. if something is a fatal error, it should always be explicitly, obviously fatal. Changing that *should* require an explicit change at the place where the error is logged. Frankly, I would not allow changing loggers LogLevel after initialization either. There is simply no good reason to do that and sound so would lead to really annoying issues when analyzing code. (See PHP's awful operator.) Lastly, I think what Piotr meant by "separated" is that there is a common need to separate log *collector* (which should be as standard as possible) and log *emitters* (which would do different things for the same message, like sending emails, writing to files, outputting the yellow screen of death, etc). LogLevel should be a property of log emitters. That's a very, very common need. You want to write every single message to a file, but you only want to get fatal errors via email.
Aug 23 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/23/13 4:10 AM, Piotr Szturmaj wrote:
 Just thought that the ability to add user defined log levels may be
 useful too.
(Just hanging this to a random comment in this thread.) I think there's some pretty good work on logging by myself and another poster (Jose?) that has since gone abandoned. It included some nice approach to conditional logging and had both compile-time and run-time configurability. Andrei
Aug 23 2013
next sibling parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 09:41 PM, Andrei Alexandrescu wrote:
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another poster
 (Jose?) that has since gone abandoned. It included some nice approach
 to conditional logging and had both compile-time and run-time
 configurability 
Yes I know, but as you said, it got abandoned. So something must have been wrong. I rather start something fresh and argue about whats wrong with that, than argue whats wrong with something old that has been already argued about and got abandoned.
Aug 23 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/23/13 1:10 PM, Robert Schadek wrote:
 On 08/23/2013 09:41 PM, Andrei Alexandrescu wrote:
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another poster
 (Jose?) that has since gone abandoned. It included some nice approach
 to conditional logging and had both compile-time and run-time
 configurability
Yes I know, but as you said, it got abandoned. So something must have been wrong. I rather start something fresh and argue about whats wrong with that, than argue whats wrong with something old that has been already argued about and got abandoned.
Great. There was nothing wrong with it except it got no more work. I'd expect a new framework to be at least as good if not better. Andrei
Aug 23 2013
parent Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 11:49 PM, Andrei Alexandrescu wrote:
 On 8/23/13 1:10 PM, Robert Schadek wrote:
 On 08/23/2013 09:41 PM, Andrei Alexandrescu wrote:
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another poster
 (Jose?) that has since gone abandoned. It included some nice approach
 to conditional logging and had both compile-time and run-time
 configurability
Yes I know, but as you said, it got abandoned. So something must have been wrong. I rather start something fresh and argue about whats wrong with that, than argue whats wrong with something old that has been already argued about and got abandoned.
Great. There was nothing wrong with it except it got no more work. I'd expect a new framework to be at least as good if not better. Andrei
Of course, it has to be phobos worthy.
Aug 24 2013
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Aug 23, 2013 at 12:41:45PM -0700, Andrei Alexandrescu wrote:
[...]
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another
 poster (Jose?) that has since gone abandoned. It included some nice
 approach to conditional logging and had both compile-time and
 run-time configurability.
[...] Where's the code? T -- What doesn't kill me makes me stranger.
Aug 23 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Friday, 23 August 2013 at 20:11:38 UTC, H. S. Teoh wrote:
 On Fri, Aug 23, 2013 at 12:41:45PM -0700, Andrei Alexandrescu 
 wrote:
 [...]
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another
 poster (Jose?) that has since gone abandoned. It included some 
 nice
 approach to conditional logging and had both compile-time and
 run-time configurability.
[...] Where's the code? T
https://github.com/D-Programming-Language/phobos/pull/432 (got from http://wiki.dlang.org/Review_Queue)
Aug 25 2013
prev sibling parent "Jose A Garcia Sancio" <jsancio gmail.com> writes:
On Friday, 23 August 2013 at 20:11:38 UTC, H. S. Teoh wrote:
 On Fri, Aug 23, 2013 at 12:41:45PM -0700, Andrei Alexandrescu 
 wrote:
 [...]
 (Just hanging this to a random comment in this thread.) I think
 there's some pretty good work on logging by myself and another
 poster (Jose?) that has since gone abandoned. It included some 
 nice
 approach to conditional logging and had both compile-time and
 run-time configurability.
[...] Where's the code? T
The code is here: https://github.com/jsancio/log.d/blob/master/src/log.d For what it is worth, there was a lot of thought and effort that was put into the design, implementation and testing of it. If you are interested in the design goals of the std.log library then my recommendation is to learn about glog (http://google-glog.googlecode.com/svn/trunk/doc/glog.html). If the community decides that they like those design principles then I highly recommend you continue where std.log left off. Thanks, -Jose
Sep 17 2013
prev sibling next sibling parent Artem Tarasov <lomereiter gmail.com> writes:
 http://burner.github.io/phobos/phobos-prerelease/std_logger.html
6 params to logMessage and writeMsgToLog is way too many. First four of them should be merged into a single struct representing the location.
Aug 22 2013
prev sibling next sibling parent reply "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 22 August 2013 at 14:13:29 UTC, Robert Schadek wrote:
 lots..
Just to weight in and give my four penneth. It needs to be made a little more flexible in the formats it produces (syslog?) and the input it takes. e.g. i've just finished a nice logger for work and it's main usage is like this: auto logger = new Logger("file"); logger.info("device id: %s", device.id); I like the formatting that sprintf gives. Also behind the scenes everything is nicely abstract and uses the same method for output only the options change.
Aug 23 2013
parent reply Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 06:28 PM, Gary Willoughby wrote:
 On Thursday, 22 August 2013 at 14:13:29 UTC, Robert Schadek wrote:
 lots..
Just to weight in and give my four penneth. It needs to be made a little more flexible in the formats it produces (syslog?) and the input it takes. e.g. i've just finished a nice logger for work and it's main usage is like this:
Ask if you are allowed to put it into phobos.
 auto logger = new Logger("file");
 logger.info("device id: %s", device.id);

 I like the formatting that sprintf gives.
I do as well, but this will lead to template bloat, as many people will tell you, and log("device id: %s".format(device.id)); is not so bad.
Aug 23 2013
next sibling parent reply "Gary Willoughby" <dev nomad.so> writes:
On Friday, 23 August 2013 at 16:49:00 UTC, Robert Schadek wrote:
 I like the formatting that sprintf gives.
I do as well, but this will lead to template bloat, as many people will tell you, and log("device id: %s".format(device.id)); is not so bad.
I don't think you can bloat a simple logger too much with templates. It's a pretty simple framework. The problem is that people rarely want to log only strings and not supporting other types will lead to this: logger.info(format("Device id: %s"), device.id); as i found when i started logging stuff. yuk!
Aug 23 2013
next sibling parent Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 07:21 PM, Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with templates.
 It's a pretty simple framework.
every log call with more than a string will be a template. My first private logger version was like this. After reading the last logger thread and other stuff, template bloat seams to be an important issue. I will change some stuff tonight and will also make printf style logging a default. But if anyone screams template bloat in this thread, I expect you to hunt him down!
 The problem is that people rarely want to log only strings and not
 supporting other types will lead to this:

 logger.info(format("Device id: %s"), device.id);

 as i found when i started logging stuff. yuk!
my version looked prettier!
Aug 23 2013
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 23, 2013 19:21:33 Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with
 templates. It's a pretty simple framework.
If __FILE__ and __LINE__ are template arguments to a logging function rather than function arguments (and you can't make __FILE__ and __LINE__ default function arguments if the function is variadic as it would have to be to support format strings), then you get a new template instantation every single time that you call the function, unless you call it more than once on the same line (which you're unlikely to ever do). I want to think that there's a way to handle this if you get clever, but I can't think of a clever way to get around the problem at the moment. - Jonathan M Davis
Aug 23 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Fri, 23 Aug 2013 15:16:05 -0400
schrieb "Jonathan M Davis" <jmdavisProg gmx.com>:

 On Friday, August 23, 2013 19:21:33 Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with
 templates. It's a pretty simple framework.
If __FILE__ and __LINE__ are template arguments to a logging function rather than function arguments (and you can't make __FILE__ and __LINE__ default function arguments if the function is variadic as it would have to be to support format strings), then you get a new template instantation every single time that you call the function, unless you call it more than once on the same line (which you're unlikely to ever do). I want to think that there's a way to handle this if you get clever, but I can't think of a clever way to get around the problem at the moment. - Jonathan M Davis
Make the templated function a stub which redirects to a function which uses normal function arguments and rely on inlining?
Aug 23 2013
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 23, 2013 21:47:44 Johannes Pfau wrote:
 Am Fri, 23 Aug 2013 15:16:05 -0400
 
 schrieb "Jonathan M Davis" <jmdavisProg gmx.com>:
 On Friday, August 23, 2013 19:21:33 Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with
 templates. It's a pretty simple framework.
If __FILE__ and __LINE__ are template arguments to a logging function rather than function arguments (and you can't make __FILE__ and __LINE__ default function arguments if the function is variadic as it would have to be to support format strings), then you get a new template instantation every single time that you call the function, unless you call it more than once on the same line (which you're unlikely to ever do). I want to think that there's a way to handle this if you get clever, but I can't think of a clever way to get around the problem at the moment. - Jonathan M Davis
Make the templated function a stub which redirects to a function which uses normal function arguments and rely on inlining?
Inlining will have no effect on __FILE__ and __LINE__, and it would be a bug if it did, because they're supposed to give the file and line number of the source code, whereas inlining only affects the generated binary. - Jonathan M Davis
Aug 23 2013
parent Johannes Pfau <nospam example.com> writes:
Am Fri, 23 Aug 2013 16:08:40 -0400
schrieb "Jonathan M Davis" <jmdavisProg gmx.com>:

 On Friday, August 23, 2013 21:47:44 Johannes Pfau wrote:
 Am Fri, 23 Aug 2013 15:16:05 -0400
 
 schrieb "Jonathan M Davis" <jmdavisProg gmx.com>:
 On Friday, August 23, 2013 19:21:33 Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with
 templates. It's a pretty simple framework.
If __FILE__ and __LINE__ are template arguments to a logging function rather than function arguments (and you can't make __FILE__ and __LINE__ default function arguments if the function is variadic as it would have to be to support format strings), then you get a new template instantation every single time that you call the function, unless you call it more than once on the same line (which you're unlikely to ever do). I want to think that there's a way to handle this if you get clever, but I can't think of a clever way to get around the problem at the moment. - Jonathan M Davis
Make the templated function a stub which redirects to a function which uses normal function arguments and rely on inlining?
Inlining will have no effect on __FILE__ and __LINE__, and it would be a bug if it did, because they're supposed to give the file and line number of the source code, whereas inlining only affects the generated binary.
I think you misunderstood. If you write code like this: void log(string file = __FILE__)() //A template { logImpl(file); } void logImpl(string file){} //Not a template The compiler can always inline the log template. So there's no template bloat as there will be effectively no instances of log. Instead it will be inlined and logImpl will be called directly just as if you manually called logImpl(__FILE__).
Aug 23 2013
prev sibling parent Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 10:08 PM, Jonathan M Davis wrote:
 On Friday, August 23, 2013 21:47:44 Johannes Pfau wrote:

 Make the templated function a stub which redirects to a function which
 uses normal function arguments and rely on inlining?
Inlining will have no effect on __FILE__ and __LINE__, and it would be a bug if it did, because they're supposed to give the file and line number of the source code, whereas inlining only affects the generated binary. - Jonathan M Davis
I think I came up with something good looking. A regex explains it best. log // still returns the default logger (LOGGER\.)?log\((LOGLEVEL,)? (CONDITION,)? (MESSAGE)?\) // normal logging without template bloat (LOGGER\.)?logf\((LOGLEVEL,)? (CONDITION,)? (MESSAGE), (ARGS,)*\) // printf logging with templates bloat I push the changes and the new docu (which needs some more work). Good night everybody.
Aug 23 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Aug 23, 2013 at 03:16:05PM -0400, Jonathan M Davis wrote:
 On Friday, August 23, 2013 19:21:33 Gary Willoughby wrote:
 I don't think you can bloat a simple logger too much with
 templates. It's a pretty simple framework.
If __FILE__ and __LINE__ are template arguments to a logging function rather than function arguments (and you can't make __FILE__ and __LINE__ default function arguments if the function is variadic as it would have to be to support format strings), then you get a new template instantation every single time that you call the function, unless you call it more than once on the same line (which you're unlikely to ever do). I want to think that there's a way to handle this if you get clever, but I can't think of a clever way to get around the problem at the moment.
[...] Hmm. What about: void funcImpl(A...)(string file, int line, A args) { ... } template func(string file=__FILE__, int line=__LINE__, A...) { func(A args) { funcImpl(file, line, args); } } It still has template bloat in that func will be instantiated for every call though, but at least the function body isn't duplicated that many times. T -- A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. -- Michael B. Allen
Aug 23 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-23 18:48, Robert Schadek wrote:

 I do as well, but this will lead to template bloat, as many people will
 tell you, and log("device id: %s".format(device.id)); is
 not so bad.
Won't it be just as much template bloat using "format"? -- /Jacob Carlborg
Aug 23 2013
parent Robert Schadek <realburner gmx.de> writes:
On 08/23/2013 10:34 PM, Jacob Carlborg wrote:
 On 2013-08-23 18:48, Robert Schadek wrote:

 I do as well, but this will lead to template bloat, as many people will
 tell you, and log("device id: %s".format(device.id)); is
 not so bad.
Won't it be just as much template bloat using "format"?
well yes, but log(A...)(A a) { format("...", a); } is worse ;-)
Aug 23 2013
prev sibling parent "Volcz" <volcz kth.se> writes:
I would to drop some requirements I've seen in the Java world.

* As klamonte mention in the pull request on GitHub: Appenders 
are a must in a enterprise enviorment.
* Same with filters. In SLF4J normally each class has it's own 
logger which can be filtered or appended to individual files.
* Runtime configuration. Crucial when collection data about bugs 
in running systems.
* Performance... The possibility to write asynchronous loggers.
* Sometimes it's expensive to construct the log output. In Java 
for example Logback calls toString on objects after it has 
concluded that the data is needed. So a possibility to pass an 
function for constructing the log output only when the logger is 
sure that it's going to be logged.

My 2 SEK. ;-)
Sep 07 2013