diff --git a/src/main/include/log4cxx/asyncappender.h b/src/main/include/log4cxx/asyncappender.h
index 21af7779e..406183d34 100644
--- a/src/main/include/log4cxx/asyncappender.h
+++ b/src/main/include/log4cxx/asyncappender.h
@@ -42,10 +42,11 @@ to prevent undefined behaviour when using this appender.
This appender is useful when outputting to a slow event sink,
for example, a remote SMTP server or a database.
-Attaching a FileAppender to AsyncAppender
-to reduce logging overhead is not recommended
+Note that configuring a FileAppender to use [buffered output](@ref log4cxx::FileAppender::setOption)
+usually results in lower overhead than
+attaching the FileAppender to an AsyncAppender
as the inter-thread communication overhead
-can exceed the time to write directly to a file.
+can exceed the time to add a message to a buffer.
You can attach multiple appenders to an AsyncAppender by:
- calling AsyncAppender::addAppender repeatedly when progammatically configuring Log4cxx.
@@ -58,13 +59,13 @@ Here is a sample configuration file:
### Configurable properties
-When the application produces logging events faster
+\anchor BlockingProperty When the application produces logging events faster
than the background thread is able to process,
the bounded buffer can become full.
In this situation AsyncAppender will either
block until the bounded buffer has a free slot or
discard the event.
-The Blocking property controls which behaviour is used.
+The [Blocking property](@ref AsyncAppender::setOption) controls which behaviour is used.
When events are discarded,
the logged output will indicate this
with a log message prefixed with Discarded.
@@ -100,7 +101,7 @@ class LOG4CXX_EXPORT AsyncAppender :
AsyncAppender();
/**
- * Destructor.
+ * If not closed, calls AsyncAppender::close.
*/
virtual ~AsyncAppender();
@@ -112,9 +113,17 @@ class LOG4CXX_EXPORT AsyncAppender :
*/
void addAppender(const AppenderPtr newAppender) override;
+ /**
+ * Call AppenderSkeleton#doAppendImpl without acquiring a lock.
+ */
void doAppend(const spi::LoggingEventPtr& event,
helpers::Pool& pool1) override;
+ /**
+ * Add \c event to a ring buffer.
+ * The behaviour when the ring buffer is full
+ * is controlled by the [Blocking property](@ref BlockingProperty) value.
+ */
void append(const spi::LoggingEventPtr& event, helpers::Pool& p) override;
/**
@@ -149,6 +158,8 @@ class LOG4CXX_EXPORT AsyncAppender :
*/
bool isAttached(const AppenderPtr appender) const override;
+ /** Return false
+ */
bool requiresLayout() const override;
/**
diff --git a/src/site/markdown/performance.md b/src/site/markdown/performance.md
index 424876c18..e5a309dd0 100644
--- a/src/site/markdown/performance.md
+++ b/src/site/markdown/performance.md
@@ -103,7 +103,7 @@ The "Iterations" column derivation is explained in [Google Benchmark documentati
| Multiprocess logging int+float using MessageBuffer, pattern: \%d \%m\%n | 3456 ns | 3456 ns | 203235 |
-# The "Appending" benchmarks just format the message (using PatternLayout) then discard the result.
--# The "Async" benchmarks test AsyncAppender throughput, with logging events discarded in the background thread.
+-# The "Async" benchmarks test [AsyncAppender](@ref log4cxx::AsyncAppender) throughput, with logging events discarded in the background thread.
-# The "Logging" benchmarks write to a file using buffered output. Overhead is 2-3 times more when not using buffered output.
The above table shows that the overhead of an enabled logging request
@@ -113,8 +113,21 @@ Most importantly note that [using buffered output](@ref log4cxx::FileAppender::s
reduces overhead more than any other detail.
Note also that logging from multiple threads concurrently
-to a common appender does not increase throughput due to lock contention.
+to a common appender generally does not increase throughput
+due to lock contention in [doAppend method](@ref log4cxx::AppenderSkeleton::doAppend).
To simplify the work of an appender implementator,
the [doAppend method](@ref log4cxx::AppenderSkeleton::doAppend) currently prevents multiple threads
concurrently entering [the append method](@ref log4cxx::AppenderSkeleton::append),
which is the method required to be implemented by a concrete appender class.
+
+The [AsyncAppender](@ref log4cxx::AsyncAppender) provides the least overhead
+when logging concurrently from multiple threads
+as it overrides the [doAppend method](@ref log4cxx::AsyncAppender::doAppend)
+and uses [std::atomic](https://en.cppreference.com/w/cpp/atomic/atomic.html)
+counters and a ring buffer to store logging events.
+A single background thread is used to extract the logging events
+from the ring bufffer and send them
+to the attached appenders.
+This moves the overhead of [the layout method](@ref log4cxx::Layout::format)
+and the blocking transfer of message data to the operating system
+from the calling thread to the background thread.