This is an automated email from the ASF dual-hosted git repository.

vy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/main by this push:
     new 78e132613c Revamp the performance-related website content (#2585)
78e132613c is described below

commit 78e132613c39c1b1e940261f0c91d96ae39bc6ef
Author: Volkan Yazıcı <vol...@yazi.ci>
AuthorDate: Tue May 21 13:21:07 2024 +0200

    Revamp the performance-related website content (#2585)
---
 src/site/antora/antora.tmpl.yml                    |   1 +
 src/site/antora/antora.yml                         |   1 +
 .../images/AsyncWithLocationThrpt1T-labeled.png    | Bin 23273 -> 0 bytes
 .../Log4j2AppenderThroughputComparison-linux.png   | Bin 37494 -> 0 bytes
 .../Log4j2AppenderThroughputComparison-windows.png | Bin 36089 -> 0 bytes
 .../ROOT/images/MarkerFilterCostComparison.png     | Bin 22737 -> 0 bytes
 .../modules/ROOT/images/ParamMsgThrpt1-4T.png      | Bin 37465 -> 0 bytes
 .../ResponseTimeAsyncClassicVsGcFree-label.png     | Bin 31148 -> 0 bytes
 .../ResponseTimeAsyncLogging16Threads_8kEach.png   | Bin 29429 -> 0 bytes
 ...ncLogging16Threads_8kEachLog4j2Only-labeled.png | Bin 41663 -> 0 bytes
 .../ResponseTimeAsyncLogging4Threads_16kEach.png   | Bin 32664 -> 0 bytes
 .../images/ResponseTimeSyncClassicVsGcFree.png     | Bin 33240 -> 0 bytes
 .../ResponseTimeVsServiceTimeAsyncLoggers.png      | Bin 34779 -> 0 bytes
 .../images/SyncThroughputLoggerComparisonLinux.png | Bin 33718 -> 0 bytes
 .../SynchronousFileResponseTime2T32k-labeled.png   | Bin 31907 -> 0 bytes
 .../images/ThreadContextFilterCostComparison.png   | Bin 17874 -> 0 bytes
 .../ROOT/images/async-throughput-comparison.png    | Bin 66734 -> 0 bytes
 .../ROOT/images/async-vs-sync-throughput.png       | Bin 60459 -> 0 bytes
 .../images/garbage-free2.6-SyncThroughputLinux.png | Bin 24197 -> 0 bytes
 .../log4j-2.5-FlightRecording-thumbnail40pct.png   | Bin 57992 -> 0 bytes
 .../ROOT/images/log4j-2.5-FlightRecording.png      | Bin 68628 -> 0 bytes
 .../log4j-2.6-FlightRecording-thumbnail40pct.png   | Bin 45221 -> 0 bytes
 .../ROOT/images/log4j-2.6-FlightRecording.png      | Bin 50481 -> 0 bytes
 src/site/antora/modules/ROOT/nav.adoc              |   4 +-
 .../antora/modules/ROOT/pages/manual/async.adoc    | 170 +----
 .../modules/ROOT/pages/manual/garbagefree.adoc     | 698 +++++++--------------
 .../ROOT/pages/manual/json-template-layout.adoc    |  32 +-
 .../antora/modules/ROOT/pages/manual/layouts.adoc  | 220 ++++++-
 .../modules/ROOT/pages/manual/performance.adoc     | 655 ++++---------------
 .../ROOT/partials/manual/async-drawbacks.adoc      |  36 ++
 .../ROOT/partials/manual/garbagefree-intro.adoc    |  21 +
 .../manual/layouts-location-information.adoc       |  55 ++
 .../ROOT/partials/premature-optimization.adoc      |  24 +
 .../ROOT/partials/properties-async-logger.adoc     | 153 +++++
 .../partials/properties-garbage-collection.adoc    | 148 +++++
 .../modules/ROOT/partials/properties-meta.adoc     |  52 ++
 .../ROOT/partials/properties-thread-context.adoc   | 101 +++
 37 files changed, 1175 insertions(+), 1196 deletions(-)

diff --git a/src/site/antora/antora.tmpl.yml b/src/site/antora/antora.tmpl.yml
index 61e9a581d9..afd7e6aba4 100644
--- a/src/site/antora/antora.tmpl.yml
+++ b/src/site/antora/antora.tmpl.yml
@@ -45,6 +45,7 @@ asciidoc:
     java-target-version: "${maven.compiler.target}"
     java-compiler-version: "${minimalJavaBuildVersion}"
     logging-services-url: "https://logging.apache.org";
+    lmax-disruptor-url: "https://lmax-exchange.github.io/disruptor";
     # Dependency versions
     commons-logging-version: "${site-commons-logging.version}"
     log4j-api-version: "${log4j-api.version}"
diff --git a/src/site/antora/antora.yml b/src/site/antora/antora.yml
index 68647cec44..50ccf7e30b 100644
--- a/src/site/antora/antora.yml
+++ b/src/site/antora/antora.yml
@@ -45,6 +45,7 @@ asciidoc:
     java-target-version: "17"
     java-compiler-version: "[17,18)"
     logging-services-url: "https://logging.apache.org";
+    lmax-disruptor-url: "https://lmax-exchange.github.io/disruptor";
     # Dependency versions
     commons-logging-version: "1.4.0"
     log4j-api-version: "1.2.3-api"
diff --git 
a/src/site/antora/modules/ROOT/images/AsyncWithLocationThrpt1T-labeled.png 
b/src/site/antora/modules/ROOT/images/AsyncWithLocationThrpt1T-labeled.png
deleted file mode 100644
index 484b33b805..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/AsyncWithLocationThrpt1T-labeled.png and 
/dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-linux.png
 
b/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-linux.png
deleted file mode 100644
index 4bb18a7807..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-linux.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-windows.png
 
b/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-windows.png
deleted file mode 100644
index 678dc9572a..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/Log4j2AppenderThroughputComparison-windows.png
 and /dev/null differ
diff --git a/src/site/antora/modules/ROOT/images/MarkerFilterCostComparison.png 
b/src/site/antora/modules/ROOT/images/MarkerFilterCostComparison.png
deleted file mode 100644
index ae48521b3f..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/MarkerFilterCostComparison.png and 
/dev/null differ
diff --git a/src/site/antora/modules/ROOT/images/ParamMsgThrpt1-4T.png 
b/src/site/antora/modules/ROOT/images/ParamMsgThrpt1-4T.png
deleted file mode 100644
index 9983dfe59a..0000000000
Binary files a/src/site/antora/modules/ROOT/images/ParamMsgThrpt1-4T.png and 
/dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncClassicVsGcFree-label.png
 
b/src/site/antora/modules/ROOT/images/ResponseTimeAsyncClassicVsGcFree-label.png
deleted file mode 100644
index 22994316c1..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncClassicVsGcFree-label.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEach.png
 
b/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEach.png
deleted file mode 100644
index 63e24fb7e8..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEach.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEachLog4j2Only-labeled.png
 
b/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEachLog4j2Only-labeled.png
deleted file mode 100644
index 3202c2353f..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging16Threads_8kEachLog4j2Only-labeled.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging4Threads_16kEach.png
 
b/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging4Threads_16kEach.png
deleted file mode 100644
index 87b83cbe49..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeAsyncLogging4Threads_16kEach.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeSyncClassicVsGcFree.png 
b/src/site/antora/modules/ROOT/images/ResponseTimeSyncClassicVsGcFree.png
deleted file mode 100644
index a4f19ac2a4..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeSyncClassicVsGcFree.png and 
/dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ResponseTimeVsServiceTimeAsyncLoggers.png 
b/src/site/antora/modules/ROOT/images/ResponseTimeVsServiceTimeAsyncLoggers.png
deleted file mode 100644
index 7aff3006bb..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ResponseTimeVsServiceTimeAsyncLoggers.png 
and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/SyncThroughputLoggerComparisonLinux.png 
b/src/site/antora/modules/ROOT/images/SyncThroughputLoggerComparisonLinux.png
deleted file mode 100644
index aeb36adbc8..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/SyncThroughputLoggerComparisonLinux.png 
and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/SynchronousFileResponseTime2T32k-labeled.png
 
b/src/site/antora/modules/ROOT/images/SynchronousFileResponseTime2T32k-labeled.png
deleted file mode 100644
index abce722b80..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/SynchronousFileResponseTime2T32k-labeled.png
 and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/ThreadContextFilterCostComparison.png 
b/src/site/antora/modules/ROOT/images/ThreadContextFilterCostComparison.png
deleted file mode 100644
index 1ca12a243c..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/ThreadContextFilterCostComparison.png and 
/dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/async-throughput-comparison.png 
b/src/site/antora/modules/ROOT/images/async-throughput-comparison.png
deleted file mode 100644
index 623a3f55c6..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/async-throughput-comparison.png and 
/dev/null differ
diff --git a/src/site/antora/modules/ROOT/images/async-vs-sync-throughput.png 
b/src/site/antora/modules/ROOT/images/async-vs-sync-throughput.png
deleted file mode 100644
index 75e53bce87..0000000000
Binary files a/src/site/antora/modules/ROOT/images/async-vs-sync-throughput.png 
and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/garbage-free2.6-SyncThroughputLinux.png 
b/src/site/antora/modules/ROOT/images/garbage-free2.6-SyncThroughputLinux.png
deleted file mode 100644
index 80f9b6aac8..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/garbage-free2.6-SyncThroughputLinux.png 
and /dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording-thumbnail40pct.png
 
b/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording-thumbnail40pct.png
deleted file mode 100644
index 2144149cbb..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording-thumbnail40pct.png
 and /dev/null differ
diff --git a/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording.png 
b/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording.png
deleted file mode 100644
index bd69464d43..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/log4j-2.5-FlightRecording.png and 
/dev/null differ
diff --git 
a/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording-thumbnail40pct.png
 
b/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording-thumbnail40pct.png
deleted file mode 100644
index 46ec82b42f..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording-thumbnail40pct.png
 and /dev/null differ
diff --git a/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording.png 
b/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording.png
deleted file mode 100644
index e42b1de842..0000000000
Binary files 
a/src/site/antora/modules/ROOT/images/log4j-2.6-FlightRecording.png and 
/dev/null differ
diff --git a/src/site/antora/modules/ROOT/nav.adoc 
b/src/site/antora/modules/ROOT/nav.adoc
index ec81d8cc05..026d4b60b7 100644
--- a/src/site/antora/modules/ROOT/nav.adoc
+++ b/src/site/antora/modules/ROOT/nav.adoc
@@ -48,8 +48,6 @@
 * xref:manual/layouts.adoc[]
 ** xref:manual/json-template-layout.adoc[]
 * xref:manual/filters.adoc[]
-* xref:manual/async.adoc[]
-* xref:manual/garbagefree.adoc[]
 * xref:manual/extending.adoc[]
 * xref:manual/plugins.adoc[]
 * xref:manual/customconfig.adoc[]
@@ -57,6 +55,8 @@
 * xref:manual/jmx.adoc[]
 * xref:manual/logsep.adoc[]
 * xref:manual/performance.adoc[]
+** xref:manual/async.adoc[]
+** xref:manual/garbagefree.adoc[]
 
 .References
 * xref:plugin-reference.adoc[Plugin reference]
diff --git a/src/site/antora/modules/ROOT/pages/manual/async.adoc 
b/src/site/antora/modules/ROOT/pages/manual/async.adoc
index f4dcbbb97a..054e414482 100644
--- a/src/site/antora/modules/ROOT/pages/manual/async.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/async.adoc
@@ -14,7 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 ////
-= Lock-free Asynchronous Loggers for Low-Latency Logging
+= Asynchronous loggers
 
 Asynchronous logging can improve your application's performance by executing 
the I/O operations in a separate thread.
 Log4j 2 makes a number of improvements in this area.
@@ -37,7 +37,7 @@ This section describes some of the trade-offs of asynchronous 
logging.
 
 === Benefits
 
-* Higher peak link:#Performance[throughput].
+* Higher peak performance throughput.
 With an asynchronous logger your application can log messages at 6 - 68 times 
the rate of a synchronous logger.
 +
 This is especially interesting for applications that occasionally need to log 
bursts of messages.
@@ -49,32 +49,9 @@ Asynchronous Loggers have consistently lower latency than 
synchronous loggers or
 
 === Drawbacks
 
-* Error handling.
-If a problem happens during the logging process and an exception is thrown, it 
is less easy for an asynchronous logger or appender to signal this problem to 
the application.
-This can partly be alleviated by configuring an `ExceptionHandler`, but this 
may still not cover all cases.
-For this reason, if logging is part of your business logic, for example if you 
are using Log4j as an audit logging framework, we would recommend to 
synchronously log those audit messages.
-(Note that you can still link:#MixedSync-Async[combine] them and use 
asynchronous logging for debug/trace logging in addition to synchronous logging 
for the audit trail.)
-* In some rare cases, care must be taken with mutable messages.
-Most of the time you don't need to worry about this.
-Log4 will ensure that log messages like `logger.debug("My object is {}", 
myObject)` will use the state of the `myObject` parameter at the time of the 
call to
-`logger.debug()`.
-The log message will not change even if `myObject` is modified later.
-It is safe to asynchronously log mutable objects because most
-link:../javadoc/log4j-api/org/apache/logging/log4j/message/Message.html[`Message`]
-implementations built-in to Log4j take a snapshot of the parameters.
-There are some exceptions however:
-link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[`MapMessage`]
-and
-link:../javadoc/log4j-api/org/apache/logging/log4j/message/StructuredDataMessage.html[`StructuredDataMessage`]
-are mutable by design: fields can be added to these messages after the message 
object was created.
-These messages should not be modified after they are logged with asynchronous 
loggers or asynchronous appenders; you may or may not see the modifications in 
the resulting log output.
-Similarly, custom
-link:../javadoc/log4j-api/org/apache/logging/log4j/message/Message.html[`Message`]
-implementations should be designed with asynchronous use in mind, and either 
take a snapshot of their parameters at construction time, or document their 
thread-safety characteristics.
-* If your application is running in an environment where CPU resources are 
scarce, like a machine with one CPU with a single core, starting another thread 
is not likely to give better performance.
-* If the _sustained rate_ at which your application is logging messages is 
faster than the maximum sustained throughput of the underlying appender, the 
queue will fill up and the application will end up logging at the speed of the 
slowest appender.
-If this happens, consider selecting a 
xref:manual/performance.adoc#whichAppender[faster appender], or logging less.
-If neither of these is an option, you may get better throughput and fewer 
latency spikes by logging synchronously.
+There are certain drawbacks associated with asynchronous logging:
+
+include::partial$manual/async-drawbacks.adoc[]
 
 [#AllAsync]
 == Making All Loggers Asynchronous
@@ -256,147 +233,12 @@ Synchronous loggers wait as long as possible before they 
take this stack snapsho
 If no location is required, the snapshot will never be taken.
 
 However, asynchronous loggers need to make this decision before passing the 
log message to another thread; the location information will be lost after that 
point.
-The
-xref:manual/performance.adoc#asyncLoggingWithLocation[performance impact] of 
taking a stack trace snapshot is even higher for asynchronous loggers:
+The performance impact of taking a stack trace snapshot is even higher for 
asynchronous loggers:
 logging with location is 30-100 times slower than without location.
 For this reason, asynchronous loggers and asynchronous appenders do not 
include location information by default.
 
 You can override the default behaviour in your logger or asynchronous appender 
configuration by specifying `includeLocation="true"`.
 
-[#Performance]
-== Asynchronous Logging Performance
-
-The throughput performance results below were derived from running the 
PerfTest, MTPerfTest and PerfTestDriver classes which can be found in the Log4j 
2 unit test source directory.
-For throughput tests, the methodology used was:
-
-* First, warm up the JVM by logging 200,000 log messages of 500 characters.
-* Repeat the warm-up 10 times, then wait 10 seconds for the I/O thread to 
catch up and buffers to drain.
-* Measure how long it takes to execute 256 * 1024 / threadCount calls to 
Logger.log and express the result in messages per second.
-* Repeat the test 5 times and average the results.
-
-The results below were obtained with log4j-2.0-beta5, disruptor-3.0.0.beta3, 
log4j-1.2.17 and logback-1.0.10.
-
-=== Logging Peak Throughput
-
-The graph below compares the throughput of synchronous loggers, asynchronous 
appenders and asynchronous loggers.
-This is the total throughput of all threads together.
-In the test with 64 threads, asynchronous loggers are 12 times faster than 
asynchronous appenders, and 68 times faster than synchronous loggers.
-
-Asynchronous loggers' throughput increases with the number of threads, whereas 
both synchronous loggers and asynchronous appenders have more or less constant 
throughput regardless of the number of threads that are doing the logging.
-
-image:async-vs-sync-throughput.png[Async loggers have much higher throughput 
than sync loggers.]
-
-=== Asynchronous Throughput Comparison with Other Logging Packages
-
-We also compared peak throughput of asynchronous loggers to the synchronous 
loggers and asynchronous appenders available in other logging packages, 
specifically log4j-1.2.17 and logback-1.0.10, with similar results.
-For asynchronous appenders, total logging throughput of all threads together 
remains roughly constant when adding more threads.
-Asynchronous loggers make more effective use of the multiple cores available 
on the machine in multi-threaded scenarios.
-
-image:async-throughput-comparison.png[Async loggers have the highest 
throughput.]
-
-On Solaris 10 (64bit) with JDK1.7.0_06, 4-core Xeon X5570 dual CPU @2.93Ghz 
with hyperthreading switched on (16 virtual cores):
-
-.Throughput per thread in messages/second
-[cols="h,>,>,>,>,>,>,>",options="header",]
-|=======================================================================
-|Logger |1 thread |2 threads |4 threads |8 threads |16 threads |32
-threads |64 threads
-|Log4j 2: Loggers all asynchronous |2,652,412 |909,119 |776,993 |516,365
-|239,246 |253,791 |288,997
-
-|Log4j 2: Loggers mixed sync/async |2,454,358 |839,394 |854,578 |597,913
-|261,003 |216,863 |218,937
-
-|Log4j 2: Async Appender |1,713,429 |603,019 |331,506 |149,408 |86,107
-|45,529 |23,980
-
-|Log4j1: Async Appender |2,239,664 |494,470 |221,402 |109,314 |60,580
-|31,706 |14,072
-
-|Logback: Async Appender |2,206,907 |624,082 |307,500 |160,096 |85,701
-|43,422 |21,303
-
-|Log4j 2: Synchronous |273,536 |136,523 |67,609 |34,404 |15,373 |7,903
-|4,253
-
-|Log4j1: Synchronous |326,894 |105,591 |57,036 |30,511 |13,900 |7,094
-|3,509
-
-|Logback: Synchronous |178,063 |65,000 |34,372 |16,903 |8,334 |3,985
-|1,967
-|=======================================================================
-
-On Windows 7 (64bit) with JDK1.7.0_11, 2-core Intel i5-3317u CPU @1.70Ghz with 
hyperthreading switched on (4 virtual cores):
-
-.Throughput per thread in messages/second
-[cols="h,>,>,>,>,>,>",options="header",]
-|=======================================================================
-|Logger |1 thread |2 threads |4 threads |8 threads |16 threads |32
-threads
-|Log4j 2: Loggers all asynchronous |1,715,344 |928,951 |1,045,265
-|1,509,109 |1,708,989 |773,565
-
-|Log4j 2: Loggers mixed sync/async |571,099 |1,204,774 |1,632,204
-|1,368,041 |462,093 |908,529
-
-|Log4j 2: Async Appender |1,236,548 |1,006,287 |511,571 |302,230
-|160,094 |60,152
-
-|Log4j1: Async Appender |1,373,195 |911,657 |636,899 |406,405 |202,777
-|162,964
-
-|Logback: Async Appender |1,979,515 |783,722 |582,935 |289,905 |172,463
-|133,435
-
-|Log4j 2: Synchronous |281,250 |225,731 |129,015 |66,590 |34,401 |17,347
-
-|Log4j1: Synchronous |147,824 |72,383 |32,865 |18,025 |8,937 |4,440
-
-|Logback: Synchronous |149,811 |66,301 |32,341 |16,962 |8,431 |3,610
-|=======================================================================
-
-[#Latency]
-=== Response Time Latency
-
-This section has been rewritten with the Log4j 2.6 release.
-The previous version only reported _service time_ instead of _response time_.
-See the xref:manual/performance.adoc[response time] side bar on the 
performance page on why this is too optimistic.
-Furthermore the previous version reported average latency, which does not make 
sense since latency is not a normal distribution.
-Finally, the previous version of this section only reported the maximum 
latency of up to 99.99% of the measurements, which does not tell you how bad 
the worst 0.01% were.
-This is unfortunate because often the "outliers" are all that matter when it 
comes to response time.
-From this release we will try to do better and report response time latency 
across the full range of percentages, including all the outliers.
-Our thanks to Gil Tene for his 
http://www.infoq.com/presentations/latency-response-time[How NOT to
-measure latency] presentation.
-(Now we know why this is also known as the "Oh s#@t!" presentation.)
-
-xref:manual/performance.adoc[Response time] is how long it takes to log a 
message under a certain load.
-What is often reported as latency is actually _service time_: how long it took 
to perform the operation.
-This hides the fact that a single spike in service time adds queueing delay 
for many of the subsequent operations.
-Service time is easy to measure (and often looks good on paper) but is 
irrelevant for users since it omits the time spent waiting for service.
-For this reason we report response time: service time plus wait time.
-
-The response time test results below were all derived from running the 
ResponseTimeTest class which can be found in the Log4j 2 unit test source 
directory.
-If you want to run these tests yourself, here are the command line options we 
used:
-
-* -Xms1G -Xmx1G (prevent heap resizing during the test)
-* 
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
 -DAsyncLogger.WaitStrategy=busyspin (to use Async Loggers.
-The BusySpin wait strategy reduces some jitter.)
-* *classic mode:* -Dlog4j2.enable.direct.encoders=false +
-*garbage-free mode:* -Dlog4j2.enable.direct.encoders=true
-* 
-XX:CompileCommand=dontinline,org.apache.logging.log4j.core.async.perftest.NoOpIdleStrategy::idle
-* -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps 
-XX:+PrintTenuringDistribution -XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCApplicationStoppedTime (to eyeball GC and safepoint pauses)
-
-The graph below compares response time latency of the ArrayBlockingQueue-based 
asynchronous appenders in Logback 1.1.7, Log4j 1.2.17 to the various options 
for asynchronous logging that Log4j 2.6 offers.
-Under a workload of 128,000 messages per second, using 16 threads (each 
logging at a rate of 8,000 messages per second), we see that Logback 1.1.7, 
Log4j 1.2.17 experience latency spikes that are orders of magnitude larger than 
Log4j 2.
-
-image:ResponseTimeAsyncLogging16Threads_8kEach.png[When 16 threads generate a 
total workload of 128,000 msg/sec,Logback 1.1.7 and Log4j 1.2.17 experience 
latency spikes that are orders of magnitude larger than Log4j 2]
-
-The graph below zooms in on the Log4j 2 results for the same test.
-We see that the worst-case response time is highest for the 
ArrayBlockingQueue-based Async Appender.
-xref:manual/garbagefree.adoc[Garbage-free] async loggers have the best 
response time behaviour.
-
-image:ResponseTimeAsyncLogging16Threads_8kEachLog4j2Only-labeled.png[image]
-
 [#UnderTheHood]
 == Under The Hood
 
diff --git a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc 
b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc
index 190fe191a4..c39d36810f 100644
--- a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc
@@ -14,557 +14,301 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 ////
-= Garbage-free Steady State Logging
 
-////
-Different applications have different performance requirements.
-Some only need to worry about throughput, but for many
-the most important performance consideration is latency (response time).
-Users of such applications would consider it a serious problem
-if the system becomes unresponsive for more than a few seconds, or even 
milliseconds in some cases.
-In financial trading for example predictable low latency is so important that 
it is often considered
-worthwhile to trade off some throughput in return for a consistent response 
time.
-////
+= Garbage-free logging
 
-Garbage collection pauses are a common cause of latency spikes and for
-many systems significant effort is spent on controlling these pauses.
+include::partial$manual/garbagefree-intro.adoc[]
 
-Many logging libraries, including previous versions of Log4j, allocate
-temporary objects like log event objects, Strings, char arrays, byte
-arrays and more during steady state logging. This contributes to
-pressure on the garbage collector and increases the frequency with which
-GC pauses occur.
+include::partial$premature-optimization.adoc[]
 
-From version 2.6, Log4j runs in "garbage free" mode by default where
-objects and buffers are reused and no temporary objects are allocated as
-much as possible. There is also a "low garbage" mode which is not
-completely garbage free but does not use ThreadLocal fields. This is the
-default mode when Log4j link:#Config[detects] it is running in a web
-application. Finally, it is possible to switch off all garbage-free
-logic and run in "classic mode" instead. For details, see the
-link:#Config[Configuration] section below.
+The act of logging is an interplay between the logging API (i.e., Log4j API) 
where the programmer publishes logs and a logging implementation (i.e., Log4j 
Core) where published logs get consumed; filtered, enriched, encoded, and 
written to files, databases, network sockets, etc.
+Both parties contain different features with different memory allocation 
characteristics.
+To achieve an end-to-end garbage-free logging system, they need to work hand 
in hand.
+Hence, we will discuss both:
 
-[#jfr]
-== A Contrived Example
+. xref:#Config[]
+. xref:#api[]
 
-To highlight the difference that garbage-free logging can make, we used
-Java Flight Recorder to measure a simple application that does nothing
-but log a simple string as often as possible for about 12 seconds.
+[NOTE]
+====
+Garbage-free logging is currently implemented for Log4j API and its reference 
implementation, Log4j Core.
+If you use another setup (e.g., a different logging API or implementation) 
this promise might not hold.
+====
 
-The application was configured to use Async Loggers, a RandomAccessFile
-appender and a "%d %p %c{1.} [%t] %m %ex%n" pattern layout. (Async
-Loggers used the Yield WaitStrategy.)
+[#quick]
+== Quick start
 
-Mission Control shows that with Log4j 2.5 this application allocates
-memory at a rate of about 809 MB/sec, resulting in 141 minor
-collections. Log4j 2.6 does not allocate temporary objects in this
-configuration, and as a result the same application with Log4j 2.6 has a
-memory allocation rate of 1.6 MB/sec and was GC-free with 0 (zero)
-garbage collections.
+If you want to have a garbage-free Log4j setup, but don't want to spend time 
with the associated details, you can quickly get started with the following 
instructions:
 
-.Using Log4j 2.5: memory allocation rate 809 MB/sec, 141 minor collections
-image:log4j-2.5-FlightRecording-thumbnail40pct.png[xref=image$log4j-2.5-FlightRecording.png]
+. Set the following system properties to `true`:
+** xref:#log4j2.enableThreadlocals[`log4j2.enableThreadlocals`]
+** 
xref:#log4j2.garbagefreeThreadContextMap[`log4j2.garbagefreeThreadContextMap`]
+. Use garbage-free
+** xref:#Layouts[]
+** xref:#Appenders[]
+** xref:#Filters[]
 
-.Using Log4j 2.6: no temporary object allocations, 0 (zero) garbage collections
-image:log4j-2.6-FlightRecording-thumbnail40pct.png[xref=image$log4j-2.6-FlightRecording.png]
+This should be sufficient for a majority of use cases.
+If not for yours, keep on reading.
 
 [#Config]
-== Configuration
-
-Garbage-free logging in Log4j 2.6 is partially implemented by reusing
-objects in ThreadLocal fields, and partially by reusing buffers when
-converting text to bytes.
-
-ThreadLocal fields holding non-JDK classes can cause memory leaks in web
-applications when the application server's thread pool continues to
-reference these fields after the web application is undeployed. To avoid
-causing memory leaks, Log4j will not use these ThreadLocals when it
-detects that it is used in a web application (when the
-`javax.servlet.Servlet` class is in the classpath, or when system
-property `log4j2.isWebapp` is set to "true").
-
-Some garbage-reducing functionality does not rely on ThreadLocals and is
-enabled by default for all applications: in Log4j 2.6, converting log
-events to text and text to bytes can be done by directly encoding text
-into a reused ByteBuffer without creating intermediary Strings, char
-arrays and byte arrays. So while logging is not completely garbage-free
-for web applications yet, the pressure on the garbage collector can
-still be significantly reduced.
-
-NOTE: As of version 2.6, a Log4j configuration containing a
-`<Properties>` section will result in temporary objects being created
-during steady-state logging.
-
-NOTE:  as of version 2.18.0, the default Async Logger wait strategy used by 
Log4j
-(Timeout) is garbage-free. Some of the wait strategies included in LMAX 
disruptor 3.4.4,
-especially `TimeoutBlockingWaitStrategy` and `BlockingWaitStrategy` (Block)
-are not garbage-free since they
-cause `java.util.concurrent.locks.AbstractQueuedSynchronizer$Node` objects to 
be created.
-The default wait strategy used by Log4j uses a synchronized block instead of a 
ReentrantLock to avoid this problem.
-The Yield and Sleep wait strategies are garbage-free. (For configuring 
predefined wait strategies, see
-xref:manual/systemproperties.adoc#properties-log4j-async-logger[here],
-you may also configure a xref:manual/async.adoc#WaitStrategy[custom wait 
strategy].)
-
-=== Disabling Garbage-free Logging
-
-There are two separate system properties for manually controlling the
-mechanisms Log4j uses to avoid creating temporary objects:
-
-* `log4j2.enableDirectEncoders` - if "true" (the default) log events are
-converted to text and this text is converted to bytes without creating
-temporary objects. Note: _synchronous_ logging performance may be worse
-for multi-threaded applications in this mode due to synchronization on
-the shared buffer. If your application is multi-threaded and logging
-performance is important, consider using Async Loggers.
-* The ThreadContext map is _not_ garbage-free by default, but from Log4j
-2.7 it can be configured to be garbage-free by setting system property
-`log4j2.garbagefreeThreadContextMap` to "true".
-
-Instead of system properties, the above properties can also be specified
-in a file named `log4j2.component.properties` by including this file in
-the classpath of the application. See the
-xref:manual/systemproperties.adoc[manual regarding system
-properties] for more info.
+== Log4j Core configuration
 
-[#Appenders]
-=== Supported Appenders
+In order to have a garbage-free Log4j Core, you need to
 
-The following xref:manual/appenders.adoc[appenders] are garbage-free during
-steady-state logging:
+* xref:#properties[configure it using properties],
+* and employ garbage-free xref:#Layouts[layouts], xref:Appenders[appenders], 
and xref:#Filters[filters].
 
-* Console
-* File
-* RollingFile (some temporary objects are created during file rollover)
-* RandomAccessFile
-* RollingRandomAccessFile (some temporary objects are created during
-file rollover)
-* MemoryMappedFile
+[#core-properties]
+=== Properties
 
-Any other appenders not in the above list (including AsyncAppender)
-create temporary objects during steady-state logging. Instead of
-AsyncAppender, use xref:manual/async.adoc[Async Loggers] to log asynchronously
-in a garbage-free manner.
-
-[#Filters]
-=== Supported Filters
+Garbage-free logging can be configured for Log4j Core using properties listed 
below.
+(See xref:manual/configuration.adoc[] on details how you can set these 
properties.)
 
-The following xref:manual/filters.adoc[filters] are garbage-free during
-steady-state logging:
+include::partial$manual/systemproperties/properties-meta.adoc[leveloffset=+2]
 
-* CompositeFilter (adding and removing element filters creates temporary
-objects for thread safety)
-* DynamicThresholdFilter
-* LevelRangeFilter (garbage free since 2.8)
-* MapFilter (garbage free since 2.8)
-* MarkerFilter (garbage free since 2.8)
-* StructuredDataFilter (garbage free since 2.8)
-* ThreadContextMapFilter (garbage free since 2.8)
-* ThresholdFilter (garbage free since 2.8)
-* TimeFilter (garbage free since 2.8 except when range must be recalculated 
once per day)
-
-Other filters like BurstFilter, RegexFilter and ScriptFilter are not
-trivial to make garbage free, and there is currently no plan to change
-them.
+include::partial$manual/systemproperties/properties-garbage-collection.adoc[leveloffset=+2]
 
 [#Layouts]
-=== Supported Layouts
+=== Layouts
 
-==== JsonTemplateLayout
+The following xref:manual/layouts.adoc[layouts] can be configured to run 
garbage-free during steady-state logging.
+To understand which configuration knobs exhibit what kind of allocation 
behaviour, see their dedicated pages.
 
-`JsonTemplateLayout` is garbage-free with
-xref:manual/json-template-layout.adoc#faq-garbage-free[a few exceptions].
+// Maintain the alphabetical ordering while making changes, please!
+* xref:manual/layouts.adoc#GELFLayout[`GelfLayout`]
+* xref:manual/json-template-layout.adoc#faq-garbage-free[`JsonTemplateLayout`]
+* xref:manual/layouts.adoc#PatternLayout-gcfree[`PatternLayout`]
 
-==== PatternLayout
+.Implementation notes
+[%collapsible]
+====
+Garbage-free xref:manual/layouts.adoc[layouts] need to implement the 
`Encoder<LogEvent>` interface.
+link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/StringBuilderEncoder.html[`StringBuilderEncoder`]
 helps with encoding text to bytes in a garbage-free manner.
+====
 
-PatternLayout with the following limited set of conversion patterns is
-garbage-free. Format modifiers to control such things as field width,
-padding, left and right justification will not generate garbage.
+[#Appenders]
+=== Appenders
 
-[cols="1m,2"]
-|===
-|Conversion Pattern |Description
+The following xref:manual/appenders.adoc[appenders] are garbage-free during 
steady-state logging:
 
-|%c\{precision}, %logger\{precision}
-|Logger name
+// Maintain the alphabetical ordering while making changes, please!
+* xref:manual/appenders.adoc#ConsoleAppender[`ConsoleAppender`]
+* xref:manual/appenders.adoc#FileAppender[`FileAppender`]
+* 
xref:manual/appenders.adoc#MemoryMappedFileAppender[`MemoryMappedFileAppender`]
+* 
xref:manual/appenders.adoc#RandomAccessFileAppender[`RandomAccessFileAppender`]
+* xref:manual/appenders.adoc#RollingFileAppender[`RollingFileAppender`] 
(except during rollover)
+* 
xref:manual/appenders.adoc#RollingRandomAccessFileAppender[`RollingRandomAccessFileAppender`]
 (except during rollover)
 
-|%d, %date
-a|
-Note: Only the predefined date formats are garbage-free: (millisecond
-separator may be either a comma ',' or a period '.')
+Any other appender not shared in the above list (including 
xref:manual/appenders.adoc#AsyncAppender[`AsyncAppender`]) is not garbage-free.
 
-[cols="1m,1"]
-!===
-!Pattern !Example
+.Implementation notes
+[%collapsible]
+====
+Garbage-free xref:manual/appenders.adoc[appenders] need to provide their 
xref:manual/layouts.adoc[layout] with a `ByteBufferDestination` implementation 
that the layout can directly write into.
 
-!%d\{DEFAULT}
-!2012-11-02 14:34:02,781
+[NOTE]
+=====
+`AbstractOutputStreamAppender` has been modified to make the following 
appenders garbage-free:
 
-!%d\{ISO8601}
-!2012-11-02T14:34:02,781
+* `ConsoleAppender`
+* `(Rolling)FileAppender`
+* `(Rolling)RandomAccessFileAppender`
+* `MemoryMappedFileAppender`
 
-!%d\{ISO8601_BASIC}
-!20121102T143402,781
+An effort has been made to minimize impact on custom appenders that extend 
`AbstractOutputStreamAppender`, but it is impossible to guarantee that changing 
the superclass will not impact any and all subclasses.
+Custom appenders that extend `AbstractOutputStreamAppender` should verify that 
they still function correctly.
+In case there is a problem, xref:#log4j2.enableDirectEncoders[the 
`log4j2.enableDirectEncoders` system property] can be set to `false` to revert 
to the pre-Log4j 2.6 behaviour.
+=====
+====
 
-!%d\{ABSOLUTE}
-!14:34:02,781
+[#Filters]
+=== Filters
 
-!%d\{DATE}
-!02 Nov 2012 14:34:02,781
+The following xref:manual/filters.adoc[filters] are garbage-free during 
steady-state logging:
 
-!%d\{COMPACT}
-!20121102143402781
+// Maintain the alphabetical ordering while making changes, please!
+* xref:manual/filters.adoc#CompositeFilter[`CompositeFilter`] (adding and 
removing element filters creates temporary
+objects for thread safety)
+* xref:manual/filters.adoc#DynamicThresholdFilter[`DynamicThresholdFilter`]
+* xref:manual/filters.adoc#LevelRangeFilter[`LevelRangeFilter`] (garbage-free 
since `2.8`)
+* xref:manual/filters.adoc#MapFilter[`MapFilter`] (garbage-free since `2.8`)
+* xref:manual/filters.adoc#MarkerFilter[`MarkerFilter`] (garbage-free since 
`2.8`)
+* xref:manual/filters.adoc#StructuredDataFilter[`StructuredDataFilter`] 
(garbage-free since `2.8`)
+* xref:manual/filters.adoc#ThreadContextMapFilter[`ThreadContextMapFilter]` 
(garbage-free since `2.8`)
+* xref:manual/filters.adoc#ThresholdFilter[`ThresholdFilter`] (garbage-free 
since `2.8`)
+* xref:manual/filters.adoc#TimeFilter[`TimeFilter`] (garbage-free since `2.8` 
except when range must be recalculated once per day)
 
-!%d{HH:mm:ss,SSS}
-!14:34:02,781
+Any other filter not shared in the above list is not garbage-free.
 
-!%d{dd MMM yyyy HH:mm:ss,SSS}
-!02 Nov 2012 14:34:02,781
+[#recyclers]
+=== Recyclers
 
-!%d{HH:mm:ss}{GMT+0}
-!18:34:02
+Log4j contains the _recycler_ abstraction that models the buffer-and-reuse 
strategy used to operate without any extra memory allocations at steady state.
+Recyclers implement the `RecyclerFactoryProvider` interface in `log4j-kit` (a 
dependency of `log4j-core`) and provide themselves as a `ServiceLoader`.
+Hence, you can easily implement custom recyclers and inject them using the 
`ServiceLoader` mechanism.
 
-!%d\{UNIX}
-!1351866842
+Log4j chooses a recycler using the following procedure:
 
-!%d\{UNIX_MILLIS}
-!1351866842781
-!===
+. Using `ServiceLoader` mechanism, loads all available 
`RecyclerFactoryProvider` implementations
+. Sorts providers (the lower `order` value will come first)
+. If xref:#log4j.recycler.factory[] is provided, the first provider whose name 
matching with this property value will be used. Otherwise, the first provider 
will be used.
 
-|%enc\{pattern}, %encode\{pattern}
-|Encodes special characters such as
-'\n' and HTML characters to help prevent log forging and some XSS
-attacks that could occur when displaying logs in a web browser -
-garbage-free since 2.8
+The predefined recycler factory providers are as follows:
 
-|%equals\{pattern}\{test}\{substitution},
-%equalsIgnoreCase\{pattern}\{test}\{substitution}
-|Replaces occurrences
-of 'test', a string, with its replacement 'substitution' in the string
-resulting from evaluation of the pattern - garbage-free since 2.8
+[#recyclers-dummy]
+==== Dummy recycler
 
-|%highlight\{pattern}\{style}
-|Adds ANSI colors - garbage-free since 2.7
-(unless nested pattern is not garbage free)
+[cols="1h,5"]
+|===
+| Module
+| `log4j-kit`
 
-|%K\{key}, %map\{key}, %MAP\{key}
-|Outputs the entries in a
-link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[MapMessage],
-if one is present in the event - garbage-free since 2.8.
+| Name
+| `dummy`
 
-|%m, %msg, %message
-|Log message (garbage-free unless message text
-contains '${')
+| Order
+| 900
+|===
 
-|%marker
-|The full name of the marker (including parents) - garbage-free
-since 2.8
+Does not recycle anything; all instances are freshly created.
+(Not recommended for production!
+Intended as a test utility.)
 
-|%markerSimpleName
-|The simple name of the marker (not including
-parents)
+[#recyclers-threadLocal]
+==== Thread-local recycler
 
-|%maxLen, %maxLength
-|Truncates another pattern to some max number of
-characters - garbage-free since 2.8
+[cols="1h,5"]
+|===
+| Module
+| `log4j-kit`
 
-|%n
-|The platform dependent line separator
+| Name
+| `threadLocal`
 
-|%N, %nano
-|System.nanoTime() when the event was logged
+| Order
+| `Integer.MAX_VALUE`, if `javax.servlet.Servlet` or `jakarta.servlet.Servlet` 
is in the classpath; +
+700, otherwise
+|===
 
-|%notEmpty\{pattern}, %varsNotEmpty\{pattern},
-%variablesNotEmpty\{pattern}
-|Outputs the result of evaluating the
-pattern if and only if all variables in the pattern are not empty -
-garbage-free since 2.8
+Pools objects in a fixed-size queue stored in a `ThreadLocal`.
+If pool runs out of capacity (see xref:#log4j.recycler.capacity[]), it will 
start creating fresh instances for new requests.
 
-|%p, %level
-|The level of the logging event
+Note that it is effectively disabled for servlet environments.
 
-|%r, %relative
-|The number of milliseconds elapsed since the JVM was
-started until the creation of the logging event - garbage-free since 2.8
+[#recyclers-queue]
+==== Queueing recycler
 
-|%sn, %sequenceNumber
-|A sequence number that will be incremented in
-every event - garbage-free since 2.8
+[cols="1h,5"]
+|===
+| Module
+| `log4j-kit`
 
-|%style\{pattern}{ANSI style}
-|Style the message - garbage-free since
-2.7 (unless nested pattern is not garbage free)
+| Name
+| `queue`
 
-|%T, %tid, %threadId
-|The ID of the thread that generated the logging
-event
+| Order
+| 800
+|===
+
+Pools objects in a fixed-size `ArrayBlockingQueue` queue.
+If pool runs out of capacity (see xref:#log4j.recycler.capacity[]), it will 
start creating fresh instances for new requests.
 
-|%t, %tn, %thread, %threadName
-|The name of the thread that generated
-the logging event
+[#recyclers-jctools]
+==== JCTools MPMC recycler
 
-|%tp
-|The priority of the thread that generated the logging event
+[cols="1h,5"]
+|===
+| Module
+| `log4j-jctools`
 
-|%X{key[,key2...]}, %mdc{key[,key2...]}, %MDC{key[,key2...]}
-|Outputs
-the Thread Context Map (also known as the Mapped Diagnostic Context or
-MDC) associated with the thread that generated the logging event -
-garbage-free since 2.8
+| Name
+| `jctools-mpmc`
 
-|literal text
-|Garbage-free unless literal contains '${' (variable
-substitution)
+| Order
+| 600
 |===
 
-Other PatternLayout conversion patterns, and other Layouts may be
-updated to avoid creating temporary objects in future releases. (Patches
-welcome!)
-
-NOTE: Logging exceptions and stack traces will create temporary
-objects with any layout. (However, Layouts will only create these
-temporary objects when an exception actually occurs.) We haven't figured
-out a way to log exceptions and stack traces without creating temporary
-objects. That is unfortunate, but you probably still want to log them
-when they happen.
-
-****
-NOTE: patterns containing regular expressions and lookups for property
-substitution will result in temporary objects being created during
-steady-state logging.
-
-Including location information is done by walking the stacktrace of an
-exception, which creates temporary objects, so the following patterns
-are not garbage-free:
-
-* %C, %class - Class Name
-* %F, %file - File Location
-* %l, %location - Location
-* %L, %line - Line Location
-* %M, %method - Method Location
-
-Also, the pattern converters for formatting Throwables are not
-garbage-free:
-
-* %ex, %exception, %throwable - The Throwable trace bound to the
-LoggingEvent
-* %rEx, %rException %rThrowable - Same as %ex but with wrapping
-exceptions
-* %xEx, %xException, %xThrowable - Same as %ex but with class packaging
-information
-* %u, %uuid - Creates a new random or time-based UUID while formatting
-
-****
+Pools objects in a fixed-size JCTools MPMC queue.
+If pool runs out of capacity (see xref:#log4j.recycler.capacity[]), it will 
start creating fresh instances for new requests.
+
+[#core-limitations]
+=== Limitations
+
+There are certain caveats associated with the configuration of garbage-free 
logging:
+
+[#core-limitation-properties]
+`<Properties>` section::
+
+A configuration containing 
xref:manual/configuration.adoc#PropertySubstitution[a `<Properties>` section] 
will result in temporary objects being created during steady-state logging.
+
+[#core-limitation-async-logger-wait-strategy]
+Asynchronous logger wait strategies::
+
+As of version `2.18.0`, the default xref:manual/async.adoc[asynchronous 
logger] wait strategy (i.e., `Timeout`) is garbage-free while running against 
both LMAX Disruptor 3 and 4.
+See 
xref:manual/async.adoc#log4j2.asyncLoggerWaitStrategy[`log4j2.asyncLoggerWaitStrategy`]
 for details on predefined wait strategies.
 
 [#api]
-=== API Changes
+== Log4j API usage
 
-Methods have been added to the `Logger` interface so that no vararg
-array objects are created when logging messages with up to ten
-parameters.
+xref:manual/api.adoc[Log4j API] contains several features to facilitate 
garbage-free logging:
 
-Also, methods have been added to the `Logger` interface to log
-`java.lang.CharSequence` messages. User-defined objects that implement
-the `CharSequence` interface can be logged without creating temporary
-objects: Log4j will try to turn CharSequence messages, Object messages
-and message parameters into text by appending them to a StringBuilder as
-a CharSequence. This avoids calling `toString()` on these objects.
+[#api-vararg]
+=== Parameterized message arguments
 
-An alternative is to implement the
-http://logging.apache.org/log4j/2.x/log4j-api/xref/org/apache/logging/log4j/util/StringBuilderFormattable.html[`org.apache.logging.log4j.util.StringBuilderFormattable`]
-interface. If an object is logged that implements this interface, its
-`formatTo` method is called instead of `toString()`.
+The `Logger` interface contains methods for parameterized messages up to 10 
arguments.
+Logging more than 10 parameters creates 
https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html[vararg
 arrays].
 
-[#codeImpact]
-=== Impact on Application Code: Autoboxing
+[#api-encode-custom-object]
+=== Encoding custom objects
+
+When a message parameter contains an unknown type by the layout, it will 
encode by calling `toString()` on these objects.
+Most objects don't have garbage-free `toString()` methods.
+Objects themselves can implement their own garbage-free encoders by either 
extending from 
https://docs.oracle.com/javase/8/docs/api/java/lang/CharSequence.html[Java's 
`CharSequence`] or 
link:../javadoc/log4j-api/org/apache/logging/log4j/util/StringBuilderFormattable.html[Log4j's
 `StringBuilderFormattable`].
 
-We made an effort to make logging garbage-free without requiring code
-changes in existing applications, but there is one area where this was
-not possible. When logging primitive values (i.e. int, double, boolean,
-etc.) the JVM autoboxes these primitive values to their Object wrapper
-equivalents, creating garbage.
+[#codeImpact]
+=== Avoiding autoboxing
 
-Log4j provides an `Unbox` utility to prevent autoboxing of primitive
-parameters. This utility contains a thread-local pool of reused
-`StringBuilder`s. The `Unbox.box(primitive)` methods write directly into
-a StringBuilder, and the resulting text will be copied into the final
-log message text without creating temporary objects.
+We made an effort to make logging garbage-free without requiring code changes 
in existing applications, but there is one area where this was not possible.
+https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html[When logging 
primitive values (i.e., `int`, `double`, `boolean`, etc.) the JVM autoboxes 
these primitive values to their `Object` wrapper equivalents, creating garbage.]
+Log4j provides an `Unbox` utility to prevent autoboxing of primitive 
parameters:
 
 [source,java]
 ----
 import static org.apache.logging.log4j.util.Unbox.box;
 
-// ...
-public void garbageFree() {
-    logger.debug("Prevent primitive autoboxing {} {}", box(10L), box(2.6d));
-}
+LOGGER.debug("Prevent primitive autoboxing {} {}", box(10L), box(2.6d));
 ----
 
-****
-NOTE: not all logging is garbage free. Specifically:
-
-* The ThreadContext map is not garbage-free by default, but can be
-configured to be garbage-free by setting system property
-`log4j2.garbagefreeThreadContextMap` to "true".
-* The ThreadContext stack is not garbage-free.
-* Logging more than 10 parameters creates vararg arrays.
-* Logging very large messages (more than 518 characters) when all
-loggers are Async Loggers will cause the internal StringBuilder in the
-RingBuffer to be trimmed back to their max size.
-* Logging messages containing '${': substituting a `$\{variable}` creates
-temporary objects.
-* Logging a lambda _as a parameter_
-(`logger.info("lambda value is {}", () -> callExpensiveMethod())`)
-creates a vararg array. Logging a lambda expression by itself is
-garbage-free: `logger.debug(() -> callExpensiveMethod())`.
-* The `Logger.traceEntry` and `Logger.traceExit` methods create
-temporary objects.
-* Time calculations are not garbage free when log4j2.usePreciseClock is set to 
true.
-The default is false.
-****
-
-[#Performance]
-== Performance
-
-[#Latency]
-=== Response Time Latency
-
-Response time is how long it takes to log a message under a certain
-load. What is often reported as latency is actually _service time_: how
-long it took to perform the operation. This hides the fact that a single
-spike in service time adds queueing delay for many of the subsequent
-operations. Service time is easy to measure (and often looks good on
-paper) but is irrelevant for users since it omits the time spent waiting
-for service. For this reason we report response time: service time plus
-wait time. See the xref:manual/performance.adoc[response time
-section] of the performance page for more detail.
-
-The response time test results below were all derived from running the
-ResponseTimeTest class which can be found in the Log4j 2 unit test
-source directory. If you want to run these tests yourself, here are the
-command line options we used:
-
-* -Xms1G -Xmx1G (prevent heap resizing during the test)
-* 
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
--DAsyncLogger.WaitStrategy=busyspin (to use Async Loggers. The BusySpin
-wait strategy reduces some jitter.)
-* *classic mode:* -Dlog4j2.enable.direct.encoders=false +
-*garbage-free mode:* -Dlog4j2.enable.direct.encoders=true
-* 
-XX:CompileCommand=dontinline,org.apache.logging.log4j.core.async.perftest.NoOpIdleStrategy::idle
-* -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps
--XX:+PrintTenuringDistribution -XX:+PrintGCApplicationConcurrentTime
--XX:+PrintGCApplicationStoppedTime (to eyeball GC and safepoint pauses)
-
-=== Async Loggers
-
-The graph below compares "classic" logging to garbage-free logging
-response time behaviour for Log4j's Async Loggers. In the graph, "100k"
-means logging at a sustained load of 100,000 messages/second, "800k" is
-a sustained load of 800,000 messages/second.
-
-image:ResponseTimeAsyncClassicVsGcFree-label.png[image]
-
-In *classic* mode we see numerous minor garbage collections which pause
-the application threads for 3 milliseconds or more. This quickly adds up
-to response time delays of almost 10 milliseconds. As you can see in the
-graph, increasing the load shifts the curve to the left (there are more
-spikes). This makes sense: logging more means more pressure on the
-garbage collector resulting in more minor GC pauses. We experimented a
-little with reducing the load to 50,000 or even 5000 messages/second,
-but this did not eliminate the 3 millisecond pauses, it just made them
-occur less frequently. Note that all GC pauses in this test are minor GC
-pauses. We did not see any full garbage collections.
-
-In *garbage-free* mode, maximum response time remains well below 1
-millisecond under a wide range of loads. (Max 780 us at 800,000
-messages/sec, max 407 us at 600,000 messages/sec, with the 99% around 5
-us for all loads up to 800,000 messages/sec.) Increasing or decreasing
-the load does not change the response time behaviour. We did not
-investigate the cause of the 200-300 microsecond pauses we saw in these
-tests.
-
-When we increased the load further we begin to see larger response time
-pauses for both classic and garbage-free logging. At sustained loads of
-1 million messages/second or more we start to approach the maximum
-throughput of the underlying RandomAccessFile Appender (see the
-synchronous logging throughput chart below). At these loads the
-ringbuffer starts to fill up and backpressure kicks in: attempting to
-add another message when the ringbuffer is full will block until a free
-slot becomes available. We start to see response times of tens of
-milliseconds or more; and attempting to increase the load even more
-results in larger and larger response time spikes.
-
-=== Synchronous File Logging
-
-With synchronous file logging, garbage-free logging still performs
-better than classic logging, but the difference is less pronounced.
-
-At a workload of 100,000 messages/second, classic logging max response
-time was a little over 2 milliseconds where garbage-free logging was a
-little over 1 millisecond. When the workload is increased to 300,000
-messages/second, classic logging shows response time pauses of 6
-milliseconds where the garbage-free response times were less than 3
-milliseconds. It may be possible to improve on this, we did not
-investigate further yet.
-
-image:ResponseTimeSyncClassicVsGcFree.png[image]
-
-The above results are obtained with the ResponseTimeTest class which can
-be found in the Log4j 2 unit test source directory, running on JDK
-1.8.0_45 on RHEL 6.5 (Linux 2.6.32-573.1.1.el6.x86_64) with 10-core Xeon
-CPU E5-2660 v3 @2.60GHz with hyperthreading switched on (20 virtual
-cores).
-
-[#Throughput]
-=== Classic Logging has Slightly Higher Throughput
-
-Throughput is slightly worse for garbage-free logging, compared to
-classic logging. This is true for both synchronous and asynchronous
-logging. The graph below compares the sustained throughput of
-synchronous logging to a file with Log4j 2.6 in garbage-free mode,
-classic mode and Log4j 2.5.
-
-image:garbage-free2.6-SyncThroughputLinux.png[Throughput of
-Log4j 2.6 in garbage-free mode is slightly worse than in classic mode,
-but on par with 2.5 and much better than alternatives logging libraries]
-
-The results above are obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the FileAppenderBenchmark source code in the log4j-perf-test
-module.
-
-[#UnderTheHood]
-== Under the Hood
-
-Custom Message implementations that implement
-`org.apache.logging.log4j.util.StringBuilderFormattable` can be
-converted to text by garbage-free Layouts without creating temporary
-objects. PatternLayout uses this mechanism and other layouts that
-convert LogEvents to text will likely also look for this interface.
-
-Custom Layouts that want to be garbage-free should implement the
-`Encoder<LogEvent>` interface. For custom Layouts that convert a
-LogEvent to a text representation, the
-`org.apache.logging.log4j.core.layout.StringBuilderEncoder` class may be
-useful to convert this text to bytes in a garbage-free manner.
-
-Custom Appenders that want to be garbage-free should provide their
-Layout with a `ByteBufferDestination` implementation that the Layout can
-directly write into.
-
-`AbstractOutputStreamAppender` has been modified to make the
-ConsoleAppender, (Rolling)FileAppender,
-(Rolling)RandomAccessFileAppender and MemoryMappedFileAppender
-garbage-free. An effort has been made to minimize impact on custom
-Appenders that extend `AbstractOutputStreamAppender`, but it is
-impossible to guarantee that changing the superclass will not impact any
-and all subclasses. Custom Appenders that extend
-`AbstractOutputStreamAppender` should verify that they still function
-correctly. In case there is a problem, system property
-`log4j2.enable.direct.encoders` can be set to "false" to revert to the
-pre-Log4j 2.6 behaviour.
+This utility contains a `ThreadLocal` pool of reused ``StringBuilder``s.
+The pool size is configured by xref:#log4j2.unboxRingbufferSize[the 
`log4j2.unboxRingbufferSize` system property].
+The `Unbox.box(primitive)` methods write directly into a `StringBuilder`, and 
the resulting text will be copied into the final log message text without 
creating temporary objects.
 
-////
-TODO Applications that wish to reuse custom Message instances with Async 
Loggers should let
-their Message classes implement the 
`org.apache.logging.log4j.message.ReusableMessage` interface.
-TODO This is not sufficient: see LOG4J2-1342, would be nice if we could solve 
this in a generic way.
-////
+[#api-limitations]
+=== Limitations
+
+Not all Log4j API feature set is garbage-free, specifically:
+
+* The `ThreadContext` map (aka. MDC) is not garbage-free by default, but can 
be configured to be garbage-free by setting 
xref:#log4j2.garbagefreeThreadContextMap[the 
`log4j2.garbagefreeThreadContextMap` system property] to `true`.
+* The `ThreadContext` stack is not garbage-free.
+* Logging very large messages (i.e., more than 
xref:#log4j2.maxReusableMsgSize[`log4j2.maxReusableMsgSize`] characters, which 
defaults to 518), when all loggers are xref:manual/async.adoc[asynchronous 
loggers], will cause the internal `StringBuilder` in the
+`RingBuffer` to be trimmed back to their configured maximum size.
+* Logging messages containing `$\{variable}` substitutions creates temporary 
objects.
+* Logging a lambda as a parameter:
++
+[source,java]
+----
+LOGGER.info("lambda value is {}", () -> callExpensiveMethod());
+----
++
+creates a vararg array.
+Logging a lambda expression by itself:
++
+[source,java]
+----
+LOGGER.debug(() -> callExpensiveMethod());
+----
++
+is garbage-free.
+* The 
link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html#traceEntry()[`traceEntry()`]
 and 
link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html#traceExit()[`traceExit()`]
 methods create temporary objects.
+* Time calculations are not garbage-free when the `log4j2.usePreciseClock` 
system property (defaults to `false`) is set to `true`.
diff --git 
a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc 
b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc
index 593827f68f..c373706b4a 100644
--- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc
@@ -211,7 +211,7 @@ appender.console.layout.eventTemplateUri = 
classpath:EcsLayout.json
   separating rendered ``LogEvent``s (defaults to `false` set by
   `log4j.layout.jsonTemplate.nullEventDelimiterEnabled` property)
 
-| maxStringLength
+| [[maxStringLength]] maxStringLength
 | int
 | truncate string values longer than the specified limit (defaults to 16384 set
   by `log4j.layout.jsonTemplate.maxStringLength` property)
@@ -382,7 +382,7 @@ Event template resolvers consume a `LogEvent` and render a 
certain property of
 it at the point of the JSON where they are declared. For instance, `marker`
 resolver renders the marker of the event, `level` resolver renders the level,
 and so on. An event template resolver is denoted with a special object
-containing a`$resolver` key:
+containing a `$resolver` key:
 
 .Example event template demonstrating the usage of `level` resolver
 [source,json]
@@ -396,9 +396,8 @@ containing a`$resolver` key:
 }
 ----
 
-Here `version` field will be rendered as is, while `level` field will be
-populated by the `level` resolver. That is, this template will generate JSON
-similar to the following:
+Here `version` field will be rendered as is, while `level` field will be 
populated by xref:#event-template-resolver-level[the `level` resolver].
+That is, this template will generate JSON similar to the following:
 
 .Example JSON generated from the demonstrated event template
 [source,json]
@@ -1157,6 +1156,13 @@ Resolves the fields of the `StackTraceElement` returned 
by
 Note that this resolver is toggled by
 `log4j.layout.jsonTemplate.locationInfoEnabled` property.
 
+[WARNING]
+====
+Mind that capturing the source location information is an expensive operation, 
and is not garbage-free.
+Note that xref:#event-template-resolver-logger[the `logger` resolver] can 
generally be used as a zero-cost substitute for `className`.
+See xref:manual/layouts.adoc#LocationInformation[this section of the layouts 
page] for details.
+====
+
 ====== Examples
 
 Resolve the line number:
@@ -1845,6 +1851,17 @@ alternatives.
 | ✕
 |===
 
+[#performance]
+== Performance
+
+JSON Template Layout is a heavily optimized piece of software to encode a log 
event as fast as possible.
+To get the most out of it, mind the following checklist:
+
+* Enable xref:manual/garbagefree.adoc[garbage-free logging]
+* Mind xref:#faq-garbage-free[the garbage footprint of features you use]
+* Choose a xref:#recycling-strategy[recycling strategy] that suits best to 
your deployment environment
+* Don't give too much slack to xref:#maxStringLength[`maxStringLength`] and 
try to keep it relatively tight
+
 [#faq]
 == F.A.Q.
 
@@ -1874,9 +1891,8 @@ logging.
 [#faq-garbage-free]
 === Is `JsonTemplateLayout` garbage-free?
 
-Yes, if the garbage-free layout behaviour toggling properties
-`log4j2.enableDirectEncoders` and `log4j2.garbagefreeThreadContextMap` are
-enabled. Take into account the following caveats:
+Yes, if xref:manual/garbagefree.adoc[garbage-free logging] is enabled.
+Take into account the following caveats:
 
 * The configured recycling strategy might not be
   garbage-free.
diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc 
b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc
index 214011130b..27a560c57c 100644
--- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc
@@ -1609,6 +1609,198 @@ should be used.
 </PatternLayout>
 ----
 
+[#PatternLayout-gcfree]
+=== Garbage-free configuration
+
+PatternLayout with the following limited set of conversion patterns is
+garbage-free. Format modifiers to control such things as field width,
+padding, left and right justification will not generate garbage.
+
+[cols="1m,2"]
+|===
+|Conversion Pattern |Description
+
+|%c\{precision}, %logger\{precision}
+|Logger name
+
+|%d, %date
+a|
+Note: Only the predefined date formats are garbage-free: (millisecond
+separator may be either a comma ',' or a period '.')
+
+[cols="1m,1"]
+!===
+!Pattern !Example
+
+!%d\{DEFAULT}
+!2012-11-02 14:34:02,781
+
+!%d\{ISO8601}
+!2012-11-02T14:34:02,781
+
+!%d\{ISO8601_BASIC}
+!20121102T143402,781
+
+!%d\{ABSOLUTE}
+!14:34:02,781
+
+!%d\{DATE}
+!02 Nov 2012 14:34:02,781
+
+!%d\{COMPACT}
+!20121102143402781
+
+!%d{HH:mm:ss,SSS}
+!14:34:02,781
+
+!%d{dd MMM yyyy HH:mm:ss,SSS}
+!02 Nov 2012 14:34:02,781
+
+!%d{HH:mm:ss}{GMT+0}
+!18:34:02
+
+!%d\{UNIX}
+!1351866842
+
+!%d\{UNIX_MILLIS}
+!1351866842781
+!===
+
+|%enc\{pattern}, %encode\{pattern}
+|Encodes special characters such as
+'\n' and HTML characters to help prevent log forging and some XSS
+attacks that could occur when displaying logs in a web browser -
+garbage-free since 2.8
+
+|%equals\{pattern}\{test}\{substitution},
+%equalsIgnoreCase\{pattern}\{test}\{substitution}
+|Replaces occurrences
+of 'test', a string, with its replacement 'substitution' in the string
+resulting from evaluation of the pattern - garbage-free since 2.8
+
+|%highlight\{pattern}\{style}
+|Adds ANSI colors - garbage-free since 2.7
+(unless nested pattern is not garbage free)
+
+|%K\{key}, %map\{key}, %MAP\{key}
+|Outputs the entries in a
+link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[MapMessage],
+if one is present in the event - garbage-free since 2.8.
+
+|%m, %msg, %message
+|Log message (garbage-free unless message text
+contains '${')
+
+|%marker
+|The full name of the marker (including parents) - garbage-free
+since 2.8
+
+|%markerSimpleName
+|The simple name of the marker (not including
+parents)
+
+|%maxLen, %maxLength
+|Truncates another pattern to some max number of
+characters - garbage-free since 2.8
+
+|%n
+|The platform dependent line separator
+
+|%N, %nano
+|System.nanoTime() when the event was logged
+
+|%notEmpty\{pattern}, %varsNotEmpty\{pattern},
+%variablesNotEmpty\{pattern}
+|Outputs the result of evaluating the
+pattern if and only if all variables in the pattern are not empty -
+garbage-free since 2.8
+
+|%p, %level
+|The level of the logging event
+
+|%r, %relative
+|The number of milliseconds elapsed since the JVM was
+started until the creation of the logging event - garbage-free since 2.8
+
+|%sn, %sequenceNumber
+|A sequence number that will be incremented in
+every event - garbage-free since 2.8
+
+|%style\{pattern}{ANSI style}
+|Style the message - garbage-free since
+2.7 (unless nested pattern is not garbage free)
+
+|%T, %tid, %threadId
+|The ID of the thread that generated the logging
+event
+
+|%t, %tn, %thread, %threadName
+|The name of the thread that generated
+the logging event
+
+|%tp
+|The priority of the thread that generated the logging event
+
+|%X{key[,key2...]}, %mdc{key[,key2...]}, %MDC{key[,key2...]}
+|Outputs
+the Thread Context Map (also known as the Mapped Diagnostic Context or
+MDC) associated with the thread that generated the logging event -
+garbage-free since 2.8
+
+|literal text
+|Garbage-free unless literal contains '${' (variable
+substitution)
+|===
+
+Other PatternLayout conversion patterns, and other Layouts may be
+updated to avoid creating temporary objects in future releases. (Patches
+welcome!)
+
+NOTE: Logging exceptions and stack traces will create temporary
+objects with any layout. (However, Layouts will only create these
+temporary objects when an exception actually occurs.) We haven't figured
+out a way to log exceptions and stack traces without creating temporary
+objects. That is unfortunate, but you probably still want to log them
+when they happen.
+
+[NOTE]
+====
+patterns containing regular expressions and lookups for property
+substitution will result in temporary objects being created during
+steady-state logging.
+
+Including location information is done by walking the stacktrace of an
+exception, which creates temporary objects, so the following patterns
+are not garbage-free:
+
+* %C, %class - Class Name
+* %F, %file - File Location
+* %l, %location - Location
+* %L, %line - Line Location
+* %M, %method - Method Location
+
+Also, the pattern converters for formatting Throwables are not
+garbage-free:
+
+* %ex, %exception, %throwable - The Throwable trace bound to the
+LoggingEvent
+* %rEx, %rException %rThrowable - Same as %ex but with wrapping
+exceptions
+* %xEx, %xException, %xThrowable - Same as %ex but with class packaging
+information
+* %u, %uuid - Creates a new random or time-based UUID while formatting
+====
+
+[#PatternLayout-performance]
+=== Performance
+
+Great effort has been put into the efficiency of Pattern Layout.
+To get the most out of it, mind the following checklist:
+
+* Enable xref:manual/garbagefree.adoc[garbage-free logging]
+* Mind xref:#PatternLayout-gcfree[the garbage footprint of features you use]
+* Don't give too much slack to 
xref:manual/configuration.adoc#log4j2.layoutStringBuilderMaxSize[`log4j2.layoutStringBuilderMaxSize`]
 and try to keep it relatively tight
+
 [#RFC5424Layout]
 == RFC5424 Layout
 
@@ -1781,28 +1973,6 @@ within the message text.
 |===
 
 [#LocationInformation]
-== Location Information
-
-If one of the layouts is configured with a location-related attribute
-like HTML link:#HtmlLocationInfo[locationInfo], or one of the patterns
-link:#PatternClass[%C or %class], link:#PatternFile[%F or %file],
-link:#PatternLocation[%l or %location], link:#PatternLine[%L or %line],
-link:#PatternMethod[%M or %method], Log4j will take a snapshot of the
-stack, and walk the stack trace to find the location information.
-
-This is an expensive operation: 1.3 - 5 times slower for synchronous
-loggers. Synchronous loggers wait as long as possible before they take
-this stack snapshot. If no location is required, the snapshot will never
-be taken.
-
-However, asynchronous loggers need to make this decision before passing
-the log message to another thread; the location information will be lost
-after that point. The
-xref:manual/performance.adoc#asyncLoggingWithLocation[performance impact] of
-taking a stack trace snapshot is even higher for asynchronous loggers:
-logging with location is 30-100 times slower than without location. For
-this reason, asynchronous loggers and asynchronous appenders do not
-include location information by default.
-
-You can override the default behaviour in your logger or asynchronous
-appender configuration by specifying `includeLocation="true"`.
+== Location information
+
+include::partial$manual/layouts-location-information.adoc[]
diff --git a/src/site/antora/modules/ROOT/pages/manual/performance.adoc 
b/src/site/antora/modules/ROOT/pages/manual/performance.adoc
index 583498f2bb..3ea2e59eea 100644
--- a/src/site/antora/modules/ROOT/pages/manual/performance.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/performance.adoc
@@ -14,550 +14,165 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 ////
+
 = Performance
 
-////
-One of the often-cited arguments against logging is its
-computational cost. This is a legitimate concern as even moderately
-sized applications can generate thousands of log requests. Much
-effort was spent measuring and tweaking logging performance. Log4j
-claims to be fast and flexible: speed first, flexibility second.
-////
+In this page we will guide you through certain steps that will show how to 
improve the performance of your Log4j configuration to serve your particular 
use case best.
+
+The act of logging is an interplay between the logging API (i.e., Log4j API) 
where the programmer publishes logs and a logging implementation (i.e., Log4j 
Core) where published logs get consumed; filtered, enriched, encoded, and 
written to files, databases, network sockets, etc.
+Both parties can have dramatic impact on performance.
+Hence, we will discuss the performance optimization of both individually:
+
+. xref:#api[Using Log4j API efficiently]
+. xref:#core[Tuning Log4j Core for performance]
+
+[#api]
+== Using Log4j API efficiently
 
-Apart from functional requirements, an important reason for selecting a
-logging library is often how well it fulfills non-functional
-requirements like reliability and performance.
-
-This page compares the performance of a number of logging frameworks
-(java.util.logging "JUL", Logback, Log4j 1.2 and Log4j 2.6), and
-documents some performance trade-offs for Log4j 2 functionality.
-
-[#benchmarks]
-== Benchmarks
-
-Performance can mean different things to different people. Common terms
-in this context are throughput and latency: _Throughput_ is a measure of
-capacity and can be expressed in a single number: how many messages can
-be logged in a certain period of time. _Response time latency_ is how
-long it takes to log a message. This cannot be expressed in a single
-number because each measurement has its own response time and we are
-often most interested in the outliers: how many there were and how large
-they were.
-
-When evaluating a logging framework's performance these may be useful
-questions to ask:
-
-[qanda]
-What is its *peak throughput*?::
-Many systems that react to external
-events need to log bursts of messages followed by periods of relative
-quiet. This number is the maximum throughput measured over a short
-period of time and gives some idea of how well the logging library deals
-with bursts. For systems that need to log a lot at a constant high rate
-(for example, batch jobs) this is less likely to be a useful measure of
-performance.
-
-What is the *maximum sustained throughput*?::
-This is the throughput
-averaged over a long time. This is a useful measure of the "upper limit"
-of the capacity of the logging library. It is not recommended that
-reactive applications actually log at this rate since under this load
-they will likely experience jitter and large response time spikes.
-
-What is its *response time* behaviour under various loads?::
-This is the
-most important question for applications that need to react to external
-events in a timely manner. Response time is the total amount of time it
-takes to log a message and is the sum of the service time and wait time.
-The *service time* is the time it takes to do the work to log the
-message. As the workload increases, the service time often varies
-little: to do X amount of work it always takes X amount of time. The
-*wait time* is how long the request had to wait in a queue before being
-serviced. _As the workload increases, wait time often grows to many
-times the service time._
-
-[#responseTimeVsServiceTime]
-=== Why Care About Response Time Latency?
-
-What is often measured and reported as _latency_ is actually _service
-time_, and omits that a service time spike adds wait time for many
-subsequent events. This may present results that are more optimistic
-than what users experience.
-
-The graph on the right illustrates how much more optimistic service time
-is than response time. The graph shows response time and service time
-for the same system under a load of 100,000 messages per second. Out of
-24 million measurements, only ~50 are more than 250 microseconds, less
-than 0.001%. In a service time-only graph this would hardly be visible.
-However, the depending on the load it will take a while to catch up
-after a spike.
-
-The response time graph shows that in reality many more events are
-impacted by these delays than the service time numbers alone would
-suggest.
-
-To learn more, watch Gil Tene's eye-opening presentation
-http://www.infoq.com/presentations/latency-response-time[How NOT to
-measure latency].
-
-image:ResponseTimeVsServiceTimeAsyncLoggers.png[image,width=480,height=288]
-
-[#loglibComparison]
-== Logging Library Performance Comparison
-
-[#asyncLogging]
-=== Asynchronous Logging - Peak Throughput Comparison
-
-Asynchronous logging is useful to deal with bursts of events. How this
-works is that a minimum amount of work is done by the application thread
-to capture all required information in a log event, and this log event
-is then put on a queue for later processing by a background thread. As
-long as the queue is sized large enough, the application threads should
-be able to spend very little time on the logging call and return to the
-business logic very quickly.
-
-It turns out that the choice of queue is extremely important for peak
-throughput. Log4j 2's Async Loggers use a
-https://lmax-exchange.github.io/disruptor/[lock-free data structure],
-whereas Logback, Log4j 1.2 and Log4j 2's Asynchronous Appenders use an
-ArrayBlockingQueue. With a blocking queue, multi-threaded applications
-often experience lock contention when trying to enqueue the log event.
-
-The graph below illustrates the difference a lock-free data structure
-can make to throughput in multi-threaded scenarios. _Log4j 2 scales
-better with the number of threads: an application with more threads can
-log more. The other logging libraries suffer from lock contention and
-total throughput stays constant or drops when more threads are logging.
-This means that with the other logging libraries, each individual thread
-will be able to log less._
-
-Bear in mind that this is _peak_ throughput: Log4j 2's Async Loggers
-give better throughput up to a point, but once the queue is full, the
-appender thread needs to wait until a slot becomes available in the
-queue, and throughput will drop to the maximum sustained throughput of
-the underlying appenders at best.
-
-image:async-throughput-comparison.png[Peak throughput comparison]
-
-For details, see the xref:manual/async.adoc[Async Loggers] manual page.
-
-[#asyncLoggingResponseTime]
-=== Asynchronous Logging Response Time
-
-Response time behaviour varies a lot with the workload and the number of
-threads that log concurrently. The xref:manual/async.adoc#Latency[Async
-Loggers] manual page and the
-xref:manual/garbagefree.adoc#Latency[garbage-free logging] manual page
-provide some graphs showing response time behaviour under various loads.
-
-This section shows another graph showing response time latency behaviour
-under a modest total workload of 64,000 messages per second, with 4
-threads logging concurrently. At this load and on this hardware/OS/JVM
-configuration, lock contention and context switches play less of a role
-and the pauses are mostly caused by minor garbage collections. Garbage
-collection pause duration and frequency can vary a lot: when testing the
-Log4j 1.2.17 Async Appender a minor GC pause of 7 milliseconds occurred
-while the Log4j 2 Async Appender test only saw a GC pause of a little
-over 2 milliseconds. This does not necessarily mean that one is better
-than the other.
-
-Generally, garbage-free async loggers had the best response time
-behaviour in all configurations we tested.
-
-image:ResponseTimeAsyncLogging4Threads_16kEach.png[Response time comparison]
-
-The above result was obtained with the ResponseTimeTest class which can
-be found in the Log4j 2 unit test source directory, running on JDK
-1.8.0_45 on RHEL 6.5 (Linux 2.6.32-573.1.1.el6.x86_64) with 10-core Xeon
-CPU E5-2660 v3 @2.60GHz with hyperthreading switched on (20 virtual
-cores).
-
-[#asyncLoggingWithParams]
-=== Asynchronous Logging Parameterized Messages
-
-Many logging libraries offer an API for logging parameterized messages.
-This enables application code to look something like this:
+Log4j API bundles a rich set of features to either totally avoid or minimize 
expensive computations whenever possible.
+We will walk you through these features with examples.
 
+[TIP]
+====
+Remember that a logging API and a logging implementation are two different 
things.
+You can use Log4j API in combination with a logging implementation other than 
Log4j Core (e.g., Logback).
+**The tips shared in this section are logging implementation agnostic.**
+====
+
+[#api-concat]
+=== Don't use string concatenation
+
+If you are using `String` concatenation while logging, you are doing something 
very wrong and dangerous!
+
+* [ ] Don't use `String` concatenation to format arguments!
+This circumvents the handling of arguments by message type and layout.
+More importantly, **this approach is prone to attacks!**
+Imagine `userId` being provided by the user with the following content:
+`placeholders for non-existing args to trigger failure: {} {} 
\{dangerousLookup}`
++
 [source,java]
 ----
-logger.debug("Entry number: {} is {}", i, entry[i]);
+/* BAD! */ LOGGER.info("failed for user ID: " + userId);
 ----
 
-In the above example, the fully formatted message text is not created
-unless the DEBUG level is enabled for the logger. Without this API, you
-would need three lines of code to accomplish the same:
+* [x] Use message parameters.
+Parameterized messages allow safe encoding of parameters and avoid formatting 
totally if the message is filtered.
+For instance, if the associated level for the logger is discarded, no 
formatting will take place.
++
+[source,java]
+----
+/* GOOD */ LOGGER.info("failed for user ID `{}`", userId);
+----
+
+[#api-supplier]
+=== Use ``Supplier``s to pass computationally expensive arguments
+
+If one or more arguments of the log statement are computationally expensive, 
it is not wise to evaluate them knowing that their results can be discarded.
+Consider the following example:
 
 [source,java]
 ----
-if (logger.isDebugEnabled()) {
-    logger.debug("Entry number: " + i + " is " + entry[i].toString());
-}
+/* BAD! */ LOGGER.info("failed for user ID `{}` and role `{}`", userId, 
db.findUserRoleById(userId));
 ----
 
-If the DEBUG level _is_ enabled, then at some point the message needs to
-be formatted. When logging asynchronously, the message parameters may be
-changed by the application thread before the background thread had a
-chance to log the message. This would show the wrong values in the log
-file. To prevent this, Log4j 2, Log4j 1.2 and Logback format the message
-text in the application thread _before_ passing off the log event to the
-background thread.
-
-This is the safe thing to do, but the formatting has a performance cost.
-The graph below compares the throughput of logging messages with
-parameters using various logging libraries. These are all asynchronous
-logging calls, so these numbers do not include the cost of disk I/O and
-represent _peak_ throughput.
-
-JUL (java.util.logging) does not have a built-in asynchronous Handler.
-https://docs.oracle.com/javase/8/docs/api/java/util/logging/MemoryHandler.html[`MemoryHandler`]
-is the nearest thing available so we included it here. MemoryHandler
-does _not_ do the safe thing of taking a snapshot of the current
-parameter state (it just keeps a reference to the original parameter
-objects), and as a result it is very fast when single-threaded. However,
-when more application threads are logging concurrently, the cost of lock
-contention outweighs this gain.
-
-In absolute numbers, _Log4j 2's Async Loggers perform well compared to
-the other logging frameworks, but notice that the message formatting
-cost increases sharply with the number of parameters. In this area,
-Log4j 2 still has work to do to improve: we would like to keep this cost
-more constant._
-
-image:ParamMsgThrpt1-4T.png[image]
-
-The results above are for JUL (java.util.logging) 1.8.0_45, Log4j 2.6,
-Log4j 1.2.17 and Logback 1.1.7, and were obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the AsyncAppenderLog4j1Benchmark,
-AsyncAppenderLog4j2Benchmark, AsyncAppenderLogbackBenchmark,
-AsyncLoggersBenchmark and the MemoryHandlerJULBenchmark source code in
-the log4j-perf-test module.
-
-[#asyncLoggingWithLocation]
-=== Asynchronous Logging with Caller Location Information
-
-Some layouts can show the class, method and line number in the
-application where the logging call was made. In Log4j 2, examples of
-such layout options are HTML
-xref:manual/layouts.adoc#HtmlLocationInfo[locationInfo], or one of the patterns
-xref:manual/layouts.adoc#PatternClass[%C or $class],
-xref:manual/layouts.adoc#PatternFile[%F or %file],
-xref:manual/layouts.adoc#PatternLocation[%l or %location],
-xref:manual/layouts.adoc#PatternLine[%L or %line],
-xref:manual/layouts.adoc#PatternMethod[%M or %method]. In order to provide
-caller location information, the logging library will take a snapshot of
-the stack, and walk the stack trace to find the location information.
-
-The graph below shows the performance impact of capturing caller
-location information when logging asynchronously from a single thread.
-Our tests show that _capturing caller location has a similar impact
-across all logging libraries, and slows down asynchronous logging by
-about 30-100x_.
-
-image:AsyncWithLocationThrpt1T-labeled.png[image]
-
-The results above are for JUL (java.util.logging) 1.8.0_45, Log4j 2.6,
-Log4j 1.2.17 and Logback 1.1.7, and were obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the AsyncAppenderLog4j1LocationBenchmark,
-AsyncAppenderLog4j2LocationBenchmark,
-AsyncAppenderLogbackLocationBenchmark, AsyncLoggersLocationBenchmark and
-the MemoryHandlerJULLocationBenchmark source code in the log4j-perf-test
-module.
-
-[#fileLoggingComparison]
-=== Synchronous File Logging - Sustained Throughput Comparison
-
-This section discusses the maximum sustained throughput of logging to a
-file. In any system, the maximum sustained throughput is determined by
-its slowest component. In the case of logging, this is the appender,
-where the message formatting and disk I/O takes place. For this reason
-we will look at simple _synchronous_ logging to a file, without queues
-or background threads.
-
-The graph below compares Log4j 2.6's RandomAccessFile appender to the
-respective File appenders of Log4j 1.2.17, Logback 1.1.7 and Java util
-logging (JUL) on Oracle Java 1.8.0_45. ImmediateFlush was set to false
-for all loggers that support this. The JUL results are for the
-XMLFormatter (which in our measurements was about twice as fast as the
-SimpleFormatter).
-
-_Log4j 2's sustained throughput drops a little when more threads are
-logging simultaneously, but its fine-grained locking pays off and
-throughput stays relatively high. The other logging frameworks'
-throughput drops dramatically in multi-threaded applications: Log4j 1.2
-has 1/4th of its single-threaded capacity, Logback has 1/10th of its
-single-threaded capacity, and JUL steadily drops from 1/4th to 1/10th of
-its single-threaded throughput as more threads are added._
-
-image:SyncThroughputLoggerComparisonLinux.png[image]
-
-The synchronous logging throughput results above are obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the FileAppenderBenchmark source code in the log4j-perf-test
-module.
-
-=== Synchronous File Logging - Response Time Comparison
-
-Response time for synchronous file logging varies a lot with the
-workload and the number of threads. Below is a sample for a workload of
-32,000 events per second, with 2 threads logging 16,000 events per
-second each.
-
-image:SynchronousFileResponseTime2T32k-labeled.png[image]
-
-The above result was obtained with the ResponseTimeTest class which can
-be found in the Log4j 2 unit test source directory, running on JDK
-1.8.0_45 on RHEL 6.5 (Linux 2.6.32-573.1.1.el6.x86_64) with 10-core Xeon
-CPU E5-2660 v3 @2.60GHz with hyperthreading switched on (20 virtual
-cores).
+The database query (i.e., `db.findUserNameById(userId)`) can be a significant 
bottleneck if the created the log event will be discarded anyway – maybe the 
`INFO` level or the associated xref:manual/markers.adoc[marker] is not accepted 
for this package, or due to some other filtering.
 
-////
-TODO
-=== Synchronous Socket Sustained Throughput Comparison
+* [ ] The old-school way of solving this problem is to level-guard the log 
statement:
++
+[source,java]
+----
+/* BAD! */ if (LOGGER.isInfoEnabled()) { LOGGER.info(...); }
+----
++
+While this would work for cases where the message can be dropped due to 
insufficient level, this approach is still prone to other filtering cases; 
e.g., maybe the associated xref:manual/markers.adoc[marker] is not accepted.
 
-=== Synchronous Syslog Sustained Throughput Comparison
-////
+* [x] Use ``Supplier``s to pass arguments containing computationally expensive 
items:
++
+[source,java]
+----
+/* GOOD */ LOGGER.info("failed for user ID `{}` and role `{}`", () -> userId, 
() -> db.findUserRoleById(userId));
+----
 
-[#filtering]
-=== Filtering by Level
-
-The most basic filtering a logging framework provides is filtering by
-log level. When logging is turned off entirely or just for a set of
-Levels, the cost of a log request consists of a number of method
-invocations plus an integer comparison. Unlike Log4j, Log4j 2 Loggers
-don't "walk a hierarchy". Loggers point directly to the Logger
-configuration that best matches the Logger's name. This incurs extra
-overhead when the Logger is first created but reduces the overhead every
-time the Logger is used.
-
-=== Advanced Filtering
-
-Both Logback and Log4j 2 support advanced filtering. Logback calls them
-TurboFilters while Log4j 2 has a single Filter object. Advanced
-filtering provides the capability to filter LogEvents using more than
-just the Level before the events are passed to Appenders. However, this
-flexibility does come with some cost. Since multi-threading can also
-have an impact on the performance of advanced filtering, the chart below
-shows the difference in performance of filtering based on a Marker or a
-Marker's parent.
-
-The "Simple Marker" comparison checks to see if a Marker that has no
-references to other markers matches the requested Marker. The "Parent
-Marker" comparison checks to see if a Marker that does have references
-to other markers matches the requested Marker.
-
-It appears that coarse-grained synchronization in SLF4J can impact
-performance in multi-threaded scenarios. See
-http://jira.qos.ch/browse/SLF4J-240[SLF4J-240].
-
-image:MarkerFilterCostComparison.png[image]
-
-Log4j and Logback also support filtering on a value in the Log4j
-ThreadContext vs filtering in Logback on a value in the MDC. The graph
-below shows that the performance difference between Log4j 2 and Logback
-is small for the ThreadContext filter.
-
-image:ThreadContextFilterCostComparison.png[image]
-
-The Filter comparison results above are obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the MarkerFilterBenchmark and MDCFilterBenchmark in the
-log4j-perf-test module for details on these benchmarks.
-
-[#tradeoffs]
-== Trade-offs
-
-[#whichAppender]
-=== Which Log4j 2 Appender to Use?
-
-Assuming that you selected Log4j 2 as your logging framework, next you
-may be interested in learning what the performance trade-offs are for
-selecting a specific Log4j 2 configuration. For example, there are three
-appenders for logging to a file: the File, RandomAccessFile and
-MemoryMappedFile appenders. Which one should you use?
-
-If performance is all you care about, the graphs below show your best
-choice is either the MemoryMappedFile appender or the RandomAccessFile
-appender. Some things to bear in mind:
-
-* MemoryMappedFile appender does not have a rolling variant yet.
-* When the log file size exceeds the MemoryMappedFile's region length,
-the file needs to be remapped. This can be a very expensive operation,
-taking several seconds if the region is large.
-* MemoryMappedFile appender creates a presized file from the beginning
-and fills it up gradually. This can confuse tools like `tail`; many such
-tools don't work very well with memory mapped files.
-* On Windows, using a tool like `tail` on a file created by
-RandomAccessFile appender can hold a lock on this file which may prevent
-Log4j from opening the file again when the application is restarted. In
-a development environment where you expect to restart your application
-regularly while using tools like tail to view the log file contents, the
-File appender may be a reasonable trade-off between performance and
-flexibility. For production environments performance may have higher
-priority.
-
-The graph below shows sustained throughput for the console and file
-appenders in Log4j 2.6, and for reference also provides the 2.5
-performance.
-
-It turns out that the garbage-free text encoding logic in 2.6 gives
-these appenders a performance boost compared to Log4j 2.5. It used to be
-that the RandomAccessFile appender was significantly faster, especially
-in multi-threaded scenarios, but with the 2.6 release the File appender
-performance has improved and the performance difference between these
-two appender is smaller.
-
-Another takeaway is just how much of a performance drag logging to the
-console can be. Considering logging to a file and using a tool like
-`tail` to watch the file change in real time.
-
-image:Log4j2AppenderThroughputComparison-linux.png[image]
-
-On Windows, the results are similar but the RandomAccessFile and
-MemoryMappedFile appenders outperform the plain File appender in
-multi-threaded scenarios. The absolute numbers are higher on Windows: we
-don't know why but it looks like Windows handles lock contention better
-than Linux.
-
-image:Log4j2AppenderThroughputComparison-windows.png[image]
-
-The Log4j 2 appender comparison results above are obtained with the
-http://openjdk.java.net/projects/code-tools/jmh/[JMH] Java benchmark
-harness. See the Log4j2AppenderComparisonBenchmark source code in the
-log4j-perf-test module.
+* [x] Use a `Supplier` to pass the message and its arguments containing 
computationally expensive items:
++
+[source,java]
+----
+/* GOOD */ LOGGER.info(() -> new ParameterizedMessage("failed for user ID `{}` 
and role `{}`", userId, db.findUserRoleById(userId)));
+----
 
-////
-The user should be aware of the following performance issues.
+[#core]
+== Tuning Log4j Core for performance
 
-=== Logging performance when logging is turned off.
+Below sections walk you through a set of features that can have significant 
impact on the performance of Log4j Core.
 
-When logging is turned off entirely or just for a set of Levels, the
-cost of a log request consists of two method invocations plus an integer
-comparison. On a 2.53 GHz Intel Core 2 Duo MacBook Pro calling
-isDebugEnabled 10 million times produces an average result in
-nanoseconds of:
+include::partial$premature-optimization.adoc[]
 
-....
-            Log4j: 4
-            Logback: 5
-            Log4j 2: 3
+[TIP]
+====
+Remember that a logging API and a logging implementation are two different 
things.
+You can use Log4j Core in combination with a logging API other than Log4j API 
(e.g., SLF4J, JUL, JPL).
+**The tips shared in this section are logging API agnostic.**
+====
 
-....
+[#layouts]
+=== Layouts
 
-The numbers above will vary slightly from run to run so the only
-conclusion that should be drawn is that all 3 frameworks perform
-similarly on this task.
+xref:manual/layouts.adoc[Layouts] are responsible for encoding a log event in 
a certain format (human-readable text, JSON, etc.) and they can have 
significant impact in your overall logging performance.
 
-However, The method invocation involves the "hidden" cost of parameter
-construction.
+[#layouts-location]
+==== Location information
 
-For example,
+include::partial$manual/layouts-location-information.adoc[]
 
-....
-              logger.debug("Entry number: " + i + " is " + 
String.valueOf(entry[i]));
+[#layouts-efficiency]
+==== Layout efficiency
 
-....
+Not all layouts are designed with the same performance considerations in mind.
+Following layouts are known to be well-optimized for performance-sensitive 
workloads:
 
-incurs the cost of constructing the message parameter, i.e. converting
-both integer `i` and `entry[i]` to a String, and concatenating
-intermediate strings, regardless of whether the message will be logged
-or not. This cost of parameter construction can be quite high and it
-depends on the size of the parameters involved. A comparison run on the
-same hardware as above yields:
-
-....
-            Log4j: 188
-            Logback: 183
-            Log4j 2: 188
-
-....
-
-Again, no conclusion should be drawn regarding relative differences
-between the frameworks on this task, but it should be obvious that it is
-considerably more expensive than simply testing the level.
-
-The best approach to avoid the cost of parameter construction is to use
-Log4j 2's formatting capabilities. For example, instead of the above
-write:
-
-....
-            logger.debug("Entry number: {} is {}", i, entry[i]);
-
-....
-
-Using this approach, a comparison run again on the same hardware
-produces:
-
-....
-            Log4j: Not supported
-            Logback: 9
-            Log4j 2: 4
-
-....
-
-These results show that the difference in performance between the call
-to isDebugEnabled and logger.debug is barely discernible.
-
-In some circumstances one of the parameters to logger.debug will be a
-costly method call that should be avoided if debugging is disabled. In
-those cases write:
-
-....
-            if(logger.isDebugEnabled() {
-                logger.debug("Entry number: " + i + " is " + 
entry[i].toString());
-            }
-
-....
-
-This will not incur the cost of whatever the toString() method needs to
-do if debugging is disabled. On the other hand, if the logger is enabled
-for the debug level, it will incur twice the cost of evaluating whether
-the logger is enabled or not: once in `isDebugEnabled` and once in
-`debug`. This is an insignificant overhead because evaluating a logger
-takes about 1% of the time it takes to actually log.
-
-Certain users resort to pre-processing or compile-time techniques to
-compile out all log statements. This leads to perfect performance
-efficiency with respect to logging. However, since the resulting
-application binary does not contain any log statements, logging cannot
-be turned on for that binary. This seems to be a disproportionate price
-to pay in exchange for a small performance gain.
-
-The performance of deciding whether to log or not to log when logging is
-turned on.
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Unlike Log4j, Log4j 2 Loggers don't "walk a hierarchy". Loggers point
-directly to the Logger configuration that best matches the Logger's
-name. This incurs extra overhead when the Logger is first created but
-reduces the overhead every time the Logger is used.
-
-=== Actually outputting log messages
-
-This is the cost of formatting the log output and sending it to its
-target destination. Here again, a serious effort was made to make
-layouts (formatters) perform as quickly as possible. The same is true
-for appenders. One of the fundamental tenets of Log4j 2 is to use
-immutable objects whenever possible and to lock at the lowest
-granularity possible. However, the cost of actually formatting and
-delivering log events will never be insignificant. For example, the
-results of writing to a simple log file using the same format using
-Log4j, Logback and Log4j 2 are:
-
-....
-              Log4j: 1651
-              Logback: 1419
-              Log4j 2.0: 1542
-
-....
-
-As with many of the other results on this page the differences between
-the frameworks above should be considered insignificant. The values will
-change somewhat on each execution and changing the order the frameworks
-are tested or adding calls to System.gc() between the tests can cause a
-variation in the reported times. However, these results show that
-actually writing out the events can be at least 1000 times more
-expensive than when they are disabled, so it is always recommended to
-take advantage of Log4j 2's fine-grained filtering capabilities.
-////
+xref:manual/json-template-layout.adoc#performance[JSON Template Layout]::
+It encodes log events into JSON according to the structure described by the 
template provided.
+Its output can safely be ingested into several log storage solutions: 
Elasticsearch, Google Cloud Logging, Graylog, Logstash, etc.
+
+xref:manual/layouts.adoc#PatternLayout-performance[Pattern Layout]::
+It encodes log events into human-readable text according to the pattern 
provided.
+
+[#async]
+=== Asynchronous logging
+
+Asynchronous logging is useful to deal with bursts of events.
+How this works is that a minimum amount of work is done by the application 
thread to capture all required information in a log event, and this log event 
is then put on a queue for later processing by a background thread.
+As long as the queue is sized large enough, the application threads should be 
able to spend very little time on the logging call and return to the business 
logic very quickly.
+
+[#async-drawbacks]
+==== Asynchronous logging drawbacks
+
+.There are certain drawbacks associated with asynchronous logging:
+[%collapsible]
+====
+include::partial$manual/async-drawbacks.adoc[]
+====
+
+[#async-strategies]
+==== Asynchronous logging strategies
+
+Log4j provides following strategies users can choose from to do asynchronous 
logging:
+
+[#async-logger]
+===== Asynchronous logger
+
+xref:manual/async.adoc[Asynchronous loggers] use {lmax-disruptor-url}[LMAX 
Disruptor] messaging library to consume log events.
+Their aim is to return from a `log()` call to the application as soon as 
possible.
+
+[#async-appender]
+===== Asynchronous appender
+
+The xref:manual/appenders.adoc#AsyncAppender[asynchronous appender] accepts 
references to other appenders and causes log events to be written to them on a 
separate thread.
+The backing queue uses `ArrayBlockingQueue` by default, though it can be 
replaced with a better performing one suitable for your use case.
+
+[#gcfree]
+=== Garbage-free logging
+
+include::partial$manual/garbagefree-intro.adoc[]
+
+See xref:manual/garbagefree.adoc[] for details.
diff --git a/src/site/antora/modules/ROOT/partials/manual/async-drawbacks.adoc 
b/src/site/antora/modules/ROOT/partials/manual/async-drawbacks.adoc
new file mode 100644
index 0000000000..e840334a73
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/manual/async-drawbacks.adoc
@@ -0,0 +1,36 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+
+Error handling::
+If a problem happens during the logging process and an exception is thrown, it 
is less easy for an asynchronous setting to signal this problem to the 
application.
+This can partly be alleviated by configuring an exception handler, but this 
may still not cover all cases.
+
+Stateful messages::
+In some rare cases, care must be taken with mutable messages.
+Most of the time you don't need to worry about this.
+Log4 will ensure that log messages like `logger.debug("My object is {}", 
myObject)` will use the state of the `myObject` parameter at the time of the 
call to `logger.debug()`.
+The log message will not change even if `myObject` is modified later.
+It is safe to asynchronously log mutable objects because most 
xref:manual/messages.adoc[built-in `Message` implementations] take a snapshot 
of the parameters.
+There are some exceptions however: 
xref:manual/messages.adoc#MapMessage[`MapMessage`] and 
xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`] are 
mutable by design, fields can be added to these messages after the message 
object was created.
+These messages should not be modified after they are logged with asynchronous 
loggers or asynchronous appenders; you may or may not see the modifications in 
the resulting log output.
+Similarly, custom `Message` implementations should be designed with 
asynchronous use in mind, and either take a snapshot of their parameters at 
construction time, or document their thread-safety characteristics.
+
+Computational overhead::
+If your application is running in an environment where CPU resources are 
scarce, like a machine with one CPU with a single core, starting another thread 
is not likely to give better performance.
+
+Appender performance::
+If the sustained rate at which your application is logging messages is faster 
than the maximum sustained throughput of the underlying appender, the queue 
will fill up and the application will end up logging at the speed of the 
slowest appender. If this happens, consider selecting a faster appender, or 
logging less. If neither of these is an option, you may get better throughput 
and fewer latency spikes by logging synchronously.
diff --git 
a/src/site/antora/modules/ROOT/partials/manual/garbagefree-intro.adoc 
b/src/site/antora/modules/ROOT/partials/manual/garbagefree-intro.adoc
new file mode 100644
index 0000000000..db3bc30852
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/manual/garbagefree-intro.adoc
@@ -0,0 +1,21 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+
+Garbage collection pauses are a common cause of latency spikes and for many 
systems significant effort is spent on controlling these pauses.
+Log4j allocates temporary `LogEvent`, `String`, `char[]`, `byte[]`, etc. 
objects during steady state logging.
+This contributes to pressure on the garbage collector and increases the 
frequency with which garbage collection pauses occur.
+In _garbage-free mode_, Log4j buffers and reuses objects to lessen this 
pressure.
diff --git 
a/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc
 
b/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc
new file mode 100644
index 0000000000..9235d0bf3e
--- /dev/null
+++ 
b/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc
@@ -0,0 +1,55 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+
+Several layouts offer directives to include the _location information_: the 
caller class, method, file, and line.
+Log4j takes a snapshot of the stack, and walks the stack trace to find the 
location information.
+**This is an expensive operation** and should be avoided in 
performance-sensitive setups.
+
+[TIP]
+====
+Note that _the caller class_ of the location information and _the logger name_ 
are two different things.
+In most setups just using _the logger name_ – which doesn't incur any overhead 
to obtain while logging! – is a sufficient and **zero-cost substitute for the 
caller class**.
+
+.Example demonstrating that the logger name can be a substitute for the caller 
name
+[%collapsible]
+=====
+[source,java]
+----
+package com.mycompany;
+
+public class PaymentService {
+
+    // Logger name: `com.mycompany.PaymentService`
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private static final class PaymentTransaction {
+        void doPayment() {
+            // Caller class: `com.mycompany.PaymentService$PaymentTransaction`
+            LOGGER.trace("...");
+        }
+    }
+
+}
+----
+
+In the above example, if _the caller class_ (which is expensive to compute!) 
is omitted in the layout, the produced log line will still be likely to contain 
sufficient information to trace back the source by just looking at _the logger 
name_.
+=====
+====
+
+Asynchronous loggers need to capture the location information before passing 
the log message to another thread; otherwise the location information will be 
lost after that point.
+Due to associated performance implications, 
xref:manual/async.adoc[asynchronous loggers] and 
xref:manual/appenders.adoc#AsyncAppender[asynchronous appenders] do **not** 
include location information by default.
+You can override the default behaviour in your asynchronous logger or 
asynchronous appender configuration.
diff --git a/src/site/antora/modules/ROOT/partials/premature-optimization.adoc 
b/src/site/antora/modules/ROOT/partials/premature-optimization.adoc
new file mode 100644
index 0000000000..f48573a3ec
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/premature-optimization.adoc
@@ -0,0 +1,24 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+
+[IMPORTANT]
+====
+Extra tuning of any application will deviate you away from defaults and add up 
to the maintenance load.
+You are strongly advised to measure your application's overall performance and 
then, if Log4j is found to be an important bottleneck factor, tune it carefully.
+When this happens, we also recommend you to evaluate your assumptions on a 
regular basis to check if they still hold.
+Remember, 
https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize[premature 
optimization is the root of all evil].
+====
diff --git a/src/site/antora/modules/ROOT/partials/properties-async-logger.adoc 
b/src/site/antora/modules/ROOT/partials/properties-async-logger.adoc
new file mode 100644
index 0000000000..3907722a49
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/properties-async-logger.adoc
@@ -0,0 +1,153 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+[id=log4j2.asyncLoggerExceptionHandler]
+== `log4j2.asyncLoggerExceptionHandler`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_EXCEPTION_HANDLER
+| Type          | 
https://lmax-exchange.github.io/disruptor/javadoc/com.lmax.disruptor/com/lmax/disruptor/ExceptionHandler.html[Class<?
 extends ExceptionHandler<? super RingBufferLogEvent>>]
+| Default value | 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/AsyncLoggerDefaultExceptionHandler[AsyncLoggerDefaultExceptionHandler]
+|===
+
+Fully qualified name of a class that implements the 
https://lmax-exchange.github.io/disruptor/javadoc/com.lmax.disruptor/com/lmax/disruptor/ExceptionHandler.html[ExceptionHandler]
 interface, which will be notified when an exception occurs while logging 
messages.
+The class needs to have a public zero-argument constructor.
+
+The default exception handler will print a message and stack trace to the 
standard error output stream.
+
+[id=log4j2.asyncLoggerRingBufferSize]
+== `log4j2.asyncLoggerRingBufferSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_RING_BUFFER_SIZE
+| Type          | `int`
+| Default value | `256 &times; 1024`
+
+(GC-free mode: `4 &times; 1024`)
+|===
+
+Size (number of slots) in the RingBuffer used by the asynchronous logging 
subsystem.
+Make this value large enough to deal with bursts of activity.
+The minimum size is 128.
+The RingBuffer will be pre-allocated at first use and will never grow or 
shrink during the life of the system.
+
+When the application is logging faster than the underlying appender can keep 
up with for a long enough time to fill up the queue, the behaviour is 
determined by the 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/AsyncQueueFullPolicy.html[AsyncQueueFullPolicy].
+
+[id=log4j2.asyncLoggerWaitStrategy]
+== `log4j2.asyncLoggerWaitStrategy`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_WAIT_STRATEGY
+| Type          | 
https://lmax-exchange.github.io/disruptor/javadoc/com.lmax.disruptor/com/lmax/disruptor/WaitStrategy.html[Class<?
 extends WaitStrategy>] or predefined constant
+| Default value | `Timeout`
+|===
+
+Specifies the 
https://lmax-exchange.github.io/disruptor/javadoc/com.lmax.disruptor/com/lmax/disruptor/WaitStrategy.html[WaitStrategy]
 used by the LMAX Disruptor.
+
+The value needs to be a fully qualified class name of a custom `WaitStrategy` 
implementation (cf. xref:manual/async.adoc#custom-waitstrategy[Custom 
WaitStrategy]) or one of the predefined constants:
+
+Block:: a strategy that uses a lock and condition variable for the I/O thread 
waiting for log events.
+Block can be used when throughput and low-latency are not as important as CPU 
resource.
+Recommended for resource constrained/virtualized environments.
+This wait strategy is not xref:manual/garbagefree.adoc[garbage free].
+
+Timeout:: a variation of the `Block` strategy that will periodically wake up 
from the lock condition `await()` call.
+This ensures that if a notification is missed somehow the consumer thread is 
not stuck but will recover with a small latency delay, see 
<<log4j2.asyncLoggerTimeout>>.
+This wait strategy is xref:manual/garbagefree.adoc[garbage free].
+
+Sleep:: a strategy that initially spins, then uses a `Thread.yield()`, and 
eventually parks for the minimum number of nanos the OS and JVM will allow 
while the I/O thread is waiting for log events (see 
<<log4j2.asyncLoggerRetries>> and <<log4j2.asyncLoggerSleepTimeNs>>).
+Sleep is a good compromise between performance and CPU resource.
+This strategy has very low impact on the application thread, in exchange for 
some additional latency for actually getting the message logged.
+This wait strategy is xref:manual/garbagefree.adoc[garbage free].
+
+Yield:: is a strategy that uses a `Thread.yield()` for waiting for log events 
after an initially spinning.
+Yield is a good compromise between performance and CPU resource, but may use 
more CPU than `Sleep` in order to get the message logged to disk sooner.
+This wait strategy is xref:manual/garbagefree.adoc[garbage free].
+
+[id=log4j2.asyncLoggerTimeout]
+== `log4j2.asyncLoggerTimeout`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_TIMEOUT
+| Type          | `int`
+| Default value | `10`
+|===
+
+Timeout in milliseconds of `Timeout` wait strategy (see 
<<log4j2.asyncLoggerWaitStrategy>>).
+
+[id=log4j2.asyncLoggerSleepTimeNs]
+== `log4j2.asyncLoggerSleepTimeNs`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_SLEEP_TIME_NS
+| Type          | `long`
+| Default value | `100`
+|===
+
+Sleep time in nanoseconds of `Sleep` wait strategy (see 
<<log4j2.asyncLoggerWaitStrategy>>).
+
+[id=log4j2.asyncLoggerRetries]
+== `log4j2.asyncLoggerRetries`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_RETRIES
+| Type          | `int`
+| Default value | `200`
+|===
+
+Total number of spin cycles and `Thread.yield()` cycles of `Sleep` (see 
<<log4j2.asyncLoggerWaitStrategy>>).
+
+[id=log4j2.asyncLoggerSynchronizeEnqueueWhenQueueFull]
+== `log4j2.asyncLoggerSynchronizeEnqueueWhenQueueFull`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL
+| Type          | `boolean`
+| Default value | `true`
+|===
+
+Synchronizes access to the Disruptor ring buffer for blocking enqueue 
operations when the queue is full.
+Users encountered excessive CPU utilization with Disruptor v3.4.2 when the 
application was logging more than the underlying appender could keep up with 
and the ring buffer became full, especially when the number of application 
threads vastly outnumbered the number of cores.
+CPU utilization is significantly reduced by restricting access to the enqueue 
operation.
+Setting this value to `false` may lead to very high CPU utilization when the 
async logging queue is full.
+
+[id=log4j2.asyncLoggerThreadNameStrategy]
+== `log4j2.asyncLoggerThreadNameStrategy`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ASYNC_LOGGER_HREAD_NAME_STRATEGY
+| Type          | 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/ThreadNameCachingStrategy[ThreadNameCachingStrategy]
 (enumeration)
+| Default value | `UNCACHED` for JRE 8u102 or later,
+
+`CACHED` otherwise
+|===
+
+Specifies the 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/ThreadNameCachingStrategy[ThreadNameCachingStrategy]
 to use to cache the result of 
https://docs.oracle.com/javase/{java-target-version}/docs/api/java/lang/Thread.html#getName--[Thread.getName()].
+
+This setting allows to cache the result of `Thread.getName()` calls and has 
two values:
+
+CACHED:: stores the name of the current thread in a `ThreadLocal` field,
+UNCACHED:: disables caching.
+
+NOTE: Since JRE 8u102 the `Thread.getName()` method does **not** allocate a 
new object.
diff --git 
a/src/site/antora/modules/ROOT/partials/properties-garbage-collection.adoc 
b/src/site/antora/modules/ROOT/partials/properties-garbage-collection.adoc
new file mode 100644
index 0000000000..92a6ebc05d
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/properties-garbage-collection.adoc
@@ -0,0 +1,148 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+[id=log4j2.enableDirectEncoders]
+== `log4j2.enableDirectEncoders`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ENABLE_DIRECT_ENCODERS
+| Type          | `boolean`
+| Default value | `true`
+|===
+
+If `true`, garbage-aware layouts will directly encode log events into 
https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html[ByteBuffer]s 
provided by appenders.
+
+This prevents allocating temporary `String` and `char[]` instances.
+
+[id=log4j2.encoderByteBufferSize]
+== `log4j2.encoderByteBufferSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ENCODER_BYTE_BUFFER_SIZE
+| Type          | `int`
+| Default value | `8192`
+|===
+
+The size in bytes of the 
link:../https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html[ByteBuffer]s
 stored in `ThreadLocal` fields by layouts and 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/StringBuilderEncoder[StringBuilderEncoder]s.
+
+This setting is only used if <<log4j2.enableDirectEncoders>> is set to `true`.
+
+[id=log4j2.encoderCharBufferSize]
+== `log4j2.encoderCharBufferSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ENCODER_CHAR_BUFFER_SIZE
+| Type          | `int`
+| Default value | `4096`
+|===
+
+The size in ``char``s of the 
link:../https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html[ByteBuffer]s
 stored in `ThreadLocal` fields 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/StringBuilderEncoder[StringBuilderEncoder]s.
+
+This setting is only used if <<log4j2.enableDirectEncoders>> is set to `true`.
+
+// tag::api[]
+
+[id=log4j2.initialReusableMsgSize]
+== `log4j2.initialReusableMsgSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_INITIAL_REUSABLE_MSG_SIZE
+| Type          | `int`
+| Default value | `128`
+|===
+
+In GC-free mode, this property determines the initial size of the reusable 
``StringBuilder``s used by 
link:../javadoc/log4j-api/org/apache/logging/log4j/message/ReusableMessage[ReusableMessages]
 for formatting purposes.
+
+[id=log4j2.maxReusableMsgSize]
+== `log4j2.maxReusableMsgSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_MAX_REUSABLE_MSG_SIZE
+| Type          | `int`
+| Default value | `518`
+|===
+
+In GC-free mode, this property determines the maximum size of the reusable 
``StringBuilder``s used by 
link:../javadoc/log4j-api/org/apache/logging/log4j/message/ReusableMessage[ReusableMessages]
 for formatting purposes.
+
+The default value allows is equal to `2 &times; (2 &times; 
log4j.initialReusableMsgSize + 2) + 2` and allows the
+`StringBuilder` to be resized twice by the current JVM resize algorithm.
+
+// end::api[]
+
+[id=log4j2.layoutStringBuilderMaxSize]
+== `log4j2.layoutStringBuilderMaxSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_LAYOUT_STRING_BUILDER_MAX_SIZE
+| Type          | `int`
+| Default value | `2048`
+|===
+
+This property determines the maximum size of the reusable ``StringBuilder``s 
used to format 
link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent[LogEvents].
+
+// tag::api[]
+
+[id=log4j2.unboxRingbufferSize]
+== `log4j2.unboxRingbufferSize`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_UNBOX_RINGBUFFER_SIZE
+| Type          | `int`
+| Default value | `32`
+|===
+
+The link:../javadoc/log4j-api/org/apache/logging/log4j/util/Unbox[Unbox] 
utility class can be used by users to format primitive values without incurring 
in the boxing allocation cost.
+
+This property specifies the maximum number of primitive arguments to a log 
message that will be cached and usually does not need to be changed.
+
+// end::api[]
+
+[id=log4j.recycler.capacity]
+== `log4j.recycler.capacity`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_RECYCLER_CAPACITY
+| Type          | `int`
+| Default value | `2C+1` (`C` denoting the number of available processors)
+|===
+
+Denotes the buffer capacity for a recycler.
+For a queueing recycler, it corresponds to the queue size.
+For a thread local recycler, it corresponds to the per-thread buffer size.
+
+If an invalid capacity is provided (i.e., if the capacity is zero or 
negative), the default value will be used.
+
+[id=log4j.recycler.factory]
+== `log4j.recycler.factory`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_RECYCLER_FACTORY
+| Type          | `String`
+| Default value | `null`
+|===
+
+If provided, available recycler factory providers will be sorted in order, and 
the first one whose name matching with this property value will be used.
+
+If missing or the selection fails, the default will be used.
diff --git a/src/site/antora/modules/ROOT/partials/properties-meta.adoc 
b/src/site/antora/modules/ROOT/partials/properties-meta.adoc
new file mode 100644
index 0000000000..088ed5d351
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/properties-meta.adoc
@@ -0,0 +1,52 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+[id=log4j2.isWebapp]
+== `log4j2.isWebapp`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_IS_WEBAPP
+| Type          | `boolean`
+| Default value | `true` if the 
https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/servlet[Servlet]
 class on classpath,
+
+`false` otherwise
+|===
+
+Setting this property to `true` switches Log4j Core into "Web application 
mode" (_"Web-app mode"_).
+
+In this mode Log4j is optimized to work in a Servlet container.
+
+This mode is incompatible with <<log4j2.enableThreadlocals>>.
+
+[id=log4j2.enableThreadlocals]
+== `log4j2.enableThreadlocals`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_ENABLE_THREADLOCALS
+| Type          | `boolean`
+| Default value | `false` if Web-app mode is enabled,
+
+`true` otherwise
+|===
+
+Setting this property to `true` switches Log4j Core into "garbage-free mode" 
(_"GC-free mode"_).
+
+In this mode Log4j uses ``ThreadLocal``s for object pooling to prevent object 
allocations.
+
+https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/lang/ThreadLocal.html[`ThreadLocal`]
 fields holding non-JDK classes can cause memory leaks in web applications when 
the application server's thread pool continues to reference these fields after 
the web application is undeployed.
+Hence, to avoid causing memory leaks, `log4j2.enableThreadlocals` by default 
reflects the opposite of xref:#log4j2.isWebapp[`log4j2.isWebapp`].
diff --git 
a/src/site/antora/modules/ROOT/partials/properties-thread-context.adoc 
b/src/site/antora/modules/ROOT/partials/properties-thread-context.adoc
new file mode 100644
index 0000000000..a3936eb2e4
--- /dev/null
+++ b/src/site/antora/modules/ROOT/partials/properties-thread-context.adoc
@@ -0,0 +1,101 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+[id=log4j2.disableThreadContext]
+== `log4j2.disableThreadContext`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_DISABLE_THREAD_CONTEXT
+| Type          | `boolean`
+| Default value | `false`
+|===
+
+If `true`, the `ThreadContext` stack and map are disabled.
+
+[id=log4j2.disableThreadContextStack]
+== `log4j2.disableThreadContextStack`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_DISABLE_THREAD_CONTEXT_STACK
+| Type          | `boolean`
+| Default value | `false`
+|===
+
+If `true`, the `ThreadContext` stack is disabled.
+
+[id=log4j2.disableThreadContextMap]
+== `log4j2.disableThreadContextMap`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_DISABLE_THREAD_CONTEXT_MAP
+| Type          | `boolean`
+| Default value | `false`
+|===
+
+If `true`, the `ThreadContext` map is disabled.
+
+// tag::gcfree[]
+
+[id=log4j2.threadContextMap]
+== `log4j2.threadContextMap`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_THREAD_CONTEXT_MAP
+| Type          | 
link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap[Class<? 
extends ThreadContextMap>] or predefined constant
+| Default value | `WebApp`
+
+(GC-free mode: `CopyOnWrite`)
+|===
+
+Fully specified class name of a custom 
link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap[ThreadContextMap]
 implementation class or one of the predefined constants:
+
+NoOp:: to disable the thread context,
+WebApp:: a web application-safe implementation, that only binds JRE classes to 
`ThreadLocal` to prevent memory leaks,
+CopyOnWrite:: a copy-on-write implementation,
+GarbageFree:: a garbage-free implementation.
+
+// end::gcfree[]
+
+[id=isThreadContextMapInheritable]
+== `log4j2.isThreadContextMapInheritable`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_IS_THREAD_CONTEXT_MAP_INHERITABLE
+| Type          | `boolean`
+| Default value | `false`
+|===
+
+If `true` uses an `InheritableThreadLocal` to copy the thread context map to 
newly created threads.
+
+// tag::gcfree[]
+
+[id=log4j2.garbagefreeThreadContextMap]
+== `log4j2.garbagefreeThreadContextMap`
+
+[cols="1h,5"]
+|===
+| Env. variable | LOG4J_GARBAGEFREE_THREAD_CONTEXT_MAP
+| Default value | `false`
+|===
+
+If set to `true` selects a garbage-free thread context map implementation.
+
+// end::gcfree[]

Reply via email to