Rsyslog 8.1 Elasticsearch Output Performance

Recently, the first rsyslog version 8 release was announced. Major changes in its core should give outputs better performance, and the one for Elasticsearch should benefit a lot. Since we’re using rsyslog and Elasticsearch in Sematext‘s own Logsene, we had to take the new version for a spin.

The Weapon and the Target

For testing, we used a good-old i3 laptop, with 8GB of RAM. We generated 20 million logs, sent them to rsyslog via TCP and from there to Elasticsearch in the Logstash format, so they can get explored with Kibana. The objective was to stuff as many events per second into Elasticsearch as possible.

Rsyslog Architecture Overview

In order to tweak rsyslog effectively, one needs to understand its architecture, which is not that obvious (although there’s an ongoing effort to improve the documentation). The gist of it its architecture represented in the figure below.

  • you have input modules taking messages (from files, network, journal, etc.) and pushing them to a main queue
  • one or more main queue threads take those events and parse them. By default, they parse syslog formats, but you can configure rsyslog to use message modifier modules to do additional parsing (e.g. CEE-formatted JSON messages). Either way, this parsing generates structured events, made out of properties
  • after parsing, the main queue threads push events to the action queue. Or queues, if there are multiple actions and you want to fan-out
  • for each defined action, one or more action queue threads takes properties from events according to templates, and makes messages that would be sent to the destination. In Elasticsearch’s case, a template should make Elasticsearch JSON documents, and the destination would be the REST API endpoint
rsyslog message flow
rsyslog message flow

There are two more things to say about rsyslog’s architecture before we move on to the actual test:

  • you can have multiple independent flows (like the one in the figure above) in the same rsyslog process by using rulesets. Think of rulesets as swim-lanes. They’re useful for example when you want to process local logs and remote logs in a completely separate manner
  • queues can be in-memory, on disk, or a combination called disk-assisted. Here, we’ll use in-memory because they’re the fastest. For more information about how queues work, take a look here


To generate messages, we used tcpflood, a small and light tool that’s part of rsyslog’s testbench. It generates messages and sends them over to the local syslog via TCP.

Rsyslog took received those messages with the imtcp input module, queued them and forwarded them to Elasticsearch 0.90.7, which was also installed locally. We also tried with Elasticsearch 1.0 Beta 1 and got the same results (see below).

The flow of messages in this test is represented in the following figure:

Flow of messages in this test
Flow of messages in this test

The actual rsyslog config is listed below. It can be tuned further (for example by using the multithreaded imptcp input module), but we didn’t get significant improvements.

module(load="imtcp")   # TCP input module
module(load="omelasticsearch") # Elasticsearch output module

input(type="imtcp" port="13514")  # where to listen for TCP messages

    queue.size="1000000" # capacity of the main queue
    queue.dequeuebatchsize="1000" # process messages in batches of 1000
    queue.workerthreads="2" # 2 threads for the main queue

# template to generate JSON documents for Elasticsearch
         type="list") {
             constant(value="\"@timestamp\":\"")      property(name="timereported" dateFormat="rfc3339")
             constant(value="\",\"host\":\"")        property(name="hostname")
             constant(value="\",\"severity\":\"")    property(name="syslogseverity-text")
             constant(value="\",\"facility\":\"")    property(name="syslogfacility-text")
             constant(value="\",\"syslogtag\":\"")   property(name="syslogtag" format="json")
             constant(value="\",\"message\":\"")    property(name="msg" format="json")

       template="plain-syslog"  # use the template defined earlier
       queue.dequeuebatchsize="5000"   # ES bulk size
       action.resumeretrycount="-1"  # retry indefinitely if ES is unreachable

You can see from the configuration that:

  • both main and action queues have a defined size in number of messages
  • both have number of threads that deliver messages to the next step. The action needs more because it has to wait for Elasticsearch to reply
  • moving of messages from the queues happens in batches. For the Elasticsearch output, the batch of messages is sent through the Bulk API, which makes queue.dequeuebatchsize effectively the bulk size


We started with default Elasticsearch settings. Then we tuned them to leave rsyslog with a more significant slice of the CPU. We measured the indexing rate with SPM. Here are the average results over 20 million indexed events:

  • with default Elasticsearch settings, we got 8,000 events per second
  • after setting Elasticsearch up more production-like (5 second refresh interval, increased index buffer size, translog thresholds, etc), and the throughput went up to average of 20,000 events per second
  • in the end, we went berserk and used in-memory indices, updated the mapping to disable any storing or indexing for any field, to have Elasticsearch do as little work as possible and make room for rsyslog. Got an average of 30,000 events per second. In this scenario, rsyslog was using between 1 and 1.5 of the 4 virtual CPU cores, with tcpflood using 0.5 and Elasticsearch using from 2 to 2.5


20K EPS on a low-end machine with production-like configuration means Elasticsearch is quick at indexing. This is very good for logs, where you typically have lots of messages being generated, compared to how often you search.

If you need some tool to ship your logs to Elasticsearch with minimum overhead, rsyslog with its new version 8 may well be your best bet.

Additional resources:

NOTE: We’re hiring, so if you find this interesting, please check us out. Search, logging and performance is our thing!

10 thoughts on “Rsyslog 8.1 Elasticsearch Output Performance”

  1. Great post. We have logstash_shipper->redis->logstash_indexer->elasticsearch setup. I would like to do same thing as: ” updated the mapping to disable any storing or indexing for any field”.Can you share how you guys achieved that?

    Also, I would like to tweak elasticsearch so that indexing is performed on few fields. Any ideas?

    1. To disable indexing on a field, you can set “index” to “no”. Storing individual fields is disabled by default, they get retrieved from _source:

      You can control what’s included in _source (see the link above), and you can also control what’s indexed in the predefined _all field or disable it completely:

      Finally, you can define a default mapping for unknown types and fields:

  2. Excelent setup!

    To be more compliant to new rsyslog 8.2 configuration syntax we use following setup for rsyslog main queue:

    queue.size=”1000000″ # capacity of the main queue
    queue.dequeuebatchsize=”1000″ # process messages in batches of 1000 and move them to the action queues
    queue.workerthreads=”2″ # 2 threads for the main queue

    Could you please post your complete elasticsearch configuration/template for clarification performance tuneing on ES side.

    1. Hello,

      Thanks for your comment! I’ve now updated the configuration snippet to be fully RainerScript:

      I don’t have the configuration/template at hand, but I can give you a few pointers:
      – increase the refresh interval. See
      – increase the store throttling limits if you have fast disks:
      – increase the index buffer size and tune merge policies for less merging (though it will slow your searches down). Have a look at two previous articles for more information:

  3. How do you solved feeding redundant elasticsearch data nodes with rsyslog omelasticsearch module?


    1. Hi Joerg,

      At the moment, we deal with redundancy in two places:
      – at the Elasticsearch level, by using replicas (so we don’t need to index data two times)
      – to make sure we don’t have a single point of failure (the address omelasticsearch points to) we’re using a load balancer

      That said, it should work by defining a second omelasticsearch action (pointing to failover ES node), and adding action.execOnlyWhenPreviousIsSuspended=”on” to it. I haven’t tried it with omelasticsearch for a few years, though. You can find more info about what this does here:

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s