OpenTelemetry CollectorでSyslogを受信するメモ
既存システムからのログの連携がSyslog形式しかサポートされていない場合に、Syslogサーバーの用意が必要になります。 どのSyslogサーバーを使うか困ることが多いので、普段使っているOpenTelemetry CollectorでSyslogを受信する方法をメモしておきます。
OpenTelemetry Collector Contribにはsyslogreceiverがあります。
これでもいいのですが、attributesのパースが好みではなかったので、tcplogreceiverとExtractGrokPatterns関数で自前でパースする方法を紹介します。かつてLogstashでGrokパターンを使ってSyslogをパースしていた経験があり、それを移植しました。
次のバージョンで動作確認しています。
$ otelcol-contrib --version
otelcol-contrib version 0.142.0
以下の例ではDocker Composeを使用しますが、otelcol-contribのバイナリはこちらからダウンロードできます。
また、ログの保存先としてOTLPサポートの軽量ログストアLognrollを使用していますが、他のログストアに変更しても構いません。lognrollのバイナリはこちらからダウンロードできます。
目次
OpenTelemetry Collectorの設定例
まずはconfig.yamlに以下の内容を記述します。
receivers:
tcplog/syslog:
listen_address: 0.0.0.0:5514
processors:
transform/syslog:
error_mode: ignore
log_statements:
#! https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor#basic-config
- context: log
statements:
- merge_maps(log.attributes, ExtractGrokPatterns(log.body, "(?:%{INT:syslog6587.msglen} )?<%{POSINT:syslog.pri}>(%{SPACE})?(?:%{NONNEGINT:syslog5424.ver} )?(?:%{SYSLOGTIMESTAMP:syslog.timestamp}|%{TIMESTAMP_ISO8601:syslog.timestamp}) %{DATA:syslog.hostname} %{DATA:syslog.program}(?:\\[%{POSINT:syslog.pid}\\])?(:)? %{GREEDYDATA:syslog.message}", true), "upsert") where IsString(log.body)
- 'merge_maps(log.attributes, ExtractGrokPatterns(log.attributes["syslog.message"], "(?:%{DATA:syslog.procid}|\\-) (?:%{DATA:syslog.msgid}|\\-)? %{GREEDYDATA:syslog.message}", true), "upsert") where log.attributes["syslog5424.ver"] == "1"'
- merge_maps(log.attributes, ExtractGrokPatterns(log.attributes["syslog.message"], "(?P<syslog_sd>\\[[^\\]\\\\]*@[^\\]\\\\]*(?:\\\\.[^\\]\\\\]*)*\\])(?P<syslog_meta>\\[meta[^\\]]*\\])?\\s+%{GREEDYDATA:syslog.message}"), "upsert")
- 'merge_maps(log.attributes, ExtractGrokPatterns(log.attributes["syslog_sd"], "\\[%{DATA:syslog.sd_id} (?<syslog_sd_params_raw>([^\\]\\\\]|\\\\.)+)\\]", true), "upsert") where IsString(log.attributes["syslog_sd"])'
- merge_maps(log.attributes, ParseKeyValue(log.attributes["syslog_sd_params_raw"]), "upsert") where IsString(log.attributes["syslog_sd_params_raw"])
- 'merge_maps(log.attributes, ExtractGrokPatterns(log.attributes["syslog_meta"], "\\[%{DATA:syslog.meta_id} (?<syslog_meta_params_raw>([^\\]\\\\]|\\\\.)+)\\]", true), "upsert") where IsString(log.attributes["syslog_meta"])'
- merge_maps(log.attributes, ParseKeyValue(log.attributes["syslog_meta_params_raw"]), "upsert") where IsString(log.attributes["syslog_meta_params_raw"])
- set(log.attributes["syslog.message"], Trim(log.attributes["syslog.message"], ""))
- set(log.attributes["syslog.facility"], Int(log.attributes["syslog.pri"]) / 8) where IsString(log.attributes["syslog.pri"])
- set(log.attributes["syslog.severity"], Int(log.attributes["syslog.pri"]) - (log.attributes["syslog.facility"] * 8)) where IsString(log.attributes["syslog.pri"])
- set(log.severity_text, "FATAL") where log.attributes["syslog.severity"] == 0
- set(log.severity_number, 21) where log.attributes["syslog.severity"] == 0
- set(log.severity_text, "FATAL") where log.attributes["syslog.severity"] == 1
- set(log.severity_number, 21) where log.attributes["syslog.severity"] == 1
- set(log.severity_text, "FATAL") where log.attributes["syslog.severity"] == 2
- set(log.severity_number, 21) where log.attributes["syslog.severity"] == 2
- set(log.severity_text, "ERROR") where log.attributes["syslog.severity"] == 3
- set(log.severity_number, 17) where log.attributes["syslog.severity"] == 3
- set(log.severity_text, "WARN") where log.attributes["syslog.severity"] == 4
- set(log.severity_number, 13) where log.attributes["syslog.severity"] == 4
- set(log.severity_text, "INFO") where log.attributes["syslog.severity"] == 5
- set(log.severity_number, 9) where log.attributes["syslog.severity"] == 5
- set(log.severity_text, "INFO") where log.attributes["syslog.severity"] == 6
- set(log.severity_number, 9) where log.attributes["syslog.severity"] == 6
- set(log.severity_text, "DEBUG") where log.attributes["syslog.severity"] == 7
- set(log.severity_number, 5) where log.attributes["syslog.severity"] == 7
- replace_pattern(log.attributes["syslog.timestamp"], "(.+)([\\+\\-])(\\d+):(\\d+)$", "$$1$$2$$3$$4")
- set(log.time, Time(log.attributes["syslog.timestamp"], "%Y-%m-%dT%H:%M:%S.%sZ")) where IsMatch(log.attributes["syslog.timestamp"], "^\\d+\\-.+Z$")
- set(log.time, Time(log.attributes["syslog.timestamp"], "%Y-%m-%dT%H:%M:%S.%s%z")) where IsMatch(log.attributes["syslog.timestamp"], "^\\d+\\-.+\\d$")
- set(log.time, Time(log.attributes["syslog.timestamp"], "%b %e %H:%M:%S")) where IsMatch(log.attributes["syslog.timestamp"], "^[a-zA-Z]+ .+\\d$")
- set(log.body, log.attributes["syslog.message"])
- delete_key(log.attributes, "syslog.message")
- delete_key(log.attributes, "syslog_sd")
- delete_key(log.attributes, "syslog_sd_params_raw")
- delete_key(log.attributes, "syslog_meta")
- delete_key(log.attributes, "syslog_meta_params_raw")
- delete_key(log.attributes, "syslog.meta_id")
- delete_key(log.attributes, "syslog.pri")
- delete_key(log.attributes, "syslog.severity")
- delete_key(log.attributes, "syslog.timestamp")
- delete_key(log.attributes, "syslog.procid") where log.attributes["syslog.procid"] == "-"
- delete_key(log.attributes, "syslog.msgid") where log.attributes["syslog.msgid"] == "-"
- delete_key(log.attributes, "syslog6587.msglen")
filter/syslog:
error_mode: ignore
logs:
log_record:
#! Example: Exclude noisy logs from "noisy-app"
- log.attributes["syslog.program"] == "noisy-app"
groupbyattrs/syslog:
keys:
- syslog.program
- syslog.hostname
- container_image
- app_name
- app
- node
transform/syslog-resource:
error_mode: ignore
log_statements:
- context: log
statements:
- set(resource.attributes["service.name"], resource.attributes["syslog.program"]) where IsString(resource.attributes["syslog.program"])
- set(resource.attributes["client.address"], resource.attributes["syslog.hostname"]) where IsString(resource.attributes["syslog.hostname"])
- set(resource.attributes["container.image.name"], resource.attributes["container_image"]) where IsString(resource.attributes["container_image"])
- set(resource.attributes["service.name"], resource.attributes["app_name"]) where IsString(resource.attributes["app_name"])
- set(resource.attributes["service.name"], resource.attributes["app"]) where IsString(resource.attributes["app"])
- set(resource.attributes["k8s.node.name"], resource.attributes["node"]) where IsString(resource.attributes["node"])
- delete_key(resource.attributes, "syslog.program")
- delete_key(resource.attributes, "syslog.hostname")
- delete_key(resource.attributes, "container_image")
- delete_key(resource.attributes, "app_name")
- delete_key(resource.attributes, "app")
- delete_key(resource.attributes, "node")
exporters:
otlphttp/lognroll:
endpoint: http://lognroll:4318
tls:
insecure: true
headers:
Authorization: Bearer changeme
compression: gzip
debug:
verbosity: detailed
service:
pipelines:
logs/tcplog:
receivers:
- tcplog/syslog
processors:
- transform/syslog
- filter/syslog
- groupbyattrs/syslog
- transform/syslog-resource
exporters:
- debug
- otlphttp/lognroll
パイプラインは以下の順序で処理されます:
receivers:
- tcplog/syslog # Receive syslog
processors:
- transform/syslog # Parse and normalize
- filter/syslog # Filter out noisy logs
- groupbyattrs/syslog # Group by program into separate resources
- transform/syslog-resource # Set resource attributes
exporters:
- debug # Debug output
- otlphttp/lognroll # OTLP export
transform/syslog - syslog メッセージのパース
ここが設定の中核部分です。OTTL (OpenTelemetry Transformation Language)を使用して、生のsyslogメッセージを構造化データに変換します。
まず、syslog には主に2つのフォーマットがあります。
RFC 3164 (BSD syslog)
<14>Jan 6 12:00:00 myhost myapp[1234]: Hello world
RFC 5424
<14>1 2026-01-06T12:00:00Z myhost myapp 1234 ID001 [sd@123 key="value"] Hello world
これらを Grok パターンでパースします。
- merge_maps(log.attributes, ExtractGrokPatterns(log.body,
"(?:%{INT:syslog6587.msglen} )?<%{POSINT:syslog.pri}>(%{SPACE})?(?:%{NONNEGINT:syslog5424.ver} )?(?:%{SYSLOGTIMESTAMP:syslog.timestamp}|%{TIMESTAMP_ISO8601:syslog.timestamp}) %{DATA:syslog.hostname} %{DATA:syslog.program}(?:\\[%{POSINT:syslog.pid}\\])?(:)? %{GREEDYDATA:syslog.message}",
true), "upsert") where IsString(log.body)
このパターンは RFC 3164 と RFC 5424 の両方に対応し、以下の属性を抽出します:
| 属性 | 説明 | 例 |
|---|---|---|
syslog.pri |
Priority 値 | 14 |
syslog5424.ver |
RFC 5424 バージョン | 1 |
syslog.timestamp |
タイムスタンプ | Jan 6 12:00:00 or 2026-01-06T12:00:00Z |
syslog.hostname |
ホスト名 | myhost |
syslog.program |
プログラム名 | myapp |
syslog.pid |
プロセス ID | 1234 |
syslog.message |
メッセージ本文 | Hello world |
RFC 5424 では [sd_id key="value"] 形式の構造化データを含められるため、これもパースします。
# Extract Structured Data
- merge_maps(log.attributes, ExtractGrokPatterns(log.attributes["syslog.message"],
"(?P<syslog_sd>\\[[^\\]\\\\]*@[^\\]\\\\]*(?:\\\\.[^\\]\\\\]*)*\\])..."), "upsert")
# Parse Key-Value pairs
- merge_maps(log.attributes, ParseKeyValue(log.attributes["syslog_sd_params_raw"]), "upsert")
where IsString(log.attributes["syslog_sd_params_raw"])
これにより [mysd@123 app="web" node="node-1"] のようなデータが app="web", node="node-1"として属性に展開されます。
次に、syslog の Priority 値から Facility と Severity を計算します。Priority 値は Facility * 8 + Severity で計算されているため、逆算します。
- set(log.attributes["syslog.facility"], Int(log.attributes["syslog.pri"]) / 8)
- set(log.attributes["syslog.severity"], Int(log.attributes["syslog.pri"]) - (log.attributes["syslog.facility"] * 8))
そして、syslog の severity (0-7) を OpenTelemetry の severity_text/severity_number に変換します。
- set(log.severity_text, "FATAL") where log.attributes["syslog.severity"] == 0 # Emergency
- set(log.severity_number, 21) where log.attributes["syslog.severity"] == 0
- set(log.severity_text, "ERROR") where log.attributes["syslog.severity"] == 3 # Error
- set(log.severity_number, 17) where log.attributes["syslog.severity"] == 3
- set(log.severity_text, "WARN") where log.attributes["syslog.severity"] == 4 # Warning
- set(log.severity_number, 13) where log.attributes["syslog.severity"] == 4
- set(log.severity_text, "INFO") where log.attributes["syslog.severity"] == 6 # Informational
- set(log.severity_number, 9) where log.attributes["syslog.severity"] == 6
- set(log.severity_text, "DEBUG") where log.attributes["syslog.severity"] == 7 # Debug
- set(log.severity_number, 5) where log.attributes["syslog.severity"] == 7
また、syslog のタイムスタンプ形式に応じて log.time を設定します。
# ISO 8601 (UTC)
- set(log.time, Time(log.attributes["syslog.timestamp"], "%Y-%m-%dT%H:%M:%S.%sZ"))
where IsMatch(log.attributes["syslog.timestamp"], "^\\d+\\-.+Z$")
# ISO 8601 (with timezone)
- set(log.time, Time(log.attributes["syslog.timestamp"], "%Y-%m-%dT%H:%M:%S.%s%z"))
where IsMatch(log.attributes["syslog.timestamp"], "^\\d+\\-.+\\d$")
# BSD syslog format (Jan 6 12:00:00)
- set(log.time, Time(log.attributes["syslog.timestamp"], "%b %e %H:%M:%S"))
where IsMatch(log.attributes["syslog.timestamp"], "^[a-zA-Z]+ .+\\d$")
注意: BSD 形式では日付が1桁の場合スペースでパディングされるため、%d ではなく %e を使用します。
最後に、パース後の中間属性を削除してログをクリーンに保ちます。
- set(log.body, log.attributes["syslog.message"]) # Set message to body
- delete_key(log.attributes, "syslog.message")
- delete_key(log.attributes, "syslog.pri")
- delete_key(log.attributes, "syslog.severity")
- delete_key(log.attributes, "syslog.timestamp")
# ... delete other intermediate attributes
filter/syslog - ノイズの除去
特定のプログラムからのログを除外します:
filter/syslog:
error_mode: ignore
logs:
log_record:
- log.attributes["syslog.program"] == "noisy-app"
groupbyattrs/syslog - リソースの分離
groupbyattrs/syslog:
keys:
- syslog.program
- syslog.hostname
# ...
groupbyattrs processor は重要な役割を果たします:
- 同じ
syslog.program、syslog.hostnameなどを持つログを同一リソースにグループ化 syslog.programなどをlog.attributesからresource.attributesに昇格
これにより、異なるプログラムからのログが同じバッチで処理されても、リソース属性が混在しません。
次のProcessor(transform/syslog-resource)でリソース属性の設定を行いますが、ここのgroupbyattrs がないと、例えば、以下の問題が発生します:
- 複数のログが同じバッチで処理される
- 各ログで
resource.attributes["service.name"]が上書きされる - 最後に処理されたログの値で全ログの
service.nameが決まってしまう
リソース属性に設定したいキーは全て groupbyattrs の keys に含めてください。
transform/syslog-resource - リソース属性の設定
transform/syslog-resource:
error_mode: ignore
log_statements:
- context: log
statements:
- set(resource.attributes["container.image.name"], log.attributes["container_image"])
where IsString(log.attributes["container_image"])
- set(resource.attributes["service.name"], resource.attributes["syslog.program"])
where IsString(resource.attributes["syslog.program"])
- set(resource.attributes["service.name"], log.attributes["app_name"])
where IsString(log.attributes["app_name"])
# ...
groupbyattrs の後に実行されるため、syslog.program は既に resource.attributes に移動しています。これを service.name として設定します。
また、Structured Data から抽出された app_name, container_image, nodeなども対応するSemantic Conventionsの属性にマッピングします。
繰り返しになりますが、これらのリソース属性に設定したいキーは全て groupbyattrs の keys に含めてください。
Processorの順序が重要です。
processors:
- transform/syslog # 1. Parse (set syslog.program in log.attributes)
- filter/syslog # 2. Filter (evaluate log.attributes["syslog.program"])
- groupbyattrs/syslog # 3. Group (move syslog.program to resource.attributes)
- transform/syslog-resource # 4. Set resource (use resource.attributes["syslog.program"])
この順序により:
- フィルタは
log.attributesを参照できる - リソース属性は
groupbyattrsで分離された後に設定される
となります。
Docker Composeファイル例
ではこの設定ファイルを使ってDocker Composeで起動してみます。以下はcompose.yamlの例です。
services:
lognroll:
image: ghcr.io/categolj/lognroll:native
pull_policy: always
restart: always
ports:
- "4318:4318"
volumes:
- ./data:/data
environment:
LOGNROLL_DB_PATH: /data/lognroll.db
LOGNROLL_AUTH_TOKEN: changeme
otelcol:
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.142.0
restart: always
command: [ "--config", "/etc/otelcol/config.yaml" ]
ports:
- "5514:5514"
volumes:
- ./config.yaml:/etc/otelcol/config.yaml:ro
depends_on:
- lognroll
次のコマンドで起動します。
docker compose up
ncコマンドを使いテストメッセージを送信します。次の例ではRFC 5424形式のメッセージを送信しています。
echo "<14>1 $(date -u +%Y-%m-%dT%H:%M:%SZ) myhost myapp 1234 ID98 [uls@0 logtype=\"access\" clustername=\"mycluster\" namespace=\"mynamespace\"] Sample app log message." | nc localhost 5514
OpenTelemetry Collectorのログに以下のような出力が表示されれば成功です。
2026-01-07T02:14:20.048Z info service@v0.143.0/service.go:250 Starting otelcol-contrib... {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "Version": "0.143.1", "NumCPU": 16}
2026-01-07T02:14:20.048Z info extensions/extensions.go:40 Starting extensions... {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}}
2026-01-07T02:14:20.048Z info adapter/receiver.go:41 Starting stanza receiver {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "otelcol.component.id": "tcplog/syslog", "otelcol.component.kind": "receiver", "otelcol.signal": "logs"}
2026-01-07T02:14:20.048Z info service@v0.143.0/service.go:273 Everything is ready. Begin running and processing data. {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}}
2026-01-07T02:14:30.149Z info Logs {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs", "resource logs": 1, "log records": 1}
2026-01-07T02:14:30.149Z info ResourceLog #0
Resource SchemaURL:
Resource attributes:
-> client.address: Str(myhost)
-> service.name: Str(myapp)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2026-01-07 02:14:30.130747673 +0000 UTC
Timestamp: 2026-01-07 02:14:30 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Sample app log message.)
Attributes:
-> logtype: Str(access)
-> clustername: Str(mycluster)
-> syslog5424.ver: Str(1)
-> syslog.sd_id: Str(uls@0)
-> syslog.facility: Int(1)
-> namespace: Str(mynamespace)
-> syslog.msgid: Str(ID98)
-> syslog.procid: Str(1234)
Trace ID:
Span ID:
Flags: 0
{"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}
LognrollのUI (http://localhost:4318 username: 空, password: changeme)で"View Logs"ボタンを押すとログが確認できます。

最後に色々なログメッセージをスクリプトで送信してみます。次のtest-syslog.shスクリプトを作成します。
#!/bin/bash
# Test script for sending syslog messages to otelcol
HOST="${SYSLOG_HOST:-localhost}"
PORT="${SYSLOG_PORT:-5514}"
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
send_syslog() {
local msg="$1"
local desc="$2"
echo -e "${BLUE}[Test]${NC} $desc"
echo -e "${GREEN}>>>${NC} $msg"
echo "$msg" | nc -w1 "$HOST" "$PORT"
echo ""
}
echo "========================================"
echo "Syslog Test Script"
echo "Target: $HOST:$PORT"
echo "========================================"
echo ""
# RFC 3164 (BSD Syslog) format tests
echo "=== RFC 3164 (BSD Syslog) Format ==="
echo ""
send_syslog '<14>Jan 6 12:00:00 myhost myapp[1234]: This is an info message' \
"RFC 3164 - Basic info message"
send_syslog '<11>Jan 6 12:00:01 myhost kernel: Critical kernel error occurred' \
"RFC 3164 - Critical message (facility=1, severity=3)"
send_syslog '<134>Jan 6 12:00:02 webserver nginx[5678]: GET /api/health 200 OK' \
"RFC 3164 - Local0 info (web server log)"
# RFC 5424 format tests
echo "=== RFC 5424 Format ==="
echo ""
send_syslog '<14>1 2026-01-06T12:00:00.123456Z myhost myapp 1234 ID001 - Hello from RFC 5424' \
"RFC 5424 - Basic message with ISO timestamp"
send_syslog '<14>1 2026-01-06T12:00:00+09:00 myhost myapp 1234 ID002 - Message with timezone offset' \
"RFC 5424 - Message with timezone"
send_syslog '<14>1 2026-01-06T12:00:00Z myhost myapp - - - Minimal RFC 5424 message' \
"RFC 5424 - Minimal (no PID, no MSGID)"
# RFC 5424 with Structured Data
send_syslog '<14>1 2026-01-06T12:00:00Z myhost myapp 1234 ID003 [exampleSDID@32473 eventSource="Application" eventID="1011"] Application started successfully' \
"RFC 5424 - With Structured Data"
send_syslog '<14>1 2026-01-06T12:00:00Z myhost myapp 1234 ID004 [mysd@12345 container_image="nginx:latest" app_name="web-frontend" node="node-1"][meta sequence="123"] Container log message' \
"RFC 5424 - With SD and meta"
# Different severity levels
echo "=== Severity Level Tests ==="
echo ""
send_syslog '<8>Jan 6 12:00:10 myhost test[100]: Emergency - System is unusable' \
"Severity 0 - Emergency"
send_syslog '<9>Jan 6 12:00:11 myhost test[100]: Alert - Action must be taken immediately' \
"Severity 1 - Alert"
send_syslog '<10>Jan 6 12:00:12 myhost test[100]: Critical - Critical conditions' \
"Severity 2 - Critical"
send_syslog '<11>Jan 6 12:00:13 myhost test[100]: Error - Error conditions' \
"Severity 3 - Error"
send_syslog '<12>Jan 6 12:00:14 myhost test[100]: Warning - Warning conditions' \
"Severity 4 - Warning"
send_syslog '<13>Jan 6 12:00:15 myhost test[100]: Notice - Normal but significant' \
"Severity 5 - Notice"
send_syslog '<14>Jan 6 12:00:16 myhost test[100]: Info - Informational messages' \
"Severity 6 - Info"
send_syslog '<15>Jan 6 12:00:17 myhost test[100]: Debug - Debug-level messages' \
"Severity 7 - Debug"
# Test filtered message (should be filtered out by config)
echo "=== Filter Test ==="
echo ""
send_syslog '<14>Jan 6 12:00:20 myhost noisy-app[9999]: This message should be filtered out' \
"Filtered message (syslog.program == noisy-app)"
echo "========================================"
echo "All test messages sent!"
echo "========================================"
次のコマンドで実行します。
chmod +x test-syslog.sh
./test-syslog.sh
OpenTelemetry Collectorのログに以下のような出力が表示されれば成功です。
2026-01-07T02:16:24.350Z info Logs {"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs", "resource logs": 4, "log records": 14}
2026-01-07T02:16:24.350Z info ResourceLog #0
Resource SchemaURL:
Resource attributes:
-> client.address: Str(webserver)
-> service.name: Str(nginx)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2026-01-07 02:16:24.253552555 +0000 UTC
Timestamp: 2026-01-06 12:00:02 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(GET /api/health 200 OK)
Attributes:
-> syslog.facility: Int(16)
-> syslog.pid: Str(5678)
Trace ID:
Span ID:
Flags: 0
ResourceLog #1
Resource SchemaURL:
Resource attributes:
-> client.address: Str(myhost)
-> service.name: Str(myapp)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2026-01-07 02:16:24.261633882 +0000 UTC
Timestamp: 2026-01-06 12:00:00.123456 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(- Hello from RFC 5424)
Attributes:
-> syslog.procid: Str(1234)
-> syslog.facility: Int(1)
-> syslog5424.ver: Str(1)
-> syslog.msgid: Str(ID001)
Trace ID:
Span ID:
Flags: 0
LogRecord #1
ObservedTimestamp: 2026-01-07 02:16:24.268348627 +0000 UTC
Timestamp: 2026-01-06 03:00:00 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(- Message with timezone offset)
Attributes:
-> syslog.facility: Int(1)
-> syslog5424.ver: Str(1)
-> syslog.procid: Str(1234)
-> syslog.msgid: Str(ID002)
Trace ID:
Span ID:
Flags: 0
LogRecord #2
ObservedTimestamp: 2026-01-07 02:16:24.275025663 +0000 UTC
Timestamp: 2026-01-06 12:00:00 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(- Minimal RFC 5424 message)
Attributes:
-> syslog5424.ver: Str(1)
-> syslog.facility: Int(1)
Trace ID:
Span ID:
Flags: 0
LogRecord #3
ObservedTimestamp: 2026-01-07 02:16:24.281035992 +0000 UTC
Timestamp: 2026-01-06 12:00:00 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Application started successfully)
Attributes:
-> syslog5424.ver: Str(1)
-> syslog.sd_id: Str(exampleSDID@32473)
-> syslog.procid: Str(1234)
-> syslog.facility: Int(1)
-> eventID: Str(1011)
-> eventSource: Str(Application)
-> syslog.msgid: Str(ID003)
Trace ID:
Span ID:
Flags: 0
ResourceLog #2
Resource SchemaURL:
Resource attributes:
-> k8s.node.name: Str(node-1)
-> container.image.name: Str(nginx:latest)
-> client.address: Str(myhost)
-> service.name: Str(web-frontend)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2026-01-07 02:16:24.286272738 +0000 UTC
Timestamp: 2026-01-06 12:00:00 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Container log message)
Attributes:
-> syslog.procid: Str(1234)
-> syslog5424.ver: Str(1)
-> sequence: Str(123)
-> syslog.msgid: Str(ID004)
-> syslog.sd_id: Str(mysd@12345)
-> syslog.facility: Int(1)
Trace ID:
Span ID:
Flags: 0
ResourceLog #3
Resource SchemaURL:
Resource attributes:
-> client.address: Str(myhost)
-> service.name: Str(test)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2026-01-07 02:16:24.291517067 +0000 UTC
Timestamp: 2026-01-06 12:00:10 +0000 UTC
SeverityText: FATAL
SeverityNumber: Fatal(21)
Body: Str(Emergency - System is unusable)
Attributes:
-> syslog.pid: Str(100)
-> syslog.facility: Int(1)
Trace ID:
Span ID:
Flags: 0
LogRecord #1
ObservedTimestamp: 2026-01-07 02:16:24.296908604 +0000 UTC
Timestamp: 2026-01-06 12:00:11 +0000 UTC
SeverityText: FATAL
SeverityNumber: Fatal(21)
Body: Str(Alert - Action must be taken immediately)
Attributes:
-> syslog.facility: Int(1)
-> syslog.pid: Str(100)
Trace ID:
Span ID:
Flags: 0
LogRecord #2
ObservedTimestamp: 2026-01-07 02:16:24.301921226 +0000 UTC
Timestamp: 2026-01-06 12:00:12 +0000 UTC
SeverityText: FATAL
SeverityNumber: Fatal(21)
Body: Str(Critical - Critical conditions)
Attributes:
-> syslog.facility: Int(1)
-> syslog.pid: Str(100)
{"@timestamp":"2026-01-07T02:16:24.354679851Z","log":{"level":"INFO","logger":"accesslog"},"process":{"pid":1,"thread":{"name":"jetty-26"}},"service":{"name":"lognroll","node":{}},"message":"kind=server method=POST url=\"http://lognroll:4318/v1/logs\" status=200 duration=3 protocol=\"HTTP/1.1\" remote=\"192.168.158.3\" user_agent=\"OpenTelemetry Collector Contrib/0.143.1 (linux/arm64)\"","kind":"server","method":"POST","url":"http://lognroll:4318/v1/logs","status":200,"duration":3,"host":"lognroll","path":"/v1/logs","remote":"192.168.158.3","protocol":"HTTP/1.1","user_agent":"OpenTelemetry Collector Contrib/0.143.1 (linux/arm64)","ecs":{"version":"8.11"}}
Trace ID:
Span ID:
Flags: 0
LogRecord #3
ObservedTimestamp: 2026-01-07 02:16:24.307254763 +0000 UTC
Timestamp: 2026-01-06 12:00:13 +0000 UTC
SeverityText: ERROR
SeverityNumber: Error(17)
Body: Str(Error - Error conditions)
Attributes:
-> syslog.facility: Int(1)
-> syslog.pid: Str(100)
Trace ID:
Span ID:
Flags: 0
LogRecord #4
ObservedTimestamp: 2026-01-07 02:16:24.315502132 +0000 UTC
Timestamp: 2026-01-06 12:00:14 +0000 UTC
SeverityText: WARN
SeverityNumber: Warn(13)
Body: Str(Warning - Warning conditions)
Attributes:
-> syslog.pid: Str(100)
-> syslog.facility: Int(1)
Trace ID:
Span ID:
Flags: 0
LogRecord #5
ObservedTimestamp: 2026-01-07 02:16:24.319832087 +0000 UTC
Timestamp: 2026-01-06 12:00:15 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Notice - Normal but significant)
Attributes:
-> syslog.pid: Str(100)
-> syslog.facility: Int(1)
Trace ID:
Span ID:
Flags: 0
LogRecord #6
ObservedTimestamp: 2026-01-07 02:16:24.323949959 +0000 UTC
Timestamp: 2026-01-06 12:00:16 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Info - Informational messages)
Attributes:
-> syslog.facility: Int(1)
-> syslog.pid: Str(100)
Trace ID:
Span ID:
Flags: 0
LogRecord #7
ObservedTimestamp: 2026-01-07 02:16:24.328798788 +0000 UTC
Timestamp: 2026-01-06 12:00:17 +0000 UTC
SeverityText: DEBUG
SeverityNumber: Debug(5)
Body: Str(Debug - Debug-level messages)
Attributes:
-> syslog.facility: Int(1)
-> syslog.pid: Str(100)
Trace ID:
Span ID:
Flags: 0
{"resource": {"service.instance.id": "ff7d6f62-ce23-43b6-a5de-d5f61e47b366", "service.name": "otelcol-contrib", "service.version": "0.143.1"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}
リソース属性が変わるごとにLogRecordのかたまりが分かれていることが確認できます。また、noisy-appからのログは出力されていないことも確認できます。
LognrollのUIでも次のようにログメッセージが確認できます。

Lognrollはbodyの全文検索や、attributesやresource attributesでのフィルタリングも可能です。

以上で、OpenTelemetry Collectorを使用したsyslogメッセージの受信、パース、正規化、フィルタリング、リソース属性の設定、およびOTLPでのエクスポートの一連の流れを解説しました。 この設定を基にカスタマイズしてSyslogログの受信環境が構築できるでしょう。