Graylog is a log management and SIEM platform that routes messages through inputs, streams, processing pipelines, index sets, destinations, and outputs. Tenzir can receive GELF streams from Graylog, send GELF into Graylog inputs, and access the OpenSearch or Elasticsearch search backend when you need backend-level queries.
Choose an integration path
Section titled “Choose an integration path”Use GELF when Graylog should ingest, route, index, and alert on the events. Use direct backend access only when you intentionally want to bypass Graylog’s ingestion path.
| Goal | Graylog side | Tenzir path |
|---|---|---|
| Receive messages | GELF output attached to a stream | accept_tcp + read_gelf |
| Receive over TLS | GELF output with protocol TCP+TLS | accept_tcp with tls + read_gelf |
| Send over TCP or TLS | GELF TCP input | to_tcp + write_delimited + print_ndjson |
| Send over UDP | GELF UDP input | to_udp + print_ndjson |
| Send HTTP batches | GELF HTTP input | every + to_http + write_ndjson |
| Query stored events | OpenSearch or Elasticsearch search backend | from_http |
| Write backend data | OpenSearch or Elasticsearch search backend | to_opensearch or to_elasticsearch |
GELF message format
Section titled “GELF message format”GELF is a JSON message format with transport-specific framing. A
GELF message must include version, host, and
short_message. The timestamp field, when present, is a Unix timestamp in
seconds. Custom fields must start with _.
TQL can build the GELF record directly:
gelf_timestamp = (timestamp? else now()).since_epoch().count_seconds()this = { version: "1.1", host: source_host? else host? else "tenzir-node", short_message: message? else event_name? else "Tenzir event", timestamp: gelf_timestamp, level: syslog_level? else 6, _tenzir_topic: "detections",}Use print_ndjson to serialize the record as compact JSON. For transport
framing, use write_delimited when a transport requires a delimiter
after each message.
Receive messages from Graylog
Section titled “Receive messages from Graylog”Use a Graylog GELF output when you want Graylog to forward messages from one or more streams to a Tenzir pipeline.
Configure the Graylog output
Section titled “Configure the Graylog output”- In Graylog, go to System > Outputs.
- Select GELF Output as the output type.
- Configure the output host and port to point at the Tenzir node.
- Select
TCPas the protocol. UseTCP+TLSif the Tenzir listener requires TLS. - Save the output.
- In Streams, attach the output to the stream that should forward messages to Tenzir.
The output sends messages only after you attach it to a stream.
Start the Tenzir receiver
Section titled “Start the Tenzir receiver”Deploy a pipeline that listens on the host and port configured in the Graylog
output. This example accepts GELF over TCP on all interfaces and publishes the
parsed events to the graylog topic:
accept_tcp "0.0.0.0:12201" { read_gelf}publish "graylog"If you selected TCP+TLS in Graylog, configure TLS on the Tenzir listener:
let $tls = { certfile: "server.pem", keyfile: "server-key.pem",}
accept_tcp "0.0.0.0:12201", tls=$tls { read_gelf}publish "graylog"Replace the certificate paths with files that match the trust configuration of your Graylog output.
Send events to Graylog
Section titled “Send events to Graylog”Use a Graylog GELF input when Graylog should index, search, alert on, or route events produced by a Tenzir pipeline.
- In Graylog, go to System > Inputs.
- Select a GELF input type, such as GELF TCP, GELF UDP, or GELF HTTP.
- Configure the bind address and port. The default GELF port is
12201. - For GELF TCP, enable null-frame delimiting if your Graylog input exposes that
option. If you keep newline delimiting, use
"\n"instead of"\x00"in the TCP examples below. - Start the input.
Send GELF over TCP
Section titled “Send GELF over TCP”Graylog GELF over TCP expects one compact GELF JSON object followed by a null
byte. Build the GELF record in TQL, serialize it with print_ndjson, and
use write_delimited to append the null-byte frame delimiter.
subscribe "detections"gelf_timestamp = (timestamp? else now()).since_epoch().count_seconds()this = { version: "1.1", host: source_host? else host? else "tenzir-node", short_message: message? else event_name? else "Tenzir event", timestamp: gelf_timestamp, level: syslog_level? else 6, _tenzir_topic: "detections",}to_tcp "graylog.example.com:12201" { write_delimited this.print_ndjson(strip_null_fields=true), "\x00"}Replace graylog.example.com with the Graylog node or load balancer that hosts
the GELF TCP input.
If the Graylog input expects TLS, add TLS options to to_tcp:
to_tcp "graylog.example.com:12201", tls={} { write_delimited this.print_ndjson(strip_null_fields=true), "\x00"}Send GELF over UDP
Section titled “Send GELF over UDP”Graylog GELF over UDP expects one GELF message per datagram. Use to_udp
when each serialized event fits into one datagram:
subscribe "detections"gelf_timestamp = (timestamp? else now()).since_epoch().count_seconds()this = { version: "1.1", host: source_host? else host? else "tenzir-node", short_message: message? else event_name? else "Tenzir event", timestamp: gelf_timestamp, level: syslog_level? else 6, _tenzir_topic: "detections",}to_udp "graylog.example.com:12201", message=this.print_ndjson(strip_null_fields=true)Prefer TCP for reliable delivery. Use UDP only when datagram loss is acceptable and messages remain small enough for your network and Graylog input limits.
Send GELF over HTTP
Section titled “Send GELF over HTTP”The GELF HTTP input accepts one JSON message per request, or newline-delimited JSON when you enable bulk receiving on the Graylog input.
For one HTTP request per event, wrap to_http in each:
let $headers = {"Content-Type": "application/json"}
subscribe "detections"gelf_timestamp = (timestamp? else now()).since_epoch().count_seconds()this = { version: "1.1", host: source_host? else host? else "tenzir-node", short_message: message? else event_name? else "Tenzir event", timestamp: gelf_timestamp, level: syslog_level? else 6, _tenzir_topic: "detections",}each { from $this to_http "http://graylog.example.com:12201/gelf", headers=$headers { write_json strip_null_fields=true }}For bulk receiving, group events into time-based batches with every and
send newline-delimited GELF JSON:
let $headers = {"Content-Type": "application/json"}
subscribe "detections"gelf_timestamp = (timestamp? else now()).since_epoch().count_seconds()this = { version: "1.1", host: source_host? else host? else "tenzir-node", short_message: message? else event_name? else "Tenzir event", timestamp: gelf_timestamp, level: syslog_level? else 6, _tenzir_topic: "detections",}every 30s { to_http "http://graylog.example.com:12201/gelf", headers=$headers { write_ndjson strip_null_fields=true }}Use https:// and configure TLS options on to_http when the input
expects TLS.
Work with the search backend
Section titled “Work with the search backend”Graylog stores searchable messages in index sets backed by OpenSearch or
Elasticsearch. You can query those indices with from_http when you need
backfill, historical enrichment, or ad hoc exports.
Use direct writes with to_opensearch or to_elasticsearch only
for custom indices that you manage outside Graylog’s ingestion path. Direct
writes don’t pass through Graylog inputs, stream routing, processing pipelines,
or destination rules.