AkurAI Build
Menu

Public repository / 811ef9b7c8ba017fa242f008efa90faab07a0454

AkurAI-Monitor / scripts/titan-monitor-agent

Latest change 400ab9d5bda6889053a6b7a3ef672c6d4144373b - monitor: structured log contract (level/target/pid/origin/fields), journald severity, per-level retention + row cap; titan agent by agent

#!/usr/bin/env bash
# Collection agent for the Titan workstation host.
#
# Samples the same metric namespace rust-monitor's own in-process collector
# uses (src/collectors.rs) and pushes each point to the central monitor's
# management API, attributed to this host's registered host_id. Run as a
# systemd oneshot on a timer (see titan-monitor-agent.service/.timer);
# each invocation is a single sample-and-push cycle.
#
# Config (env, normally supplied via /etc/titan-monitor-agent.env):
#   MONITOR_BASE_URL   default https://monitor.olibuijr.com
#   MONITOR_API_TOKEN  required — management bearer token (PassVault entry 25)
#   MONITOR_HOST_ID    required — this host's id in the `hosts` table
set -euo pipefail

BASE_URL="${MONITOR_BASE_URL:-https://monitor.olibuijr.com}"
TOKEN="${MONITOR_API_TOKEN:?MONITOR_API_TOKEN is required}"
HOST_ID="${MONITOR_HOST_ID:?MONITOR_HOST_ID is required}"
STATE_DIR="${STATE_DIRECTORY:-/var/lib/titan-monitor-agent}"
NET_STATE_FILE="$STATE_DIR/net.prev"

mkdir -p "$STATE_DIR"

push() {
  local name="$1" value="$2"
  curl -sf -m 5 -X POST "$BASE_URL/api/manage/metrics" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"host_id\":${HOST_ID},\"name\":\"${name}\",\"value\":${value}}" \
    >/dev/null || echo "titan-monitor-agent: failed to push ${name}" >&2
}

# --- cpu.usage: percent busy over a 1s /proc/stat sample window ---
read_cpu_ticks() {
  local line idle total field
  read -r line < /proc/stat
  # line: cpu user nice system idle iowait irq softirq steal guest guest_nice
  local -a f=($line)
  idle="${f[4]}"
  total=0
  for field in "${f[@]:1}"; do total=$((total + field)); done
  echo "$idle $total"
}
read -r idle1 total1 <<<"$(read_cpu_ticks)"
sleep 1
read -r idle2 total2 <<<"$(read_cpu_ticks)"
idle_delta=$((idle2 - idle1))
total_delta=$((total2 - total1))
if [ "$total_delta" -gt 0 ]; then
  cpu_usage=$(awk -v i="$idle_delta" -v t="$total_delta" 'BEGIN { printf "%.1f", (1 - i / t) * 100 }')
  push "cpu.usage" "$cpu_usage"
fi

# --- memory ---
mem_total_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
mem_available_kb=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
mem_used_kb=$((mem_total_kb - mem_available_kb))
mem_used_pct=$(awk -v u="$mem_used_kb" -v t="$mem_total_kb" 'BEGIN { printf "%.1f", (t > 0) ? (u / t * 100) : 0 }')
push "mem.total_kb" "$mem_total_kb"
push "mem.used_kb" "$mem_used_kb"
push "mem.available_kb" "$mem_available_kb"
push "mem.used_pct" "$mem_used_pct"

# --- disk (root filesystem) ---
read -r _ disk_total_kb disk_used_kb _ disk_used_pct_raw _ < <(df -kP / | tail -n1)
disk_used_pct="${disk_used_pct_raw%\%}"
disk_total_gb=$(awk -v k="$disk_total_kb" 'BEGIN { printf "%.1f", k / 1048576 }')
disk_used_gb=$(awk -v k="$disk_used_kb" 'BEGIN { printf "%.1f", k / 1048576 }')
push "disk.root.total_gb" "$disk_total_gb"
push "disk.root.used_gb" "$disk_used_gb"
push "disk.root.used_pct" "$disk_used_pct"

# --- load average ---
read -r load1 load5 load15 _ < /proc/loadavg
push "load.1m" "$load1"
push "load.5m" "$load5"
push "load.15m" "$load15"

# --- uptime ---
read -r uptime_seconds _ < /proc/uptime
push "uptime.seconds" "${uptime_seconds%.*}"

# --- network, per physical-looking interface (delta since last run) ---
declare -A prev_rx prev_tx
if [ -f "$NET_STATE_FILE" ]; then
  while IFS='=' read -r key val; do
    case "$key" in
      *_rx) prev_rx["${key%_rx}"]="$val" ;;
      *_tx) prev_tx["${key%_tx}"]="$val" ;;
    esac
  done < "$NET_STATE_FILE"
fi

: > "$NET_STATE_FILE.tmp"
while IFS=: read -r iface stats; do
  iface="${iface// /}"
  [ "$iface" = "lo" ] && continue
  [[ "$iface" == veth* || "$iface" == docker* || "$iface" == br-* ]] && continue
  read -r rx _ _ _ _ _ _ _ tx _ <<<"$stats"
  echo "${iface}_rx=${rx}" >> "$NET_STATE_FILE.tmp"
  echo "${iface}_tx=${tx}" >> "$NET_STATE_FILE.tmp"
  if [ -n "${prev_rx[$iface]:-}" ] && [ -n "${prev_tx[$iface]:-}" ]; then
    rx_delta=$((rx - prev_rx[$iface]))
    tx_delta=$((tx - prev_tx[$iface]))
    [ "$rx_delta" -lt 0 ] && rx_delta=0
    [ "$tx_delta" -lt 0 ] && tx_delta=0
    push "net.${iface}.rx_bytes" "$rx_delta"
    push "net.${iface}.tx_bytes" "$tx_delta"
  fi
done < <(tail -n +3 /proc/net/dev)
mv "$NET_STATE_FILE.tmp" "$NET_STATE_FILE"