std.experimental.logger: practical observations

Cliff via Digitalmars-d digitalmars-d at puremagic.com
Sun Sep 14 10:30:48 PDT 2014


On Sunday, 14 September 2014 at 07:22:52 UTC, Marco Leise wrote:
> Am Sat, 13 Sep 2014 14:34:16 +0000
> schrieb "Robert burner Schadek" <rburners at gmail.com>:
>
>> On Friday, 12 September 2014 at 16:08:42 UTC, Marco Leise 
>> wrote:
>> >
>> > Remember that the stdlog is __gshared? Imagine we set the
>> > LogLevel to off and while executing writeLogMsg ...
>> >
>> > * a different thread wants to log a warning to stdlog
>> > * a different thread wants to inspect/set the log level
>> >
>> > It is your design to have loggers shared between threads.
>> > You should go all the way to make them thread safe.
>> >
>> > * catch recursive calls from within the same thread,
>> >   while not affecting other threads' logging
>> > * make Logger a shared class and work with atomicLoad/Store,
>> >   a synchronized class or use the built-in monitor field
>> >   through synchronized(this) blocks.
>> 
>> hm, I don't know of any magic pill for that. I guess this 
>> would require some dataflow analysis.
>
> Why so complicated? In general - not specific to std.logger -
> I'd wrap those calls in some function that acquires a mutex
> and then check a recursion flag to abort the logging if this
> thread has already been here.
>
> synchronized(loggingMutex) {
>   if (isRecursion) return;
>   isRecursion = true;
>   scope(exit) isRecursion = false;
>   logger.writeLogMsg(...);
> }
>
>> > I know when to throw an exception, but I never used logging
>> > much. If some function throws, would I also log the same
>> > message with error() one line before the throw statement?
>> > Or would I log at the place where I catch the exception?
>> > What to do about the stack trace when I only have one line 
>> > per
>> > log entry?
>> > You see, I am a total newbie when it comes to logging and 
>> > from
>> > the question that arose in my head I figured exceptions and
>> > logging don't really mix. Maybe only info() and debug() 
>> > should
>> > be used and actual problems left to exception handling alone.
>> 
>> that is depended on what your program requires. You can write 
>> more than one line, just indent it by a tab or two. again no 
>> magic pill as far as I know
>
> Ok, I'll experiment a bit and see what works best.

I'd like to throw my oar in here:

On the subject of recursion, this is only a problem if the 
logging contract is that log methods are fully synchronous - was 
this an explicit design choice?

Loggers are not *necessarily* also debuggers.  When used for 
post-mortem analysis (the typical case), it is not generally 
important that log data has been written by the time any given 
log method has returned - if the caller *intends* that, the 
logging system can have a sync/flush method similar to I/O 
behavior, or a configuration option to force fully synchronized 
behavior.

Personally I am not a huge fan of any potential I/O calls being 
by-default synchronous - particularly when those calls may easily 
result in long-running operations e.g. a network call, wait on a 
contended resource, etc.  Coming from the .NET world and having 
seen far too many large programs with user-facing components, 
blocking I/O by-default leads to poor user experiences as their 
program starts to stutter or be subject to timeouts the original 
author did not test for or intend.  With an extensible logging 
system, the same can - I mean *will* - come about.  Logging to my 
mind is usually a fire-and-forget utility - I want to see what 
happened (past tense) not what is happening now (that's what a 
debugger is for).

A way to solve this is to make the (or some) logging methods 
asynchronous.  logger.writeLogAsync(...) which returns 
immediately.  As an implementation detail, the log request gets 
posted to an internal queue serviced by a logging thread (thread 
pool thread is probably fine for this).  Since requests are 
*conceptually* independent from each other, this breaks the 
unintentional semantic dependence which occurs when recursion is 
introduced within the logging system itself.  I think this is 
*generally* the behavior you want, and specialized methods can be 
used to enforce synchronized semantics on top of this.  This 
system also guarantees log message ordering within a given thread.

If this queue is serviced by a threadpool thread, then the next 
logical problem then is to ensure that thread does not get tied 
up by one of the endpoints. There are several ways to solve this 
as well.

- Cliff


More information about the Digitalmars-d mailing list