turnstone/scripts/collect_cluster_logs.sh
pyr0ball d198f3d269 feat: add cluster-wide log collection and Heimdall Turnstone deployment
- scripts/collect_cluster_logs.sh: collects journals from Heimdall (local),
  Navi, Sif, Cass, Strahl (SSH), Docker services, and a network syslog
  placeholder; designed for 15-min cron before ingest
- patterns/sources-cluster.yaml: ingest sources config for the full
  CircuitForge cluster stack; points at /devl/turnstone-cluster/data/
- scripts/docker-cluster.sh: Docker deployment for Heimdall cluster monitor;
  seeds preferences.json with cf-orch coordinator URL (localhost:7701) so
  LLM summarization works on first ingest without manual UI config
2026-05-12 18:53:58 -07:00

109 lines
4 KiB
Bash

#!/usr/bin/env bash
# Collect recent journal logs from all CircuitForge cluster nodes and network
# devices into /opt/turnstone/data/ for Turnstone to ingest.
#
# Run this before each ingest cycle (see cron below).
# Each remote node is collected via SSH; network devices via syslog-receiver.
#
# Prerequisites:
# - SSH key auth to each node (test: ssh <node> hostname)
# - syslog-receiver.sh running separately (or rsyslog) for network devices
#
# Cron (combined with ingest, every 15 min):
# */15 * * * * bash /opt/turnstone/scripts/collect_cluster_logs.sh && \
# docker exec turnstone-cluster python scripts/ingest_corpus.py \
# --sources /patterns/sources-cluster.yaml --db /data/turnstone.db \
# >> /var/log/turnstone-cluster-ingest.log 2>&1
#
# Manual run:
# bash /Library/Development/CircuitForge/turnstone/scripts/collect_cluster_logs.sh
set -euo pipefail
DATA_DIR=/devl/turnstone-cluster/data
WINDOW="20 minutes ago"
SSH_OPTS="-o ConnectTimeout=5 -o BatchMode=yes -o StrictHostKeyChecking=no"
mkdir -p "${DATA_DIR}"
# ── Local Heimdall journal ────────────────────────────────────────────────────
echo "heimdall: collecting local journal..."
journalctl \
--output=json \
--priority=0..5 \
--since "${WINDOW}" \
--no-pager \
> "${DATA_DIR}/heimdall-journal.jsonl"
echo "heimdall: $(wc -l < "${DATA_DIR}/heimdall-journal.jsonl") entries"
# Local kernel ring buffer
if dmesg -T &>/dev/null; then
dmesg -T > "${DATA_DIR}/heimdall-dmesg.txt"
else
dmesg > "${DATA_DIR}/heimdall-dmesg.txt"
fi
# ── Remote cluster nodes ──────────────────────────────────────────────────────
# Each entry: "<hostname> <output-file>"
declare -A NODES=(
[navi]="${DATA_DIR}/navi-journal.jsonl"
[sif]="${DATA_DIR}/sif-journal.jsonl"
[cass]="${DATA_DIR}/cass-journal.jsonl"
[strahl]="${DATA_DIR}/strahl-journal.jsonl"
)
for node in "${!NODES[@]}"; do
outfile="${NODES[$node]}"
echo "${node}: collecting journal..."
if ssh ${SSH_OPTS} "${node}" true 2>/dev/null; then
ssh ${SSH_OPTS} "${node}" \
"journalctl --output=json --priority=0..5 --since '${WINDOW}' --no-pager 2>/dev/null || true" \
> "${outfile}" 2>/dev/null || { echo "${node}: ssh failed, skipping"; : > "${outfile}"; }
echo "${node}: $(wc -l < "${outfile}") entries"
else
echo "${node}: unreachable, skipping"
: > "${outfile}"
fi
done
# ── Docker container logs from Heimdall ──────────────────────────────────────
# Collect logs from key Docker services running on Heimdall.
# Add or remove container names as needed.
DOCKER_CONTAINERS=(
cf-orch-coordinator
cf-web
cf-directus
caddy-proxy
)
for cname in "${DOCKER_CONTAINERS[@]}"; do
outfile="${DATA_DIR}/docker-${cname}.jsonl"
if docker inspect "${cname}" &>/dev/null 2>&1; then
# Docker log output: raw lines with timestamps; use json-file driver format
docker logs --since 20m "${cname}" 2>&1 | \
python3 -c "
import sys, json, time
src = '${cname}'
for line in sys.stdin:
line = line.rstrip()
if not line: continue
print(json.dumps({'MESSAGE': line, 'SYSLOG_IDENTIFIER': src, '_TRANSPORT': 'docker', 'PRIORITY': '6'}))
" > "${outfile}" 2>/dev/null || : > "${outfile}"
echo "docker/${cname}: $(wc -l < "${outfile}") entries"
else
: > "${outfile}"
fi
done
# ── Network syslog (written by syslog-receiver.service) ──────────────────────
# If the syslog receiver is running, it appends to this file.
# We don't rotate it here — ingest deduplicates by entry hash.
SYSLOG_FILE="${DATA_DIR}/network-syslog.txt"
if [ ! -f "${SYSLOG_FILE}" ]; then
: > "${SYSLOG_FILE}"
echo "network-syslog: created (empty — configure devices to send to port 5140)"
else
echo "network-syslog: $(wc -l < "${SYSLOG_FILE}") lines"
fi
echo "collect_cluster_logs: done"