| <?xml version="1.0"?> |
| <!-- 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. --> |
| <document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd"> |
| <properties> |
| <title>Garbage-free Steady State Logging</title> |
| <author email="rpopma@apache.org">Remko Popma</author> |
| </properties> |
| <body> |
| <section name="Garbage-free Steady State Logging"> |
| <!-- |
| <p> |
| 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. |
| </p> |
| --> |
| <p> |
| Garbage collection pauses are a common cause of latency spikes and for many systems |
| significant effort is spent on controlling these pauses. |
| </p> |
| <p> |
| 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. |
| </p> |
| <p> |
| 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 <a href="#Config">detects</a> it is running in a web application. |
| <!-- |
| </p> |
| <p> |
| --> |
| Finally, it is possible to switch off all garbage-free logic and run in "classic mode" instead. |
| For details, see the <a href="#Config">Configuration</a> section below. |
| </p> |
| <a name="jfr" /> |
| <subsection name="A Contrived Example"> |
| <p> |
| 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. |
| </p> |
| <p> |
| 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.) |
| </p> |
| <p> |
| 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. |
| </p> |
| <table> |
| <tr> |
| <td> |
| <a href="../images/log4j-2.5-FlightRecording.png"><img |
| src="../images/log4j-2.5-FlightRecording-thumbnail40pct.png" /></a><br /> |
| With Log4j 2.5: memory allocation rate 809 MB/sec, 141 minor collections. |
| </td> |
| <td> |
| <a href="../images/log4j-2.6-FlightRecording.png"><img |
| src="../images/log4j-2.6-FlightRecording-thumbnail40pct.png" /></a> |
| <br /> |
| Log4j 2.6 did not allocate temporary objects: 0 (zero) garbage collections. |
| </td> |
| </tr> |
| </table> |
| </subsection> |
| |
| <a name="Config" /> |
| <subsection name="Configuration"> |
| <p>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. |
| </p> |
| <p> |
| 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 <tt>javax.servlet.Servlet</tt> class is in the classpath, |
| or when system property <tt>log4j2.isWebapp</tt> is set to "true"). |
| </p> |
| <p> |
| 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. |
| </p> |
| <table><tr><td><p> |
| <b>Note 1:</b> as of version 2.6, a Log4j configuration containing a <tt><Properties></tt> section |
| will result in temporary objects being created during steady-state logging. |
| </p> |
| <p> |
| <b>Note 2:</b> the Async Logger Timeout wait strategy (the default) and the Block wait strategy |
| cause <tt>java.util.concurrent.locks.AbstractQueuedSynchronizer$Node</tt> objects to be created. |
| The Yield and Sleep wait strategies are garbage-free. (See |
| <a href="async.html#SysPropsAllAsync">here</a> and |
| <a href="async.html#SysPropsMixedSync-Async">here</a>.) |
| </p> |
| </td></tr></table> |
| <h4>Disabling Garbage-free Logging</h4> |
| <p> |
| There are two separate system properties for manually controlling the mechanisms Log4j uses to avoid |
| creating temporary objects: |
| </p> |
| <ul> |
| <li><tt>log4j2.enableThreadlocals</tt> - if "true" (the default for non-web applications) |
| objects are stored in ThreadLocal fields and reused, otherwise new |
| objects are created for each log event.</li> |
| <li><tt>log4j2.enableDirectEncoders</tt> - if "true" (the default) log events are converted to text and this |
| text is converted to bytes without creating temporary objects. Note: |
| <em>synchronous</em> 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. |
| </li> |
| <li>The ThreadContext map is <em>not</em> garbage-free by default, but from Log4j 2.7 it can be configured |
| to be garbage-free by setting system property <tt>log4j2.garbagefreeThreadContextMap</tt> to "true".</li> |
| </ul> |
| <p> |
| Instead of system properties, the above properties can also be specified in a file named |
| <tt>log4j2.component.properties</tt> by including this file in the classpath of the application. |
| See the <a href="configuration.html#SystemProperties">manual regarding system properties</a> for more info. |
| </p> |
| <a name="Appenders" /> |
| <h4>Supported Appenders</h4> |
| <p> |
| The following <a href="appenders.html">appenders</a> are garbage-free during steady-state logging: |
| </p> |
| <ul> |
| <li>Console</li> |
| <li>File</li> |
| <li>RollingFile (some temporary objects are created during file rollover)</li> |
| <li>RandomAccessFile</li> |
| <li>RollingRandomAccessFile (some temporary objects are created during file rollover)</li> |
| <li>MemoryMappedFile</li> |
| </ul> |
| <p> |
| Any other appenders not in the above list (including AsyncAppender) create temporary objects |
| during steady-state logging. Instead of AsyncAppender, use <a href="async.html">Async Loggers</a> |
| to log asynchronously in a garbage-free manner. |
| </p> |
| |
| <a name="Filters" /> |
| <h4>Supported Filters</h4> |
| <p> |
| The following <a href="filters.html">filters</a> are garbage-free during steady-state logging: |
| </p> |
| <ul> |
| <li>CompositeFilter (adding and removing element filters creates temporary objects for thread safety)</li> |
| <li>DynamicThresholdFilter</li> |
| <li>LevelRangeFilter (garbage free since 2.8)</li> |
| <li>MapFilter (garbage free since 2.8)</li> |
| <li>MarkerFilter (garbage free since 2.8)</li> |
| <li>StructuredDataFilter (garbage free since 2.8)</li> |
| <li>ThreadContextMapFilter (garbage free since 2.8)</li> |
| <li>ThresholdFilter (garbage free since 2.8)</li> |
| <li>TimeFilter (garbage free since 2.8 except when range must be recalculated once per day)</li> |
| </ul> |
| <p> |
| Other filters like BurstFilter, RegexFilter and ScriptFilter are not trivial to make garbage free, |
| and there is currently no plan to change them. |
| </p> |
| <a name="Layouts" /> |
| <h4>Supported Layouts</h4> |
| |
| <h5>GelfLayout</h5> |
| <p>GelfLayout is garbage-free when used with compressionType="OFF", |
| as long as no additional field contains '${' (variable substitution).</p> |
| |
| <h5>PatternLayout</h5> |
| <p> |
| 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. |
| </p> |
| <table style="width: 80%"> |
| <tr> |
| <th>Conversion Pattern</th> |
| <th>Description</th> |
| </tr> |
| <tr> |
| <td>%c{precision}, %logger{precision}</td> |
| <td>Logger name</td> |
| </tr> |
| <tr> |
| <td>%d, %date</td> |
| <td>Note: Only the predefined date formats are garbage-free: (millisecond separator may be either |
| a comma ',' or a period '.') |
| <table> |
| <tr> |
| <th>Pattern</th> |
| <th>Example</th> |
| </tr> |
| <tr> |
| <td>%d{DEFAULT}</td> |
| <td>2012-11-02 14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{ISO8601}</td> |
| <td>2012-11-02T14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{ISO8601_BASIC}</td> |
| <td>20121102T143402,781</td> |
| </tr> |
| <tr> |
| <td>%d{ABSOLUTE}</td> |
| <td>14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{DATE}</td> |
| <td>02 Nov 2012 14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{COMPACT}</td> |
| <td>20121102143402781</td> |
| </tr> |
| <tr> |
| <td>%d{HH:mm:ss,SSS}</td> |
| <td>14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{dd MMM yyyy HH:mm:ss,SSS}</td> |
| <td>02 Nov 2012 14:34:02,781</td> |
| </tr> |
| <tr> |
| <td>%d{HH:mm:ss}{GMT+0}</td> |
| <td>18:34:02</td> |
| </tr> |
| <tr> |
| <td>%d{UNIX}</td> |
| <td>1351866842</td> |
| </tr> |
| <tr> |
| <td>%d{UNIX_MILLIS}</td> |
| <td>1351866842781</td> |
| </tr> |
| </table> |
| </td> |
| </tr> |
| <tr> |
| <td>%enc{pattern}, |
| %encode{pattern}</td> |
| <td>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</td> |
| </tr> |
| <tr> |
| <td>%equals{pattern}{test}{substitution}, |
| %equalsIgnoreCase{pattern}{test}{substitution}</td> |
| <td>Replaces occurrences of 'test', a string, with its replacement 'substitution' |
| in the string resulting from evaluation of the pattern - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%highlight{pattern}{style}</td> |
| <td>Adds ANSI colors - garbage-free since 2.7 (unless nested pattern is not garbage free)</td> |
| </tr> |
| <tr> |
| <td>K{key}, map{key}, MAP{key} |
| </td> |
| <td>Outputs the entries in a |
| <a class="javadoc" href="../log4j-api/apidocs/org/apache/logging/log4j/message/MapMessage.html">MapMessage</a>, |
| if one is present in the event - garbage-free since 2.8.</td> |
| </tr> |
| <tr> |
| <td>%m, %msg, %message</td> |
| <td>Log message (garbage-free unless message text contains '${')</td> |
| </tr> |
| <tr> |
| <td>%marker</td> |
| <td>The full name of the marker (including parents) - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%markerSimpleName</td> |
| <td>The simple name of the marker (not including parents)</td> |
| </tr> |
| <tr> |
| <td>%maxLen, %maxLength</td> |
| <td>Truncates another pattern to some max number of characters - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%n</td> |
| <td>The platform dependent line separator</td> |
| </tr> |
| <tr> |
| <td>%N, %nano</td> |
| <td>System.nanoTime() when the event was logged</td> |
| </tr> |
| <tr> |
| <td>%notEmpty{pattern}, |
| %varsNotEmpty{pattern}, |
| %variablesNotEmpty{pattern} |
| </td> |
| <td>Outputs the result of evaluating the pattern if and only if |
| all variables in the pattern are not empty - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%p, %level</td> |
| <td>The level of the logging event</td> |
| </tr> |
| <tr> |
| <td>%r, |
| %relative</td> |
| <td>The number of milliseconds elapsed since the JVM was started until the creation of the logging event |
| - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%sn, %sequenceNumber</td> |
| <td>A sequence number that will be incremented in every event - garbage-free since 2.8</td> |
| </tr> |
| <tr> |
| <td>%style{pattern}{ANSI style}</td> |
| <td>Style the message - garbage-free since 2.7 (unless nested pattern is not garbage free)</td> |
| </tr> |
| <tr> |
| <td>%T, %tid, %threadId</td> |
| <td>The ID of the thread that generated the logging event</td> |
| </tr> |
| <tr> |
| <td>%t, %tn, %thread, %threadName</td> |
| <td>The name of the thread that generated the logging event</td> |
| </tr> |
| <tr> |
| <td>%tp</td> |
| <td>The priority of the thread that generated the logging event</td> |
| </tr> |
| <tr> |
| <td>%X{key[,key2...]}, |
| %mdc{key[,key2...]}, |
| %MDC{key[,key2...]}</td> |
| <td>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</td> |
| </tr> |
| <tr> |
| <td>literal text</td> |
| <td>Garbage-free unless literal contains '${' (variable substitution)</td> |
| </tr> |
| </table> |
| <p> |
| Other PatternLayout conversion patterns, and other Layouts may be updated |
| to avoid creating temporary objects in future releases. (Patches welcome!) |
| </p> |
| <p> |
| <em>Note</em>: 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. |
| </p> |
| <table><tr><td><b>Note:</b> patterns containing regular expressions and lookups for property substitution |
| will result in temporary objects being created during steady-state logging. |
| <p> |
| Including location information is done by walking the stacktrace of an exception, which creates temporary |
| objects, so the following patterns are not garbage-free: |
| </p> |
| <ul> |
| <li>%C, %class - Class Name</li> |
| <li>%F, %file - File Location</li> |
| <li>%l, %location - Location</li> |
| <li>%L, %line - Line Location</li> |
| <li>%M, %method - Method Location</li> |
| </ul> |
| Also, the pattern converters for formatting Throwables are not garbage-free: |
| <ul> |
| <li>%ex, %exception, %throwable - The Throwable trace bound to the LoggingEvent</li> |
| <li>%rEx, %rException %rThrowable - Same as %ex but with wrapping exceptions</li> |
| <li>%xEx, %xException, %xThrowable - Same as %ex but with class packaging information</li> |
| <li>%u, %uuid - Creates a new random or time-based UUID while formatting</li> |
| </ul> |
| |
| </td></tr></table> |
| <a name="api" /> |
| <h4>API Changes</h4> |
| <p> |
| Methods have been added to the <tt>Logger</tt> interface so that no vararg array objects are created |
| when logging messages with up to ten parameters. |
| </p> |
| <p> |
| Also, methods have been added to the <tt>Logger</tt> interface to log <tt>java.lang.CharSequence</tt> messages. |
| User-defined objects that implement the <tt>CharSequence</tt> 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 <tt>toString()</tt> on these objects. |
| </p> |
| <p> |
| An alternative is to implement the |
| <a href="http://logging.apache.org/log4j/2.x/log4j-api/xref/org/apache/logging/log4j/util/StringBuilderFormattable.html"><tt>org.apache.logging.log4j.util.StringBuilderFormattable</tt></a> |
| interface. |
| If an object is logged that implements this interface, its <tt>formatTo</tt> method is called instead of |
| <tt>toString()</tt>. |
| </p> |
| <p> |
| Log4j may call <tt>toString()</tt> on message and parameter objects when garbage-free logging |
| is disabled (when system property <tt>log4j2.enableThreadlocals</tt> is set to "false".) |
| </p> |
| <a name="codeImpact" /> |
| <h4>Impact on Application Code: Autoboxing</h4> |
| <p> |
| 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. |
| </p> |
| <p> |
| Log4j provides an <tt>Unbox</tt> utility to prevent autoboxing of primitive parameters. |
| This utility contains a thread-local pool of reused <tt>StringBuilder</tt>s. |
| The <tt>Unbox.box(primitive)</tt> methods write directly into a StringBuilder, and |
| the resulting text will be copied into the final log message text without creating temporary objects. |
| </p> |
| <pre class="prettyprint linenums">import static org.apache.logging.log4j.util.Unbox.box; |
| |
| ... |
| public void garbageFree() { |
| logger.debug("Prevent primitive autoboxing {} {}", box(10L), box(2.6d)); |
| } |
| </pre> |
| <table><tr><td> |
| <p> |
| <b>Note:</b> not all logging is garbage free. Specifically: |
| </p> |
| <ul> |
| <li>The ThreadContext map is not garbage-free by default, but can be configured to be garbage-free |
| by setting system property <tt>log4j2.garbagefreeThreadContextMap</tt> to "true".</li> |
| <li>The ThreadContext stack is not garbage-free.</li> |
| <li>Logging more than 10 parameters creates vararg arrays.</li> |
| <li>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. |
| </li> |
| <li>Logging messages containing '${': substituting a ${variable} creates temporary objects.</li> |
| <li>Logging a lambda <em>as a parameter</em> |
| (<tt>logger.info("lambda value is {}", () -> callExpensiveMethod())</tt>) creates a vararg array. |
| Logging a lambda expression by itself is garbage-free: |
| <tt>logger.debug(() -> callExpensiveMethod())</tt>. |
| </li> |
| <li>The <tt>Logger.traceEntry</tt> and <tt>Logger.traceExit</tt> methods create temporary objects.</li> |
| </ul> |
| </td></tr></table> |
| <p> |
| </p> |
| <p> |
| </p> |
| </subsection> |
| <a name="Performance" /> |
| <subsection name="Performance"> |
| <a name="Latency" /> |
| <h4>Response Time Latency</h4> |
| <p>Response time is how long it takes to log a message under a certain load. |
| What is often reported as latency is actually <em>service time</em>: 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 <a href="../performance.html#responseTime">response time section</a> of the performance page for more detail. |
| </p> |
| <p>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: |
| </p> |
| <ul> |
| <li>-Xms1G -Xmx1G (prevent heap resizing during the test)</li> |
| <!-- |
| <li>-XX:+UnlockDiagnosticVMOptions -XX:GuaranteedSafepointInterval=500000 (by default Hotspot schedules a |
| safepoint pause every second. Reduce jitter by postponing this for the duration of the test.)</li> |
| --> |
| <li>-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector |
| -DAsyncLogger.WaitStrategy=busyspin (to use Async Loggers. The BusySpin wait strategy reduces some jitter.)</li> |
| <li><b>classic mode: </b>-Dlog4j2.enable.threadlocals=false -Dlog4j2.enable.direct.encoders=false<br /> |
| <b>garbage-free mode: </b>-Dlog4j2.enable.threadlocals=true -Dlog4j2.enable.direct.encoders=true</li> |
| <li>-XX:CompileCommand=dontinline,org.apache.logging.log4j.core.async.perftest.NoOpIdleStrategy::idle</li> |
| <li>-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution |
| -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime (to eyeball GC and safepoint pauses)</li> |
| </ul> |
| <!-- |
| <pre style="overflow: auto; white-space: pre-wrap; word-wrap: normal;">java -Xms1G -Xmx1G -XX:+UnlockDiagnosticVMOptions -verbose:gc -XX:+PrintGCDetails |
| -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationConcurrentTime |
| -XX:+PrintGCApplicationStoppedTime -XX:GuaranteedSafepointInterval=500000 |
| -XX:CompileCommand=dontinline,org.apache.logging.log4j.core.async.perftest.NoOpIdleStrategy::idle |
| -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector |
| -Dlog4j2.enable.threadlocals=false -Dlog4j2.enable.direct.encoders=false -DAsyncLogger.WaitStrategy=busyspin |
| -cp .:HdrHistogram-2.1.8.jar:disruptor-3.3.4.jar:log4j-1.2.17.jar:slf4j-api-1.7.13.jar:slf4j-ext-1.7.13.jar: |
| logback-core-1.1.3.jar:logback-classic-1.1.3.jar:log4j-api-2.6-SNAPSHOT.jar:log4j-core-2.6-SNAPSHOT.jar: |
| log4j-core-2.6-SNAPSHOT-tests.jar org.apache.logging.log4j.core.async.perftest.ResponseTimeTest 1 50000</pre> |
| --> |
| <h4>Async Loggers</h4> |
| <p>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. |
| </p> |
| <p><img src="../images/ResponseTimeAsyncClassicVsGcFree-label.png" /></p> |
| <p>In <b>classic</b> 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.</p> |
| <p>In <b>garbage-free</b> 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. |
| </p> |
| <p> |
| 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. |
| </p> |
| <h4>Synchronous File Logging</h4> |
| <p>With synchronous file logging, garbage-free logging still performs better than classic logging, |
| but the difference is less pronounced. |
| </p> |
| <p>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.</p> |
| <p><img src="../images/ResponseTimeSyncClassicVsGcFree.png" /></p> |
| <p>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).</p> |
| |
| <a name="Throughput" /> |
| <h4>Classic Logging has Slightly Higher Throughput</h4> |
| <p>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.</p> |
| <p><img src="../images/garbage-free2.6-SyncThroughputLinux.png" |
| alt="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" /></p> |
| <p>The results above are obtained with the |
| <a href="http://openjdk.java.net/projects/code-tools/jmh/">JMH</a> Java benchmark harness. |
| See the FileAppenderBenchmark source code in the log4j-perf module.</p> |
| </subsection> |
| <a name="UnderTheHood" /> |
| <subsection name="Under the Hood"> |
| <p> |
| Custom Message implementations that implement <tt>org.apache.logging.log4j.util.StringBuilderFormattable</tt> |
| 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. |
| </p> |
| <p> |
| Custom Layouts that want to be garbage-free should implement the <tt>Encoder<LogEvent></tt> interface. |
| For custom Layouts that convert a LogEvent to a text representation, |
| the <tt>org.apache.logging.log4j.core.layout.StringBuilderEncoder</tt> class may be useful to convert this |
| text to bytes in a garbage-free manner. |
| </p> |
| <p> |
| Custom Appenders that want to be garbage-free should provide their Layout with a |
| <tt>ByteBufferDestination</tt> implementation that the Layout can directly write into. |
| </p> |
| <p> |
| <tt>AbstractOutputStreamAppender</tt> 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 |
| <tt>AbstractOutputStreamAppender</tt>, but it is impossible to guarantee that changing the superclass |
| will not impact any and all subclasses. Custom Appenders that extend |
| <tt>AbstractOutputStreamAppender</tt> should verify that they still function correctly. |
| In case there is a problem, system property <tt>log4j2.enable.direct.encoders</tt> can be set to "false" |
| to revert to the pre-Log4j 2.6 behaviour. |
| </p> |
| <!-- |
| <p> |
| TODO Applications that wish to reuse custom Message instances with Async Loggers should let |
| their Message classes implement the <tt>org.apache.logging.log4j.message.ReusableMessage</tt> interface. |
| TODO This is not sufficient: see LOG4J2-1342, would be nice if we could solve this in a generic way. |
| </p> |
| --> |
| </subsection> |
| </section> |
| </body> |
| </document> |