Monitoring & Logs
Log locations
All services write to the systemd journal. There are no separate log files by default.
# Live tails
journalctl --user -u shiloh-mixer -f
journalctl --user -u shiloh-web-relay -f
journalctl --user -u mixer_web -f
# Combined view
journalctl --user -u shiloh-mixer -u shiloh-web-relay -u mixer_web -f
# Time-bounded lookback
journalctl --user -u shiloh-mixer --since "30 min ago"
# Last N lines
journalctl --user -u shiloh-mixer -n 100
Log verbosity
Set via the RUST_LOG environment variable. Default is info.
| Level | What is logged |
|---|---|
error |
Crashes, unrecoverable failures only |
warn |
REJECT_TX, xruns, dropped packets, mixer unreachable transitions |
info |
Connections, disconnections, session lifecycle, config summary |
debug |
Per-packet events, ring buffer stats, all MIDI events |
Change verbosity temporarily:
systemctl --user edit shiloh-mixer
# Add or update:
# [Service]
# Environment=RUST_LOG=debug
systemctl --user daemon-reload && systemctl --user restart shiloh-mixer
Useful scoped log targets:
# Debug only ingest, info for everything else
RUST_LOG=shiloh_mixer::ingest=debug,info
# Protocol decoder debug
RUST_LOG=shiloh_protocol=debug,info
Log rotation
The systemd journal rotates automatically based on disk use. On most systems the default max journal size is 10% of filesystem or 4 GB. To cap it for a low-disk server:
# In /etc/systemd/journald.conf (or a drop-in)
[Journal]
SystemMaxUse=500M
Then systemctl restart systemd-journald.
Verbose stats output (--verbose)
The broadcaster and relay clients support a --verbose flag that prints per-second stats:
shiloh-broadcaster connect --server HOST:5005 --name studio --channels 2 --verbose
# → [stat] 375 pps underruns=0 ring_full=0
| Field | Meaning |
|---|---|
pps |
Audio packets sent per second (nominal: 375 at 48 kHz / 128 frames) |
underruns |
JACK callback missed its deadline; silence was sent instead |
ring_full |
Ingest ring overflow on the mixer side; frames were dropped |
underruns > 0 means the local JACK buffer is too small or the CPU is overloaded. See Performance Tuning.
ring_full > 0 (visible in mixer logs as ingest_drop) means the mixer’s RT thread is not consuming ingest samples fast enough.
State file health check
The mixer writes state files to ~/mixer-state/ at runtime:
# Watch mtime — should advance every ~100 ms
watch -n 1 'stat -c "%y %n" ~/mixer-state/meters.json'
# Quick check: is meters.json recent?
python3 -c "
import os, time
age = time.time() - os.path.getmtime(os.path.expanduser('~/mixer-state/meters.json'))
print(f'meters.json age: {age:.1f}s', 'OK' if age < 2 else 'STALE')
"
If meters.json is not updating, the mixer’s meters thread is stuck. Restart shiloh-mixer.
Heartbeat watchdog
The mixer includes an internal heartbeat that ticks the meters.json file. You can hook into this from a simple shell watchdog:
# Simple shell watchdog (run in a tmux or as a cron job)
while true; do
age=$(( $(date +%s) - $(stat -c %Y ~/mixer-state/meters.json) ))
if [ "$age" -gt 5 ]; then
echo "$(date): meters.json stale (${age}s), restarting mixer"
systemctl --user restart shiloh-mixer
fi
sleep 3
done
Systemd OnFailure alerting
Systemd can run a unit when another fails. To send a notification (example uses curl to post to a webhook):
Create ~/.config/systemd/user/notify-failure@.service:
[Unit]
Description=Notify on failure of %i
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'curl -s -X POST https://hooks.example.com/alert \
-H "Content-Type: application/json" \
-d "{\"text\": \"shiloh: %i failed on $(hostname)\"}"'
Add to shiloh-mixer.service (via drop-in):
systemctl --user edit shiloh-mixer
# Add:
# [Unit]
# OnFailure=notify-failure@shiloh-mixer.service
systemctl --user daemon-reload
Reading sessions.json
~/mixer-state/sessions.json lists all currently connected relay and broadcaster sessions. Useful for scripting:
# Count connected relays
python3 -c "import json; s=json.load(open(os.path.expanduser('~/mixer-state/sessions.json'))); print(len(s))"
# Names of connected clients
jq '.[].name' ~/mixer-state/sessions.json
Checking the web relay’s PeerConnections
shiloh-web-relay logs each browser connection lifecycle:
journalctl --user -u shiloh-web-relay | grep "PC state"
# PC state: connected (ICE succeeded, audio flowing)
# PC state: failed (ICE failed, probably NAT/firewall)
# PC state: closed (browser tab closed)
Count active browser connections:
journalctl --user -u shiloh-web-relay --since "10 min ago" | \
grep -c "PC state: connected"