title: Poor performance of log4j2 in combination with Kafka
body: <p>I'm having performance issues using log4j2 (2.5) in combination with Kafka (0.10.1.0). When I enable Kafka in the log4j2.xml file, my application slows down to a crawl, whilst only outputting around 200KB/s of events to the Kafka broker. This is orders of magnitude lower than that what Kafka is supposed to achieve (<a href="https://engineering.linkedin.com/kafka/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines" rel="nofollow noreferrer">https://engineering.linkedin.com/kafka/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines</a>).</p>
  
  <p>Here is the relevant part of my log4j2.xml config file:</p>
  
  <pre><code>&lt;Kafka name="KafkaAll" topic="all"&gt;
    &lt;PatternLayout pattern="%date %message" /&gt;
    &lt;Property name="bootstrap.servers"&gt;localhost:9092&lt;/Property&gt;
    &lt;Property name="buffer.memory"&gt;67108864&lt;/Property&gt;
    &lt;Property name="batch.size"&gt;8196&lt;/Property&gt;
    &lt;Property name="acks"&gt;1&lt;/Property&gt;
  &lt;/Kafka&gt;
  </code></pre>
  
  <p>After running some tests I've been able to pin down the problem and found out that the ProducerPerformance test shipped with Kafka does achieve decent performance. Its performance is around 5MB/s, with similar sized messages of 100 bytes. After extensive testing, I found out that the difference lies not in the configuration, but in the way the calls are implemented. The log4j2 KafkaAppender uses the KafkaManager class to write log to Kafka:</p>
  
  <pre><code>public void send(final byte[] msg) throws ExecutionException, InterruptedException, TimeoutException {
      if (producer != null) {
          producer.send(new ProducerRecord&lt;byte[], byte[]&gt;(topic, msg)).get(timeoutMillis, TimeUnit.MILLISECONDS);
      }
  }
  </code></pre>
  
  <p>The performance problem is caused by the call to the "get" method, which blocks until the send has completed. Funny enough, there is a log4j appender included with Kafka that does take this issue into account:</p>
  
  <pre><code>Future&lt;RecordMetadata&gt; response = producer.send(new ProducerRecord&lt;byte[], byte[]&gt;(topic, message.getBytes()));
  if (syncSend) {
      try {
          response.get();
      } catch (InterruptedException ex) {
          throw new RuntimeException(ex);
      } catch (ExecutionException ex) {
          throw new RuntimeException(ex);
      }
  }
  </code></pre>
  
  <p>In other words, when syncSend is set to false, the call to send returns immediately. This "syncSend" property is however nowhere to be found in the log4j2 implementation of the KafkaAppender.</p>
  
  <p>I've tried making calls asynchronous through other means, such as using the AsyncAppender shipped with log4j2 and setting the acks property to 0 instead of 1. However, none of the settings achieve the performance gain by not waiting for the send to complete. I've also tried to use the log4j appender shipped with Kafka, but I did not manage to get it to work with log4j2 (and I want to stick with log4j2).</p>
  
  <p>So, finally, I have decided to fork the KafkaAppender shipped with log4j2 and remove the blocking part of the call. This works, but of course I'd rather be using of-the-shelf packages.</p>
  
  <p>Is there anyone who also ran into this problem? How did you solve the issue? Is there an easier way without changing code?</p>