頻道日誌

將 Discord 頻道活動以結構化 JSONL 檔案串流輸出,用於稽核、合規,以及分析用途。

問題背景

你需要可靠的稽核紀錄來追蹤 Discord 伺服器中發生的事件。可能是為了合規、社群分析,或只是要保留歷史記錄。Discord 內建稽核日誌欄位有限,也不會保存訊息內容。

解法

使用 discli listen 即時串流事件,並導向寫入日誌檔。每筆事件都是一個獨立的 JSON 物件(JSONL 格式,一行一筆),因此可以直接用 jqgrep 或 Python 工具快速解析與分析。

完整可執行範例

#!/bin/bash
# channel-logger.sh — 將 Discord 頻道訊息串流輸出為 JSONL 日誌。
CHANNEL="${1:?用法:$0 <channel> [logfile]}"
LOGFILE="${2:-discord-${CHANNEL}-$(date +%Y%m%d).jsonl}"
echo "開始記錄 #${CHANNEL} 到 ${LOGFILE}..." >&2
echo "按 Ctrl+C 可停止。" >&2
discli --json listen --channel "$CHANNEL" --events messages | while read -r event; do
# 將原始 JSON 事件逐行寫入日誌
echo "$event" >> "$LOGFILE"
# 在終端機輸出可讀摘要
author=$(echo "$event" | jq -r '.author')
content=$(echo "$event" | jq -r '.content')
timestamp=$(echo "$event" | jq -r '.timestamp[:19]' | tr 'T' ' ')
echo "[${timestamp}] ${author}: ${content}"
done
echo "記錄已停止。" >&2

執行方式:

Terminal window
chmod +x channel-logger.sh
./channel-logger.sh general
# 或改用自訂日誌檔:
./channel-logger.sh general /var/log/discord/general.jsonl
#!/usr/bin/env python3
"""channel_logger.py — 將 Discord 事件串流輸出為 JSONL,並每日輪換檔案。"""
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
CHANNEL = sys.argv[1] if len(sys.argv) > 1 else None
LOG_DIR = Path(sys.argv[2]) if len(sys.argv) > 2 else Path(".")
if not CHANNEL:
print("用法:python channel_logger.py <channel> [log_directory]", file=sys.stderr)
sys.exit(1)
LOG_DIR.mkdir(parents=True, exist_ok=True)
def get_log_path():
"""依日期產生日誌檔路徑,用於每日輪替。"""
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
return LOG_DIR / f"discord-{CHANNEL}-{date_str}.jsonl"
current_date = None
log_file = None
# 開始監聽
cmd = ["discli", "--json", "listen", "--channel", CHANNEL, "--events", "messages"]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
print(f"開始記錄 #{CHANNEL}{LOG_DIR}/。按 Ctrl+C 可停止。", file=sys.stderr)
try:
for line in proc.stdout:
line = line.strip()
if not line:
continue
try:
event = json.loads(line)
except json.JSONDecodeError:
continue
# 每日輪替
today = datetime.now(timezone.utc).date()
if today != current_date:
if log_file:
log_file.close()
log_file = open(get_log_path(), "a", encoding="utf-8")
current_date = today
print(f"[rotate] 正在寫入 {get_log_path()}", file=sys.stderr)
# 寫入原始 JSONL
log_file.write(json.dumps(event, default=str) + "\n")
log_file.flush()
# 在終端機輸出摘要
ts = event.get("timestamp", "")[:19].replace("T", " ")
author = event.get("author", "unknown")
content = event.get("content", "")
print(f"[{ts}] {author}: {content}")
except KeyboardInterrupt:
print("\n記錄器已停止。", file=sys.stderr)
finally:
if log_file:
log_file.close()
proc.terminate()

執行方式:

Terminal window
python channel_logger.py general ./logs
#!/bin/bash
# multi-logger.sh — 同步記錄多個頻道。
LOG_DIR="${1:-./discord-logs}"
mkdir -p "$LOG_DIR"
CHANNELS=("general" "support" "announcements" "dev-chat")
echo "正在記錄 ${#CHANNELS[@]} 個頻道到 ${LOG_DIR}/..." >&2
pids=()
for channel in "${CHANNELS[@]}"; do
logfile="${LOG_DIR}/${channel}-$(date +%Y%m%d).jsonl"
discli --json listen --channel "$channel" --events messages >> "$logfile" &
pids+=("$!")
echo " #${channel} -> ${logfile}" >&2
done
echo "按 Ctrl+C 可停止全部記錄器。" >&2
# 等待 Ctrl+C,之後清除
trap 'echo "正在停止..."; kill "${pids[@]}" 2>/dev/null; exit 0' INT TERM
wait

逐步解說

啟動事件串流

discli listen 會開啟與 Discord 的 WebSocket 連線,並以 JSONL 逐行輸出事件。--channel 用來指定頻道,--events messages 則只保留訊息事件。

Terminal window
discli --json listen --channel general --events messages

每一行輸出皆為完整 JSON 物件:

{"event":"message","server":"我的伺服器","channel":"general","channel_id":"123...","author":"Alice","author_id":"456...","content":"大家好!","timestamp":"2026-03-15T10:30:00+00:00","message_id":"789...","mentions_bot":false,"attachments":[],"reply_to":null}

寫入 JSONL 檔案

每筆 JSON 事件都以獨立一行附加到日誌檔。JSONL(JSON Lines)即每行皆為合法 JSON 物件,配合 jqsed、Python 都很容易處理。

Tip

建議每次 write 後呼叫 flush()(或使用逐行緩衝輸出),避免程序異常時遺失已接收的事件。

用 jq 分析

取得日誌後,可用 jq 提取關鍵訊息:

Terminal window
# 每位作者的訊息數
jq -r '.author' discord-general-2026-03-15.jsonl | sort | uniq -c | sort -rn
# 搜尋關鍵字
jq -r 'select(.content | test("bug"; "i")) | "\(.author): \(.content)"' discord-general-2026-03-15.jsonl
# 每小時訊息數
jq -r '.timestamp[:13]' discord-general-2026-03-15.jsonl | sort | uniq -c
# 列出所有作者
jq -r '.author' discord-general-2026-03-15.jsonl | sort -u
# 取出含附件的訊息
jq 'select(.attachments | length > 0)' discord-general-2026-03-15.jsonl

設定檔案輪替

Python 版本內建每日輪替,每天會自動建立新檔;bash 版本可自行在檔名加入日期,或搭配 logrotate

Terminal window
# 使用 cron 每日輪替(午夜)
# crontab -e
0 0 * * * pkill -f "discli.*listen.*general" && /path/to/channel-logger.sh general /var/log/discord/general-$(date +\%Y\%m\%d).jsonl &

邊際案例與陷阱

Warning

磁碟空間。 熱門頻道每天可能產生數十 MB 到數百 MB 日誌。建議監控磁碟用量,並定期清除舊檔。簡單做法可在 cron 加上:find /var/log/discord -name "*.jsonl" -mtime +30 -delete

Warning

編碼問題。 Discord 訊息可能包含各種 Unicode 字元(emoji、RTL 文字、零寬字元)。寫入檔案請固定使用 encoding="utf-8",並透過 json.dumps(event, default=str, ensure_ascii=False) 保留原始文字。

Warning

重啟期間事件遺失。 如果記錄器程序當機或重啟,當中間斷期間的事件會遺失。discli listen 僅支援即時串流,不會補播缺漏。若要零缺口日誌,建議搭配定期 discli message history 回補。

Warning

隱私與合規。 記錄訊息內容可能涉及隱私法規(GDPR、CCPA)。請在伺服器規範中聲明紀錄行為,並建立使用者刪除請求處理流程。

擴充方向

上傳到雲端儲存

將每日完成的日誌上傳到 S3、GCS 或 Azure Blob。可在午夜後用排程壓縮並上傳前一日檔案。

導入 SQLite 分析

將 JSONL 解析成 SQLite,建立訊息、作者、頻道等資料表,進行更彈性的查詢分析。

即時關鍵字告警

在串流上加上關鍵字過濾,遇到特定詞立即通知監控頻道或 webhook。可結合 Moderation Bot 模式。

Grafana 儀表板

將訊息數、活躍使用者、回應時間等指標輸出到 Prometheus / InfluxDB,再用 Grafana 做即時監控。