blob: 63ddb7dbf591dbc2b071d29b7470988585a796b4 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
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.
-->
<!-- Generated by Apache Maven Doxia at 2019-06-23 -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Log4j &#x2013; Using Log4j in Cloud Enabled Applications - Apache Log4j 2</title>
<link rel="stylesheet" href="../css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="../css/site.css" type="text/css" />
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script type="text/javascript" src="../js/bootstrap.min.js"></script>
<script type="text/javascript" src="../js/prettify.min.js"></script>
<script type="text/javascript" src="../js/site.js"></script>
<meta name="Date-Revision-yyyymmdd" content="20190623" />
<meta http-equiv="Content-Language" content="en" />
</head>
<body class="composite">
<a href="https://logging.apache.org/">
<img class="logo-left" src="../images/ls-logo.jpg" alt="Apache logging services logo" />
</a>
<img class="logo-right" src="../images/logo.png" alt="Apache log4j logo" />
<div class="clear"></div>
<div class="navbar">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="https://logging.apache.org/log4j/2.x/">Apache Log4j 2 &trade;</a>
<ul class="nav">
<li>
<a href="https://cwiki.apache.org/confluence/display/LOGGING/Log4j" class="external" target="_blank" title="Logging Wiki">Logging Wiki</a>
</li>
<li>
<a href="https://www.apache.org/" class="external" target="_blank" title="Apache">Apache</a>
</li>
<li>
<a href="../../../" title="Logging Services">Logging Services</a>
</li>
<li>
<a href="https://analysis.apache.org/dashboard/index/org.apache.logging.log4j:log4j" class="external" target="_blank" title="Sonar">Sonar</a>
</li>
<li>
<a href="https://github.com/apache/logging-log4j2" class="external" target="_blank" title="GitHub">GitHub</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<table class="layout-table">
<tr>
<td class="sidebar">
<div class="well sidebar-nav">
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-home"></i>Apache Log4j™ 2</li>
<li class="none">
<a href="../index.html" title="About">About</a>
</li>
<li class="none">
<a href="../download.html" title="Download">Download</a>
</li>
<li class="collapsed">
<a href="../javadoc.html" title="Javadoc">Javadoc</a>
</li>
<li class="collapsed">
<a href="../maven-artifacts.html" title="Maven, Ivy, Gradle Artifacts">Maven, Ivy, Gradle Artifacts</a>
</li>
<li class="none">
<a href="../runtime-dependencies.html" title="Runtime Dependencies">Runtime Dependencies</a>
</li>
<li class="none">
<a href="../changelog.html" title="Changelog">Changelog</a>
</li>
<li class="none">
<a href="../faq.html" title="FAQ">FAQ</a>
</li>
<li class="collapsed">
<a href="../performance.html" title="Performance">Performance</a>
</li>
<li class="none">
<a href="../articles.html" title="Articles and Tutorials">Articles and Tutorials</a>
</li>
<li class="none">
<a href="../thanks.html" title="Thanks">Thanks</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-pencil"></i>For Contributors</li>
<li class="none">
<a href="../build.html" title="Building Log4j from Source">Building Log4j from Source</a>
</li>
<li class="none">
<a href="../guidelines.html" title="Guidelines">Guidelines</a>
</li>
<li class="none">
<a href="../javastyle.html" title="Style Guide">Style Guide</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-book"></i>Manual</li>
<li class="none">
<a href="../manual/index.html" title="Introduction">Introduction</a>
</li>
<li class="none">
<a href="../manual/architecture.html" title="Architecture">Architecture</a>
</li>
<li class="none">
<a href="../manual/migration.html" title="Log4j 1.x Migration">Log4j 1.x Migration</a>
</li>
<li class="collapsed">
<a href="../manual/api.html" title="Java API">Java API</a>
</li>
<li class="none">
<a href="../manual/scala-api.html" title="Scala API">Scala API</a>
</li>
<li class="collapsed">
<a href="../manual/configuration.html" title="Configuration">Configuration</a>
</li>
<li class="expanded">
<a href="../manual/usage.html" title="Usage">Usage</a>
<ul>
<li class="none">
<a href="../manual/usage.html#StaticVsNonStatic" title="Static vs non-Static Loggers">Static vs non-Static Loggers</a>
</li>
<li class="none">
<a href="../manual/usage.html#LoggerVsClass" title="Logger Name vs Class Name">Logger Name vs Class Name</a>
</li>
<li class="none active">
<a href="../manual/cloud.html" title="Logging in the Cloud">Logging in the Cloud</a>
</li>
</ul>
</li>
<li class="collapsed">
<a href="../manual/webapp.html" title="Web Applications and JSPs">Web Applications and JSPs</a>
</li>
<li class="collapsed">
<a href="../manual/lookups.html" title="Lookups">Lookups</a>
</li>
<li class="collapsed">
<a href="../manual/appenders.html" title="Appenders">Appenders</a>
</li>
<li class="collapsed">
<a href="../manual/layouts.html" title="Layouts">Layouts</a>
</li>
<li class="collapsed">
<a href="../manual/filters.html" title="Filters">Filters</a>
</li>
<li class="collapsed">
<a href="../manual/async.html" title="Async Loggers">Async Loggers</a>
</li>
<li class="collapsed">
<a href="../manual/garbagefree.html" title="Garbage-free Logging">Garbage-free Logging</a>
</li>
<li class="none">
<a href="../manual/jmx.html" title="JMX">JMX</a>
</li>
<li class="none">
<a href="../manual/logsep.html" title="Logging Separation">Logging Separation</a>
</li>
<li class="collapsed">
<a href="../manual/extending.html" title="Extending Log4j">Extending Log4j</a>
</li>
<li class="collapsed">
<a href="../manual/plugins.html" title="Plugins">Plugins</a>
</li>
<li class="collapsed">
<a href="../manual/customconfig.html" title="Programmatic Log4j Configuration">Programmatic Log4j Configuration</a>
</li>
<li class="collapsed">
<a href="../manual/customloglevels.html" title="Custom Log Levels">Custom Log Levels</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-tags"></i>Related Projects</li>
<li class="none">
<a href="http://logging.apache.org/log4j/scala/index.html" class="external" target="_blank" title="Log4j-Scala">Log4j-Scala</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-tags"></i>Legacy</li>
<li class="none">
<a href="http://logging.apache.org/log4j/1.2/" class="external" target="_blank" title="Log4j 1.2">Log4j 1.2</a>
</li>
<li class="none">
<a href="http://logging.apache.org/log4j/log4j-2.3/" class="external" target="_blank" title="Log4j 2.3">Log4j 2.3</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-cog"></i>Components</li>
<li class="none">
<a href="../log4j-api/index.html" title="API">API</a>
</li>
<li class="none">
<a href="../log4j-core/index.html" title="Implementation">Implementation</a>
</li>
<li class="none">
<a href="../log4j-jcl/index.html" title="Commons Logging Bridge">Commons Logging Bridge</a>
</li>
<li class="none">
<a href="../log4j-1.2-api/index.html" title="Log4j 1.2 API">Log4j 1.2 API</a>
</li>
<li class="none">
<a href="../log4j-slf4j-impl/index.html" title="SLF4J Binding">SLF4J Binding</a>
</li>
<li class="none">
<a href="../log4j-jul/index.html" title="JUL Adapter">JUL Adapter</a>
</li>
<li class="none">
<a href="../log4j-to-slf4j/index.html" title="Log4j 2 to SLF4J Adapter">Log4j 2 to SLF4J Adapter</a>
</li>
<li class="none">
<a href="../log4j-flume-ng/index.html" title="Apache Flume Appender">Apache Flume Appender</a>
</li>
<li class="none">
<a href="../log4j-taglib/index.html" title="Log4j Tag Library">Log4j Tag Library</a>
</li>
<li class="none">
<a href="../log4j-jmx-gui/index.html" title="Log4j JMX GUI">Log4j JMX GUI</a>
</li>
<li class="none">
<a href="../log4j-web/index.html" title="Log4j Web Application Support">Log4j Web Application Support</a>
</li>
<li class="none">
<a href="../log4j-appserver/index.html" title="Log4j Application Server Integration">Log4j Application Server Integration</a>
</li>
<li class="none">
<a href="../log4j-couchdb/index.html" title="Log4j CouchDB appender">Log4j CouchDB appender</a>
</li>
<li class="none">
<a href="../log4j-mongodb2/index.html" title="Log4j MongoDB2 appender">Log4j MongoDB2 appender</a>
</li>
<li class="none">
<a href="../log4j-mongodb3/index.html" title="Log4j MongoDB3 appender">Log4j MongoDB3 appender</a>
</li>
<li class="none">
<a href="../log4j-cassandra/index.html" title="Log4j Cassandra appender">Log4j Cassandra appender</a>
</li>
<li class="none">
<a href="../log4j-iostreams/index.html" title="Log4j IO Streams">Log4j IO Streams</a>
</li>
<li class="none">
<a href="../log4j-liquibase/index.html" title="Log4j Liquibase Binding">Log4j Liquibase Binding</a>
</li>
<li class="none">
<a href="../log4j-docker/index.html" title="Log4j Docker Support">Log4j Docker Support</a>
</li>
<li class="none">
<a href="../log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html" title="Log4j Spring Cloud Config Client">Log4j Spring Cloud Config Client</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-info-sign"></i>Project Information</li>
<li class="none">
<a href="../dependency-convergence.html" title="Dependency Convergence">Dependency Convergence</a>
</li>
<li class="none">
<a href="../dependency-management.html" title="Dependency Management">Dependency Management</a>
</li>
<li class="none">
<a href="../team-list.html" title="Project Team">Project Team</a>
</li>
<li class="none">
<a href="../mail-lists.html" title="Mailing Lists">Mailing Lists</a>
</li>
<li class="none">
<a href="../issue-tracking.html" title="Issue Tracking">Issue Tracking</a>
</li>
<li class="none">
<a href="../license.html" title="Project License">Project License</a>
</li>
<li class="none">
<a href="../source-repository.html" title="Source Repository">Source Repository</a>
</li>
<li class="none">
<a href="../project-summary.html" title="Project Summary">Project Summary</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-cog"></i>Project Reports</li>
<li class="none">
<a href="../changes-report.html" title="Changes Report">Changes Report</a>
</li>
<li class="none">
<a href="../jira-report.html" title="JIRA Report">JIRA Report</a>
</li>
<li class="none">
<a href="../rat-report.html" title="RAT Report">RAT Report</a>
</li>
</ul>
</div>
<div id="poweredBy">
<a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy">
<img class="poweredBy" alt="Built by Maven" src="../images/maven-feather.png" />
</a>
</div>
</td>
<td class="content">
<!-- vim: set syn=markdown : -->
<!-- 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. --><h1>Using Log4j in Cloud Enabled Applications</h1>
<div class="section">
<h2><a name="The_Twelve-Factor_Application"></a>The Twelve-Factor Application</h2>
<p>The Logging Guidelines for <a class="externalLink" href="https://12factor.net/logs">The Twelve-Factor App</a> state that all logs should be routed unbuffered to stdout. Since this is the least common denominator it is guaranteed to work for all applications. Howeever, as with any set of general guidelines, choosing the least common denominator approach comes at a cost. Some of the costs in Java applications include:</p>
<ol style="list-style-type: decimal">
<li>Java stack traces are multi-line log messages. The standard docker log driver cannot handle these properly. See <a class="externalLink" href="https://github.com/moby/moby/issues/22920">Docker Issue #22920</a> which was closed with the message &#x201c;Don&#x2019;t Care&#x201d;. Solutions for this are to: a. Use a docker log driver that does support multi-line log message, b. Use a logging format that does not produce multi-line messages, c. Log from Log4j directly to a logging forwarder or aggregator and bypass the docker logging driver.</li>
<li>When logging to stdout in Docker, log events pass through Java&#x2019;s standard output handling which is then directed to the operating system so that the output can be piped into a file. The overhead of all this is measurably slower than just writing directly to a file as can be seen by the performance results below where logging to stdout is anywhere from 20 to 200% slower than logging directly to the file. However, these results alone would not be enough to argue against writing to the standard output stream as they only amount to about 20-30 microseconds per logging call.</li>
<li>When performing audit logging using a framework such as log4j-audit guaranteed delivery of the audit events is required. Many of the options for writing the output, including writing to the standard output stream, do not guarantee delivery. In these cases the event must be delivered to a &#x201c;forwarder&#x201d; that acknowledges receipt only when it has placed the event in durable storage, such as what Apache Flume or Apache Kafka will do.</li>
</ol></div>
<div class="section">
<h2><a name="Logging_Approaches"></a>Logging Approaches</h2>
<p>All the solutions discussed on this page are predicated with the idea that log files cannot permanently reside on the file system and that all log events should be routed to one or more log analysis tools that will be used for reporting and alerting. There are many ways to forward and collect events to be sent to the log analysis tools. </p>
<p>Note that any approach that bypasses Docker&#x2019;s logging drivers requires Log4j&#x2019;s <a href="lookups.html#DockerLookup">Docker Loookup</a> to allow Docker attributes to be injected into the log events. </p>
<div class="section">
<h3><a name="Logging_to_the_Standard_Output_Stream"></a>Logging to the Standard Output Stream</h3>
<p>As discussed above, this is the recommended 12-Factor approach for applications running in a docker container. The Log4j team does not recommend this approach if exceptions will be logged by the Java application.</p>
<p><img src="../images/DockerStdout.png" alt="Stdout" /></p></div>
<div class="section">
<h3><a name="Logging_to_the_Standard_Output_Stream_with_the_Docker_Fluentd_Logging_Driver"></a>Logging to the Standard Output Stream with the Docker Fluentd Logging Driver</h3>
<p>Docker provides alternate <a class="externalLink" href="https://docs.docker.com/config/containers/logging/configure/">logging drivers</a>, such as <a class="externalLink" href="https://docs.docker.com/config/containers/logging/gelf/">gelf</a> or <a class="externalLink" href="https://docs.docker.com/config/containers/logging/fluentd/">fluentd</a>, that can be used to redirect the standard output stream to a log forwarder or log aggregator. </p>
<p>When routing to a log forwarder it is expected that the forwarder will have the same lifetime as the application. If the forwarder should fail the management tools would be expected to also terminate other containers dependent on the forwarder.</p>
<p><img src="../images/DockerFluentd.png" alt="Docker Fluentbit" /></p>
<p>As an alternative the logging drivers could be configured to route events directly to a logging aggregator. This is generally not a good idea as the logging drivers only allow a single host and port to be configured. The docker documentation isn&#x2019;t clear but infers that log events will be dropped when log events cannot be delivered so this method should not be used if a highly available solution is required.</p>
<p><img src="../images/DockerFluentdAggregator.png" alt="Docker Fluentd" /></p></div>
<div class="section">
<h3><a name="Logging_to_a_File"></a>Logging to a File</h3>
<p>While this is not the recommended 12-Factor approach, it performs very well. However, it requires that the application declare a volume where the log files will reside and then configure the log forwarder to tail those files. Care must also be taken to automatically manage the disk space used for the logs, which Log4j can perform via the Delete action on the <a href="appenders.html#RollingFileAppender">RollingFileAppender</a>.</p>
<p><img src="../images/DockerLogFile.png" alt="File" /></p></div>
<div class="section">
<h3><a name="Sending_Directly_to_a_Log_Forwarder_via_TCP"></a>Sending Directly to a Log Forwarder via TCP</h3>
<p>Sending logs directly to a Log Forwarder is simple as it generally just requires that the forwarder&#x2019;s host and port be configured on a SocketAppender with an appropriate layout.</p>
<p><img src="../images/DockerTCP.png" alt="TCP" /></p></div>
<div class="section">
<h3><a name="Sending_Directly_to_a_Log_Aggregator_via_TCP"></a>Sending Directly to a Log Aggregator via TCP</h3>
<p>Similar to sending logs to a forwarder, logs can also be sent to a cluster of aggregators. However, setting this up is not as simple since, to be highly available, a cluster of aggregators must be used. However, the SocketAppender currently can only be configured with a single host and port. To allow for failover if the primary aggregator fails the SocketAppender must be enclosed in a <a href="appenders.html#FailoverAppender">FailoverAppender</a>, which would also have the secondary aggregator configured. Another option is to have the SocketAppender point to a highly available proxy that can forward to the Log Aggregator.</p>
<p>If the log aggregator used is Apache Flume or Apache Kafka (or similar) the Appenders for these support being configured with a list of hosts and ports so high availability is not an issue. </p>
<p><img src="../images/LoggerAggregator.png" alt="Aggregator" /></p></div></div>
<div class="section">
<h2><a name="Managing_Logging_Configuration"></a>Managing Logging Configuration</h2>
<p>Spring Boot provides another least common denominator approach to logging configuration. It will let you set the log level for various Loggers within an application which can be dynamically updated via REST endpoints provided by Spring. While this works in a lot of cases it does not support any of the more advanced filtering featurs of Log4j. For example, since it cannot add or modify any Filters other than the log level of a logger, changes cannot be made to allow all log events for a specific user or customer to temporarily be logged (see <a href="filters.html#DynamicThresholdFilter">DynamicThresholdFilter</a> or <a href="filters.html#ThreadContextMapFilter">ThreadContextMapFilter</a>) or any other kinds of changes to filters. Also, in a micro-services, clustered environment it is quite likely that these changes will need to be propagated to multiple servers at the same time. Trying to achieve this via REST calls could be difficult.</p>
<p>Since its first release Log4j has supported reconfiguration through a file. Beginning with Log4j 2.12.0 Log4j also supports accessing the configuration via HTTP(S) and monitoring the file for changes by using the HTTP &#x201c;If-Modified-Since&#x201d; header. A patch has also been integrated into Spring Cloud Config starting with versions 2.0.3 and 2.1.1 for it to honor the If-Modified-Since header. In addition, the log4j-spring-cloud-config project will listen for update events published by Spring Cloud Bus and then verify that the configuratoin file has been modified, so polling via HTTP is not required.</p>
<p>Log4j also supports composite configurations. A distributed application spread across microservices could share a common configuration file that could be used to control things like enabling debug logging for a specific user.</p>
<p>While the standard Spring Boot REST endpoints to update logging will still work any changes made by those REST endpoints will be lost if Log4j reconfigures itself do to changes in the logging configuration file.</p>
<p>Further information regarding integration of the log4j-spring-cloud-config-client can be found at <a href="../log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html">Log4j Spring Cloud Config Client</a>.</p></div>
<div class="section">
<h2><a name="Integration_with_Docker"></a>Integration with Docker</h2>
<p>Applications within a Docker container that log using a Docker logging driver can include special attributes in the formatted log event as described at <a class="externalLink" href="https://docs.docker.com/config/containers/logging/log_tags/">Customize Log Driver Output</a>. Log4j provides similar functionality via the <a href="lookups.html#DockerLookup">Docker Loookup</a>. More information on Log4j&#x2019;s Docker support may also be found at <a href="../log4j-docker/index.html">Log4j-Docker</a>. </p></div>
<div class="section">
<h2><a name="Appender_Performance"></a>Appender Performance</h2>
<p>The numbers in the table below represent how much time in seceonds was required for the application to call logger.debug 100,000 times. These numbers only include the time taken to deliver to the specifcly noted endpoint and many not include the actual time required before they are availble for viewing. All measurements were performed on a MacBook Pro with a 2.9GHz Intel Core I9 processor with 6 physical and 12 logical cores, 32GB of 2400 MHz DDR4 RAM, and 1TB of Apple SSD storage. The VM used by Docker was managed by VMWare Fusion and had 4 CPUs and 2 GB of RAM. These number should be used for relative perfomance comparisons as the results on another system may vary considerably.</p>
<p>The sample application used can be found under the log4j-spring-cloud-config/log4j-spring-cloud-config-samples directory in the Log4j <a class="externalLink" href="https://github.com/apache/logging-log4j2">source repository</a>.</p>
<table border="0" class="bodyTable">
<thead>
<tr class="a">
<th>Test </th>
<th align="right">1 Thread </th>
<th align="right">2 Threads </th>
<th align="right">4 Threads </th>
<th align="right">8 Threads </th>
</tr>
</thead>
<tbody>
<tr class="b">
<td colspan="5">Flume Avro </td>
</tr>
<tr class="a">
<td>- Batch Size 1 - JSON </td>
<td align="right">49.11 </td>
<td align="right">46.54 </td>
<td align="right">46.70 </td>
<td align="right">44.92 </td>
</tr>
<tr class="b">
<td>- Batch Size 1 - RFC5424 </td>
<td align="right">48.30 </td>
<td align="right">45.79 </td>
<td align="right">46.31 </td>
<td align="right">45.50 </td>
</tr>
<tr class="a">
<td>- Batch Size 100 - JSON </td>
<td align="right">6.33 </td>
<td align="right">3.87 </td>
<td align="right">3.57 </td>
<td align="right">3.84 </td>
</tr>
<tr class="b">
<td>- Batch Size 100 - RFC5424 </td>
<td align="right">6.08 </td>
<td align="right">3.69 </td>
<td align="right">3.22 </td>
<td align="right">3.11 </td>
</tr>
<tr class="a">
<td>- Batch Size 1000 - JSON </td>
<td align="right">4.83 </td>
<td align="right">3.20 </td>
<td align="right">3.02 </td>
<td align="right">2.11 </td>
</tr>
<tr class="b">
<td>- Batch Size 1000 - RFC5424 </td>
<td align="right">4.70 </td>
<td align="right">2.40 </td>
<td align="right">2.37 </td>
<td align="right">2.37 </td>
</tr>
<tr class="a">
<td colspan="5">Flume Embedded </td>
</tr>
<tr class="b">
<td>- RFC5424 </td>
<td align="right">3.58 </td>
<td align="right">2.10 </td>
<td align="right">2.10 </td>
<td align="right">2.70 </td>
</tr>
<tr class="a">
<td>- JSON </td>
<td align="right">4.20 </td>
<td align="right">2.49 </td>
<td align="right">3.53 </td>
<td align="right">2.90 </td>
</tr>
<tr class="b">
<td colspan="5">Kafka Local JSON </td>
</tr>
<tr class="a">
<td>- sendSync true </td>
<td align="right">58.46 </td>
<td align="right">38.55 </td>
<td align="right">19.59 </td>
<td align="right">19.01 </td>
</tr>
<tr class="b">
<td>- sendSync false </td>
<td align="right">9.8 </td>
<td align="right">10.8 </td>
<td align="right">12.23 </td>
<td align="right">11.36 </td>
</tr>
<tr class="a">
<td colspan="5">Console</td>
</tr>
<tr class="b">
<td>- JSON / Kubernetes </td>
<td align="right">3.03 </td>
<td align="right">3.11 </td>
<td align="right">3.04 </td>
<td align="right">2.51 </td>
</tr>
<tr class="a">
<td>- JSON </td>
<td align="right">2.80 </td>
<td align="right">2.74 </td>
<td align="right">2.54 </td>
<td align="right">2.35 </td>
</tr>
<tr class="b">
<td>- Docker fluentd driver </td>
<td align="right">10.65 </td>
<td align="right">9.92 </td>
<td align="right">10.42 </td>
<td align="right">10.27 </td>
</tr>
<tr class="a">
<td colspan="5">Rolling File</td>
</tr>
<tr class="b">
<td>- RFC5424 </td>
<td align="right">1.65 </td>
<td align="right">0.94 </td>
<td align="right">1.22 </td>
<td align="right">1.55</td>
</tr>
<tr class="a">
<td>- JSON </td>
<td align="right">1.90 </td>
<td align="right">0.95 </td>
<td align="right">1.57 </td>
<td align="right">1.94 </td>
</tr>
<tr class="b">
<td>TCP - Fluent Bit - JSON </td>
<td align="right">2.34 </td>
<td align="right">2.167 </td>
<td align="right">1.67 </td>
<td align="right">2.50 </td>
</tr>
<tr class="a">
<td colspan="5">Async Logger</td>
</tr>
<tr class="b">
<td>- TCP - Fluent Bit - JSON</td>
<td align="right">0.90 </td>
<td align="right">0.58 </td>
<td align="right">0.36 </td>
<td align="right">0.48 </td>
</tr>
<tr class="a">
<td>- Console - JSON </td>
<td align="right">0.83 </td>
<td align="right">0.57 </td>
<td align="right">0.55 </td>
<td align="right">0.61 </td>
</tr>
<tr class="b">
<td>- Flume Avro - 1000 - JSON</td>
<td align="right">0.76 </td>
<td align="right">0.37 </td>
<td align="right">0.45 </td>
<td align="right">0.68 </td>
</tr>
</tbody>
</table>
<p>Notes:</p>
<ol style="list-style-type: decimal">
<li>Flume Avro - Buffering is controlled by the batch size. Each send is complete when the remote acknowledges the batch was written to its channel. These number seem to indicate Flume Avro could benefit from using a pool of RPCClients, at least for a batchSize of 1.</li>
<li>Flume Embedded - This is essentially asynchronous as it writes to an in-memory buffer. It is unclear why the performance isn&#x2019;t closer to the AsyncLogger results.</li>
<li>Kafka was run in standalone mode on the same laptop as the application. See sendSync set to true requires waiting for an ack from Kafka for each log event.</li>
<li>Console - System.out is redirected to a file by Docker. Testing shows that it would be much slower if it was writing to the terminal screen.</li>
<li>Rolling File - Test uses the default buffer size of 8K.</li>
<li>TCP to Fluent Bit - The Socket Appender uses a default buffer size of 8K.</li>
<li>Async Loggers - These all write to a circular buffer and return to the application. The actual I/O will take place on a separate thread. If writing the events is performed more slowly than events are being created eventually the buffer will fill up and logging will be performed at the same pace that log events are written.</li>
</ol></div>
<div class="section">
<h2><a name="Logging_Recommendations"></a>Logging Recommendations</h2>
<ol style="list-style-type: decimal">
<li>Use asynchronous logging unless guaranteed delivery is absolutely required. As the performance numbers show, so long as the volume of logging is not high enough to fill up the circular buffer the overhead of logging will almost be unnoticeable to the application.</li>
<li>If overall performance is a consideration or you require multiline events such as stack traces be processed properly then log via TCP to a companion container that acts as a log forwarder. Use the Log4j Docker Lookup to add the container information to each log event.</li>
<li>Whenvever guaranteed delivery is required use Flume Avro with a batch size of 1 or another Appender such as the Kafka Appender with syncSend set to true that only return control after the downstream agent acknowledges receipt of the event. Beware that using an Appender that writes each event individually should be kept to a minimum since it is much slower than sending buffered events.</li>
<li>Logging to files within the container is discouraged. Doing so requires that a volume be declared in the Docker configuration and that the file be tailed by a log forwarder. However, it performs better than logging to the standard output stream. If logging via TCP is not an option and proper multiline handling is required then consider this option.</li>
</ol></div>
</td>
</tr>
</table>
</div>
<div class="footer">
<p>Copyright &copy; 1999-2019 <a class="external" href="https://www.apache.org/">The Apache Software Foundation</a>. All Rights Reserved.</p>
<p>Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, and the Apache Logging project logo are trademarks of The Apache Software Foundation.</p>
<p>Site powered by <a class="external" href="http://getbootstrap.com/">Twitter Bootstrap</a>. Icons from <a class="external" href="http://glyphicons.com/">Glyphicons Free</a>.</p>
</div>
</div>
</body>
</html>