Skip to content

AsyncWrapper target

Rolf Kristensen edited this page Feb 29, 2024 · 45 revisions

Enables asynchronous logging, so the application-thread will only have the overhead of NLog automatically capturing thread-context (Ex. ${threadi}) and pushing to a concurrent-queue. Background timer-event using Dotnet-ThreadPool for writing pending LogEvents from the concurrent-queue to the wrapped target. It also activates batch-writing of LogEvents for even better performance.

Platforms Supported: All

Configuration Syntax

<targets>
  <target xsi:type="AsyncWrapper"
          name="String"
          queueLimit="Integer"
          timeToSleepBetweenBatches="Integer"
          batchSize="Integer"
          overflowAction="Enum">
    <target xsi:type="wrappedTargetType" ...target properties... />
  </target>
</targets>

Parameters

General Options

  • name - Name of the target.

Buffering Options

  • queueLimit - Limit on the number of requests in the lazy writer thread request queue. Integer Default: 10000

  • timeToSleepBetweenBatches - Time in milliseconds to sleep between batches. Integer Default: 1 (NLog 4.6 changed default from 50 to 1). When set to '0' or '1', then it will only trigger timer when something is logged (Less timer activity when idle, Faster logging and improved garbage collection behavior).

  • batchSize - Number of log events that should be processed in a batch by the lazy writer thread. Integer Default: 100 (NLog 4.4.2 and newer has Default: 200)

  • fullBatchSizeWriteLimit - Max number of consecutive full batchSize writes to perform within the same timer event. Integer Default: 5. Introduced in NLog 4.4.2

  • overflowAction - Action to be taken when in-memory-queue becomes full (Reached queueLimit). This means the background-writer is falling behind, and cannot keep up with the application-threads logging. Default: Discard
    Possible values:

    • Discard - The application-thread will discard logevent to avoid becoming blocked or causing out-of-memory issues. If memory and large-object-heap usage is not an issue, then one can consider increasing queueLimit to 100.000. (Default)
    • Block - The application-thread will block until the background-writer-thread has taken the next batch. Avoids loosing important logevents, but can block all application-threads. Consider to throttle active thread count, by limiting number of concurrent inbound requests.
    • Grow - The application-thread will ignore the queueLimit, and will just allocate more memory. Can cause the entire application to experience out-of-memory-issues.
  • ForceLockingQueue - Force thread synchronization with monitor-lock, instead of using .NET ConcurrentQueue. Legacy NetFramework application will by default use monitor-lock (Default=true). .NET Standard 2.0 will by default use of the .NET lock-free ConcurrentQueue for improved concurrency (Default=false).

    Introduced in NLog 4.6

Remarks

Remember to Flush

Remember to Flush before application exit, is very important when enabling use of the Asynchronous target wrapper.

Async attribute

Asynchronous target wrapper allows the logger code to execute more quickly, by queuing messages and processing them in a separate thread. You should wrap targets that spend a non-trivial amount of time in their Write() method with asynchronous target to speed up logging. Because asynchronous logging is quite a common scenario, NLog supports a shorthand notation for wrapping all targets with AsyncWrapper. Just add async="true" to the <targets/> element in the configuration file.

Example:

<targets async="true"> 
  ... your targets go here ...
</targets>

Async attribute will discard by default

The async attribute is a shorthand for:

xsi:type="AsyncWrapper overflowAction="Discard" queueLimit="10000" batchSize="200" timeToSleepBetweenBatches="1"

So if you write a lot of messages (more then 10000) in a short time, it's possible that messages will be lost. This is intended behavior as keeping all the messages or waiting for all the messages to be written, could have impact on the performance of your program.

If you need all the log messages, then you can change from <targets async="true"> to using <default-wrapper>:

<targets>
    <default-wrapper xsi:type="AsyncWrapper" overflowAction="Block" />

    ... your targets go here ...
</targets>

Async attribute and AsyncWrapper

Avoid explicitly using AsyncWrapper when already having enabled <targets async="true">. It will actually give a performance hit and slow down processing.

AsyncWrapper and <rules>

When using the AsyncWrapper, do write to the wrapper in your <rules> section! In the following example: do write to "target2". If the <logger> is writing to "target1", the messages are not written asynchronously!

   <targets>
      <target name="target2" xsi:type="AsyncWrapper">
        <target name ="target1" xsi:type="File"
                    fileName="c:/temp/test.log" layout="${message}"
                    keepFileOpen="true" />
      </target>
    <rules>
      <logger name="*" minlevel="Info" writeTo="target2"/>
    </rules>
  </targets> 

Asynchronously writing and custom targets

When messages are written asynchronously, this is done in another thread. There can be custom NLog targets that requires writing to happen on the main thread for proper context capture. When used together with AsyncWrapper then asynchronous writing is used and context information can be lost.

By default all standard NLog targets supports use of AsyncWrapper. Custom NLog targets that inherits from TargetWithContext should also by default work correctly with AsyncWrapper.

BufferingWrapper and Async

The BufferingWrapper can also asynchronously when having configured FlushTimeout. AsyncWrapper is intended for improving performance, where BufferingWrapper is intended for throttling or even discarding LogEvents. AsyncWrapper has an internal buffer optimized to handle many concurrent threads.

Clone this wiki locally