Fluent Bit’s tag rewriting is how you dynamically route records by changing their tag field.

Here’s Fluent Bit processing logs from two different applications, app1 and app2, and sending them to different Elasticsearch indices based on their original tags.

[SERVICE]
    Flush        1
    Daemon       off
    Log_Level    info

[INPUT]
    Name         tail
    Path         /var/log/app1.log
    Tag          app1.log
    Parser       docker

[INPUT]
    Name         tail
    Path         /var/log/app2.log
    Tag          app2.log
    Parser       docker

[FILTER]
    Name         rewrite_tag
    Match        app1.*
    Rule         set tag ${tag}.processed
    Emitter_Name rewrite_app1

[FILTER]
    Name         rewrite_tag
    Match        app2.*
    Rule         set tag ${tag}.processed
    Emitter_Name rewrite_app2

[OUTPUT]
    Name         es
    Match        app1.log.processed
    Host         elasticsearch.example.com
    Port         9200
    Index        app1-logs-${TAG_YEAR}-${TAG_MONTH}-${TAG_DAY}
    Logstash_Format on

[OUTPUT]
    Name         es
    Match        app2.log.processed
    Host         elasticsearch.example.com
    Port         9200
    Index        app2-logs-${TAG_YEAR}-${TAG_MONTH}-${TAG_DAY}
    Logstash_Format on

This configuration does the following:

  1. Input app1.log: It tails /var/log/app1.log. All records read from this file are initially tagged app1.log.
  2. Input app2.log: It tails /var/log/app2.log. All records read from this file are initially tagged app2.log.
  3. Filter rewrite_app1: This filter matches any record with a tag starting with app1.. It then rewrites the tag by appending .processed to it. So, app1.log becomes app1.log.processed. The Emitter_Name helps debug by indicating which filter processed the record.
  4. Filter rewrite_app2: Similarly, this filter matches tags starting with app2. and appends .processed, turning app2.log into app2.log.processed.
  5. Output app1 to Elasticsearch: This output plugin is configured to match records with the tag app1.log.processed. It sends these logs to Elasticsearch, creating an index named app1-logs-YYYY-MM-DD.
  6. Output app2 to Elasticsearch: This output plugin matches records tagged app2.log.processed and sends them to Elasticsearch, creating an index named app2-logs-YYYY-MM-DD.

The core problem rewrite_tag solves is the need for conditional routing of logs based on their content or origin, without needing multiple identical input plugins or complex external logic. Instead of having separate output destinations for every minute variation of a log source, you can group them by a common characteristic and then apply specific routing rules.

Internally, Fluent Bit processes records in a pipeline. When a rewrite_tag filter is encountered, it intercepts the record. It checks the Match pattern against the record’s current tag. If it matches, it applies the Rule. The Rule can modify the tag in various ways: setting it to a static string, using Lua scripting for complex logic, or using pattern substitutions. Crucially, after a tag is rewritten, the record is re-emitted. This means it goes through the entire pipeline again, including potentially other filters and, importantly, the output plugins. The output plugins then match against the new tag.

The Emitter_Name is a powerful debugging tool. When you have multiple rewrite_tag filters, or complex routing, it adds a field to the record indicating which filter modified the tag. This can be invaluable for tracing the path of a log record through your Fluent Bit configuration.

If you set Rule set tag ${tag}.processed, and the original tag was my.app.log, the new tag will be my.app.log.processed. This is a simple string concatenation. You can also use more advanced rules. For example, to extract a field service_name from a JSON log and use it in the tag:

[FILTER]
    Name         rewrite_tag
    Match        *
    Rule         set tag ${service_name}.${tag}
    Emitter_Name add_service_to_tag

This rule, applied to any record (*), will prepend the value of the service_name field (if it exists in the record) to the existing tag. If a record had tag production.logs and a field {"service_name": "webserver"}, the new tag becomes webserver.production.logs. This requires the service_name field to be parsed and available in the record before this filter runs, typically by a preceding parser or another filter.

The rewrite_tag filter supports Lua scripting for very complex transformations, allowing you to access and manipulate record fields, context, and even external data sources to determine the new tag. However, for most common routing scenarios, simple pattern matching and string manipulation suffice.

When using rewrite_tag with multiple output plugins, the order of Match clauses in your output plugins becomes critical. A record can match multiple output plugins if its rewritten tag satisfies their respective Match patterns. Fluent Bit processes output plugins in the order they appear in the configuration. If a record matches an earlier output plugin, it will be sent there and typically not processed by subsequent output plugins unless you configure specific behavior (like http_User-Agent for HTTP outputs, which is a separate topic). This means you often structure your output plugins from most specific Match to most general, or ensure your rewritten tags are unique enough to target the correct destination.

The rewrite_tag plugin can cause a record to be processed multiple times by subsequent filters and outputs. If you have a rewrite_tag filter that matches app.* and rewrites the tag to processed.app.*, and then another filter later in the chain that also matches app.*, that second filter will not see the original app.* tag again. It will only see tags that currently match app.* at the point it’s evaluated. The re-emission means the record re-enters the processing chain from the beginning of the filters, not from the point after the rewrite_tag filter.

The next concept you’ll run into is managing the lifecycle of rewritten tags, especially when you have multiple rewrite stages or complex dependencies between tags and output matching.

Want structured learning?

Take the full Fluentbit course →