Skip to content

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.

Filter RulesOutputsIndex SetsData WarehousesDestinationsRoutingInputStreamsTCP/UDPGELFHTTPSOpenSearchHTTPSGELFOpenSearch

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.

GoalGraylog sideTenzir path
Receive messagesGELF output attached to a streamaccept_tcp + read_gelf
Receive over TLSGELF output with protocol TCP+TLSaccept_tcp with tls + read_gelf
Send over TCP or TLSGELF TCP inputto_tcp + write_delimited + fnprint_ndjson
Send over UDPGELF UDP inputto_udp + fnprint_ndjson
Send HTTP batchesGELF HTTP inputevery + to_http + write_ndjson
Query stored eventsOpenSearch or Elasticsearch search backendfrom_http
Write backend dataOpenSearch or Elasticsearch search backendto_opensearch or to_elasticsearch

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 fnprint_ndjson to serialize the record as compact JSON. For transport framing, use write_delimited when a transport requires a delimiter after each message.

Use a Graylog GELF output when you want Graylog to forward messages from one or more streams to a Tenzir pipeline.

  1. In Graylog, go to System > Outputs.
  2. Select GELF Output as the output type.
  3. Configure the output host and port to point at the Tenzir node.
  4. Select TCP as the protocol. Use TCP+TLS if the Tenzir listener requires TLS.
  5. Save the output.
  6. 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.

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.

Use a Graylog GELF input when Graylog should index, search, alert on, or route events produced by a Tenzir pipeline.

  1. In Graylog, go to System > Inputs.
  2. Select a GELF input type, such as GELF TCP, GELF UDP, or GELF HTTP.
  3. Configure the bind address and port. The default GELF port is 12201.
  4. 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.
  5. Start the input.

Graylog GELF over TCP expects one compact GELF JSON object followed by a null byte. Build the GELF record in TQL, serialize it with fnprint_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"
}

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.

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.

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.

Last updated: