Tóm tắt: Vector nhận syslog UDP 514 bằng source socket, chương trình VRL trong transform remap parse header syslog (parse_syslog), tách mnemonic kiểu %ROUTING-BGP-5-ADJCHANGE, rồi tra bảng CSV để gắn source (tên thiết bị) và site theo IP gửi. Kết quả đi hai đường: Loki (tìm log nhanh, label ít) và ClickHouse (lưu lâu, thống kê bằng SQL).
Luồng hoạt động
flowchart LR XR["IOS XR (UDP 514)"] --> S["Vector source socket"] JN["Junos (UDP 514)"] --> S S --> T["remap: parse_syslog + regex"] CSV["devices.csv (enrichment)"] -.-> T T --> L["sink loki"] T --> C["sink clickhouse"] L --> G[Grafana] C --> G
Cấu hình
Thiết bị gửi syslog
IOS XR:
logging trap informational
logging 198.51.100.50 vrf default
logging source-interface Loopback0Junos (structured-data gửi theo RFC 5424):
set system syslog host 198.51.100.50 any info
set system syslog host 198.51.100.50 structured-data
set system syslog source-address 198.51.100.1Đặt NTP đúng trên thiết bị: timestamp trong log lấy từ đồng hồ của router.
Bảng tra thiết bị
ip,name,site
198.51.100.1,pe1-hni,hni
198.51.100.2,pe2-hcm,hcmvector.yaml
api:
enabled: true # cho vector top / vector tap
address: 127.0.0.1:8686
enrichment_tables:
devices:
type: file
file:
path: /etc/vector/devices.csv
encoding:
type: csv
schema:
ip: string
name: string
site: string
sources:
syslog_udp:
type: socket
mode: udp
address: 0.0.0.0:514
transforms:
parse:
type: remap
inputs: [syslog_udp]
source: |
.source_ip = del(.host) # IP thiết bị gửi (peer UDP)
del(.source_type)
del(.port)
# Header syslog RFC 3164/5424 -> hostname, appname, severity, facility, timestamp, message
parsed, err = parse_syslog(.message)
if err == null {
. = merge(., parsed)
}
# Mnemonic Cisco: %ROUTING-BGP-5-ADJCHANGE
m, err = parse_regex(.message, r'%(?P<facility>[A-Z0-9_]+(?:-[A-Z0-9_]+)*)-(?P<sev>[0-7])-(?P<mnemonic>[A-Z0-9_]+)')
if err == null {
.mnemonic = m.mnemonic
.cisco_facility = m.facility
}
# Gắn tên thiết bị / site theo IP gửi
row, err = get_enrichment_table_record("devices", {"ip": .source_ip})
if err == null {
.source = row.name
.site = row.site
} else {
.source = string(.hostname) ?? string(.source_ip) ?? "unknown"
.site = "unknown"
}
if !exists(.severity) { .severity = "unknown" }
sinks:
loki:
type: loki
inputs: [parse]
endpoint: http://loki.example.net:3100
encoding:
codec: json
labels: # giữ label ít và ổn định
job: syslog
source: "{{ source }}"
site: "{{ site }}"
severity: "{{ severity }}"
clickhouse:
type: clickhouse
inputs: [parse]
endpoint: http://clickhouse.example.net:8123
database: logs
table: syslog
skip_unknown_fields: true # field thừa (procid, msgid...) bỏ qua
date_time_best_effort: true # nhận timestamp ISO 8601 của Vector
auth:
strategy: basic
user: vector
password: "${CLICKHOUSE_PASSWORD}"
batch:
max_events: 10000
timeout_secs: 5Vector thay ${VAR} bằng biến môi trường lúc nạp config, nên password để trong /etc/default/vector (gói deb/rpm đọc file này) thay vì ghi thẳng vào YAML. Syslog qua TCP thì thêm một source socket với mode: tcp.
Bảng ClickHouse
CREATE DATABASE IF NOT EXISTS logs;
CREATE TABLE logs.syslog
(
timestamp DateTime64(3, 'UTC'),
source LowCardinality(String),
source_ip String,
site LowCardinality(String),
severity LowCardinality(String),
facility LowCardinality(String),
appname LowCardinality(String),
mnemonic LowCardinality(String),
message String
)
ENGINE = MergeTree
PARTITION BY toDate(timestamp)
ORDER BY (source, timestamp)
TTL toDateTime(timestamp) + INTERVAL 180 DAY;Giải thích ORDER BY, PARTITION BY, TTL và cách thêm bảng tổng hợp xem ClickHouse schema và TTL.
Chạy và kiểm tra
vector validate /etc/vector/vector.yaml # kiểm config + healthcheck sink
vector validate --no-environment /etc/vector/vector.yaml # chỉ kiểm cú pháp, không gọi Loki/ClickHouse
systemctl restart vector && journalctl -u vector -f
logger -n 127.0.0.1 -P 514 -d --rfc3164 -t bgp '%ROUTING-BGP-5-ADJCHANGE : neighbor 203.0.113.1 Up'
vector tap --outputs-of parse # xem event sau remap (cần api.enabled)
vector top # throughput/lỗi từng component
vector vrl # REPL để thử chương trình VRLTruy vấn log trên Loki xem LogQL cheatsheet. Trên ClickHouse:
SELECT source, mnemonic, count() AS n
FROM logs.syslog
WHERE timestamp > now() - INTERVAL 1 HOUR AND mnemonic != ''
GROUP BY source, mnemonic ORDER BY n DESC LIMIT 20;Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
Vector không start, Permission denied khi bind :514 |
Port < 1024 cần quyền | Thêm AmbientCapabilities=CAP_NET_BIND_SERVICE vào unit, hoặc listen 5514 rồi DNAT |
tcpdump -ni any udp port 514 thấy gói nhưng không có event |
Firewall host chặn (nftables), sai address |
Mở UDP 514, ss -lunp | grep 514 |
Lỗi compile VRL unhandled fallible assignment |
Gọi hàm có thể lỗi mà không xử lý | Dùng x, err = f(...), f!(...) hoặc ?? giá trị mặc định |
Lỗi VRL unnecessary error coalesce |
Dùng ?? sau biểu thức không thể lỗi |
Bỏ ??, dùng if exists(...) |
source luôn là IP |
IP gửi không có trong devices.csv (router dùng source khác Loopback) |
Đặt source-interface / source-address, bổ sung CSV |
Loki báo entry too far behind hoặc timestamp too old |
Đồng hồ router lệch, Loki từ chối log quá cũ | Sửa NTP; chỉnh reject_old_samples_max_age phía Loki |
Loki chậm, maximum active stream limit exceeded |
Label động (peer, interface) | Chỉ để job/source/site/severity làm label |
ClickHouse Cannot parse input ... DateTime64 |
Định dạng timestamp | Bật date_time_best_effort: true |
ClickHouse Unknown field found while parsing JSONEachRow |
Event có field không có cột | skip_unknown_fields: true hoặc thêm cột |
ClickHouse TOO_MANY_PARTS |
Batch quá nhỏ | Tăng batch.max_events / timeout_secs, xem ClickHouse xử lý sự cố |