Formatters
Formatter reference — how output plugins serialize events before delivery, with all available formats, parameters, and examples.
Every output plugin has a formatter that transforms event strings before writing them to the destination. Formatters are configured with the formatter field inside the output plugin config.
output:
- file:
path: output/events.log
formatter:
format: json
indent: 2If you omit the formatter field, the plugin uses its default format.
Default formatters
| Plugin | Default format | Reason |
|---|---|---|
| stdout | plain | Human-readable console output |
| file | plain | Raw log lines, one per row |
| http | json-batch | Send an entire batch in one HTTP request |
| opensearch | json | One JSON document per line for the bulk API |
| clickhouse | json | One JSON row per line for JSONEachRow input format |
You only need to set formatter when you want something different from the default.
Available formats
plain
Passes event strings through without any transformation. Each event is delivered exactly as produced by the event plugin.
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | — | Must be "plain". |
formatter:
format: plainSuppose the event plugin produces these three events:
2026-02-20T10:00:00 GET /api/users 200 12ms
2026-02-20T10:00:01 POST /api/orders 201 45ms
2026-02-20T10:00:02 GET /api/products 200 8msThe formatter outputs them unchanged — three strings, one per event:
2026-02-20T10:00:00 GET /api/users 200 12ms
2026-02-20T10:00:01 POST /api/orders 201 45ms
2026-02-20T10:00:02 GET /api/products 200 8msjson
Validates each event as JSON and optionally pretty-prints it. If an event is not valid JSON, it is skipped and counted as a format error — other events in the batch are not affected.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
format | string | — | Must be "json". | Format discriminator. |
indent | integer | 0 | >= 0 | Indentation level. 0 produces compact single-line JSON. |
Compact JSON (indent: 0)
formatter:
format: jsonEvents are validated and compacted to a single line:
{"user": "alice", "action": "login", "ip": "10.0.0.1"}
{"user": "bob", "action": "logout", "ip": "10.0.0.2"}{"user":"alice","action":"login","ip":"10.0.0.1"}
{"user":"bob","action":"logout","ip":"10.0.0.2"}Pretty-printed JSON (indent: 2)
formatter:
format: json
indent: 2{"user":"alice","action":"login","ip":"10.0.0.1"}{
"user": "alice",
"action": "login",
"ip": "10.0.0.1"
}Invalid JSON handling
Events that are not valid JSON are silently skipped. The remaining events are still formatted normally:
{"user": "alice"}
not valid json
{"user": "bob"}{"user":"alice"}
{"user":"bob"}json-batch
Collects all events in a batch into a single JSON array. Useful for HTTP endpoints that accept a batch payload. Each event must be valid JSON — invalid events are excluded from the array.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
format | string | — | Must be "json-batch". | Format discriminator. |
indent | integer | 0 | >= 0 | Indentation level. 0 produces compact single-line JSON. |
Compact batch
formatter:
format: json-batchThree events are collected into a single JSON array:
{"user": "alice", "action": "login"}
{"user": "bob", "action": "view"}
{"user": "charlie", "action": "purchase"}[{"user":"alice","action":"login"},{"user":"bob","action":"view"},{"user":"charlie","action":"purchase"}]Pretty-printed batch
formatter:
format: json-batch
indent: 2[
{
"user": "alice",
"action": "login"
},
{
"user": "bob",
"action": "view"
},
{
"user": "charlie",
"action": "purchase"
}
]This is the default formatter for the http output plugin — it lets you send an entire batch as a single HTTP request body.
template
Renders a Jinja2 template for each event individually. Use this when you need to reshape events — adding fields, wrapping in a custom envelope, or converting formats.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
format | string | — | Must be "template". | Format discriminator. |
template | string or null | null | Non-empty if set. | Inline Jinja2 template string. |
template_path | path or null | null | — | Path to a Jinja2 template file. |
Exactly one of template or template_path must be provided.
Inside the template, the variable event holds the raw event string.
Wrapping events in an envelope
Add metadata to each event before sending it to the output:
output:
- http:
url: https://ingest.example.com/events
formatter:
format: template
template: '{"source": "eventum", "environment": "staging", "payload": {{ event }}}'{"user": "alice", "action": "login"}
{"user": "bob", "action": "logout"}{"source": "eventum", "environment": "staging", "payload": {"user": "alice", "action": "login"}}
{"source": "eventum", "environment": "staging", "payload": {"user": "bob", "action": "logout"}}Converting JSON to CSV
Transform JSON events into CSV rows:
formatter:
format: template
template: '{{ (event | fromjson).timestamp }},{{ (event | fromjson).user }},{{ (event | fromjson).action }}'{"timestamp": "2026-02-20T10:00:00", "user": "alice", "action": "login"}
{"timestamp": "2026-02-20T10:00:05", "user": "bob", "action": "purchase"}2026-02-20T10:00:00,alice,login
2026-02-20T10:00:05,bob,purchaseUsing an external template file
For complex formatting, use a separate template file:
formatter:
format: template
template_path: formatters/syslog.jinja<14>1 {{ (event | fromjson).timestamp }} eventum app - - - {{ event }}{"timestamp": "2026-02-20T10:00:00", "level": "INFO", "msg": "Request processed"}<14>1 2026-02-20T10:00:00 eventum app - - - {"timestamp": "2026-02-20T10:00:00", "level": "INFO", "msg": "Request processed"}template-batch
Renders a single Jinja2 template with all events in a batch. The variable events holds the list of event strings. This produces one output string per batch, giving you full control over how events are aggregated.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
format | string | — | Must be "template-batch". | Format discriminator. |
template | string or null | null | Non-empty if set. | Inline Jinja2 template string. |
template_path | path or null | null | — | Path to a Jinja2 template file. |
Exactly one of template or template_path must be provided.
Newline-delimited batch
Join events with newlines for line-based protocols:
formatter:
format: template-batch
template: '{{ events | join("\n") }}'{"id": 1, "msg": "start"}
{"id": 2, "msg": "process"}
{"id": 3, "msg": "done"}{"id": 1, "msg": "start"}
{"id": 2, "msg": "process"}
{"id": 3, "msg": "done"}Custom XML envelope
Wrap a batch of events in an XML document for SOAP or legacy endpoints:
formatter:
format: template-batch
template_path: formatters/xml-batch.jinja<?xml version="1.0" encoding="UTF-8"?>
<events count="{{ events | length }}">
{%- for event in events %}
<event>{{ event }}</event>
{%- endfor %}
</events>user=alice action=login ip=10.0.0.1
user=bob action=logout ip=10.0.0.2<?xml version="1.0" encoding="UTF-8"?>
<events count="2">
<event>user=alice action=login ip=10.0.0.1</event>
<event>user=bob action=logout ip=10.0.0.2</event>
</events>Summary report
Aggregate a batch into a summary instead of forwarding individual events:
formatter:
format: template-batch
template_path: formatters/summary.jinja{%- set parsed = [] -%}
{%- for e in events -%}
{%- do parsed.append(e | fromjson) -%}
{%- endfor -%}
{%- set errors = parsed | selectattr("status", "ge", 400) | list -%}
{"total": {{ events | length }}, "errors": {{ errors | length }}, "error_rate": {{ "%.2f" | format(errors | length / events | length) }}}{"path": "/api/users", "status": 200}
{"path": "/api/orders", "status": 201}
{"path": "/api/users", "status": 500}
{"path": "/api/products", "status": 404}
{"path": "/api/health", "status": 200}{"total": 5, "errors": 2, "error_rate": 0.40}Per-event vs per-batch
| Format | Granularity | Result per write |
|---|---|---|
plain | Per event | N strings (one per event) |
json | Per event | N JSON strings |
template | Per event | N rendered strings |
json-batch | Per batch | 1 JSON array |
template-batch | Per batch | 1 rendered string |
Per-event formats produce one output string for each input event. Per-batch formats aggregate all events into a single output string, which reduces the number of I/O calls at the cost of sending larger payloads.
When to use per-batch formats:
- The destination expects a single payload (e.g., HTTP API accepting a JSON array)
- You want to reduce I/O overhead by writing once per batch
- You need to aggregate or summarize events before delivery
When to use per-event formats:
- The destination processes events one at a time (e.g., line-based log files)
- You want each event independently validated (invalid events are skipped, not the whole batch)
- Downstream systems need individual records (e.g., OpenSearch bulk API, ClickHouse JSONEachRow)