Hi all (and especially Remko),

Lately I spent some time looking at the asynchronous logger/logger
config and I think it could profit from some refactoring in 3.x.

The problems I am seeing are:

1. The disruptors of `AsyncLogger` and `AsyncLoggerConfig` use
different structures. The disruptor of `AsyncLoggerConfig` contains
basically a `Pair<AsyncLogger, LogEvent>`, while `AsyncLogger` uses
`RingBufferLogEvent`, which is basically `MutableLogEvent` with an
additional field of type `AsyncLogger`.

The design of the `AsyncLoggerConfigDisruptor` seems better: it always
uses an allocation-free `EventTranslatorTwoArg`[1], while
`AsyncLoggerDisruptor` users must choose between a
`EventTranslatorVarArg` which allocates a temporary array and an
`EventTranslator` that copies the log event into a thread-bound
structure first.

Can we remove `RingBufferLogEvent` and use `Pair<AsyncLogger,
MutableLogEvent>` instead?

2. I am not convinced by the utility of having multiple
implementations of `ReusableMessage`. Since the `ReusableMessage`
interface does not specify how to format messages, transferring data
between a `ReusableParameterizedMessage` and `MutableLogEvent`
requires a call to `ReusableMessage#formatTo` to account for the
possibility of different formatting algorithms.

We could remove `ReusableObjectMessage`, `ReusableSimpleMessage`,
`ReusableParameterizedMessage` and merge `ReusableMessageFactory` with
`ReusableLogEventFactory`.

Instead of 4 thread locals for each type of `ReusableMessage` we would
just have a single `Recycler<MutableLogEvent>` and the performance of
`MutableLogEvent#setMessage` could be improved:

public void setMessage(final Message message) {
    if (message instanceof MutableLogEvent) {
        // NOP
    } else ...
}

3. Before a thread jump, three values must be computed: the source
location (optional), the context map and the formatted message.
Currently the location were these are computed varies and can be as
early as the `ReusableLogEventFactory`. I would propose to move them
to the last moment before we pass control to another thread, i.e.:

 * in `EventTranslator#translateTo` (this way nothing is computed if
the ring buffer is full and the `AsyncQueueFullPolicy` chooses to
discard the message) for `AsyncLogger`/`AsyncLoggerConfig`,
 * just before  the call to `Queue#offer` for the `AsyncAppender`.

What do you think?

Piotr

[1] 
https://lmax-exchange.github.io/disruptor/javadoc/com.lmax.disruptor/com/lmax/disruptor/EventTranslatorTwoArg.html

Reply via email to