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