Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/main/include/log4cxx/asyncappender.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 <b>Blocking</b> 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 <i>Discarded</i>.
Expand Down Expand Up @@ -100,7 +101,7 @@ class LOG4CXX_EXPORT AsyncAppender :
AsyncAppender();

/**
* Destructor.
* If not closed, calls AsyncAppender::close.
*/
virtual ~AsyncAppender();

Expand All @@ -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;

/**
Expand Down Expand Up @@ -149,6 +158,8 @@ class LOG4CXX_EXPORT AsyncAppender :
*/
bool isAttached(const AppenderPtr appender) const override;

/** Return false
*/
bool requiresLayout() const override;

/**
Expand Down
17 changes: 15 additions & 2 deletions src/site/markdown/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.