Hi Joe!
On 2016-10-31 08:15, Joe wrote:
I have some ideas for developing a new AsyncAppenderSkeleton, based on
recent experience developing a custom async appender that sends
logging events to a Web API.
My current thoughts are:
1.A new base class AsyncAppenderSkeleton that can be configured to
work in synchronous mode (identical to AppenderSkeleton) or
asynchronous mode, where DoAppend queues events on to a FIFO queue,
and has a single background thread that dequeues events and calls the
existing Append methods.
This sounds mostly like the BufferingAppenderSkeleton, which only misses
the background worker thread to send the buffer. Therefore the
AsyncAppenderSkeleton could derive from BufferingAppenderSkeleton and
override a few methods that would additionally dispatch the job to a
background thread. See System.Threading.Task.Run().
2.When in asynchronous mode, there will be customizable and
configurable retry logic that can call Append if it throws an
exception. The retry login will be able to inspect the exception that
was thrown and keep a count of the number of retries so far. For
example, a derived appender that logs to a Web API might want to retry
5xx HTTP status codes, but treat 4xx status codes as permanent
errors. There will no retry logic in synchronous mode as this would
impact performance.
When running a task with System.Threading.Task.Run() it returns a
System.Threading.Tasks.Task object that allows to inspect the outcome of
the task. See:
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx
3.The default queue implementation would be a lossy in-memory Queue
with a configurable maximum length. It might be nice to provide the
possibility to plug in alternate queue implementations, such as a
persistent queue using MSMQ.
We would reuse the BufferingAppenderSkeleton implementation of the queue
which does (not yet) persist the queue. Please note also that MSMQ
sounds like a MS only service and that would in turn mean that the .net
core variant would no longer be able to benefit from the
AsyncAppenderSkeleton. To me this outlines a path that we would not like
to walk on.
4.A class AsyncForwardingAppender would derive from
AsyncAppenderSkeleton and implement IAppenderAttachable.
That's sensible to me. Looking at the class hierarchy of other
ForwardingAppender implementations composition sounds more sensible and
could be preferred over inheritance. We could try and work out a generic
ForwardingAppenderSkeleton implementation that can take arbitrary
appenders and must no longer be derived by every appender that should
become a Forwarding one.
5.It would be easy to create asynchronous versions of existing custom
appenders, by changing the base class from AppenderSkeleton to
AsyncAppenderSkeleton and optionally implementing some retry logic.
E.g. we could have a new AdoNetAppender that can be configured to
operate in synchronous mode when logging to a local database, or in
asynchronous mode when logging to a cloud-based database.
The default implementation should probably be able to operate
asynchronously or synchronously and change mode of operation based on a
configuration flag "Asynchronous=True". This levers easy reconfiguration
at runtime and has probably other benefits too.
In terms of timescales, I think it would be possible to produce
something by the end of 2016.
To do it I need to reimplement AppenderSkeleton’s DoAppend methods, to
enqueue events if in asynchronous mode, or call Append in synchronous
mode.
I don’t want to duplicate code by reimplementing the rest of
AppenderSkeleton, so I would like to do one of the following:
Option 1:
-Refactor all of AppenderSkeleton’s implementation except IAppender
and IBulkAppender into a new base class AppenderSkeletonBase. This
includes filtering, IOptionHandler, layout.
-Derive AppenderSkeleton and later the new AsyncAppenderSkeleton from
AppenderSkeletonBase.
Option 2:
-Make AppenderSkeleton’s DoAppend methods virtual, and derive
AsyncAppenderSkeleton from AppenderSkeleton.
Option 1 seems the cleanest to me; any thoughts?
Let's first write out a few thoughts and concepts. When we have a
concept, the best way to get it done will hopefully be obvious.
Cheers