feat: initial commit
This commit is contained in:
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# Podgląd logów backendu LLM - jedno wejście niezależnie od tego, czy backend
|
||||
# działa w kontenerze (vllm-gpu) czy jako proces z plikiem logu (vllm-metal, ollama, mock).
|
||||
#
|
||||
# Użycie:
|
||||
# logs.sh --backend vllm-gpu [--tail 100] [--follow|--no-follow] [--grep WZORZEC] [--save PLIK]
|
||||
#
|
||||
# Domyślnie śledzi na bieżąco (jak `tail -f`). --no-follow robi jednorazowy zrzut,
|
||||
# co jest tym, czego się chce przy wklejaniu fragmentu do zgłoszenia.
|
||||
set -euo pipefail
|
||||
|
||||
# Ctrl-C przy śledzeniu logów to normalne zakończenie, nie awaria - bez tego
|
||||
# go-task raportowałby "Failed to run task" za każdym razem, gdy ktoś przerwie podgląd.
|
||||
trap 'exit 0' INT TERM
|
||||
|
||||
BACKEND=""
|
||||
RUN_DIR=".runs/llm"
|
||||
TAIL="100"
|
||||
FOLLOW="true"
|
||||
PATTERN=""
|
||||
SAVE=""
|
||||
COMPOSE_FILE="llm/docker-compose.yml"
|
||||
|
||||
usage() { sed -n '2,12p' "$0" >&2; exit "${1:-1}"; }
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--backend) BACKEND="${2:?}"; shift 2 ;;
|
||||
--run-dir) RUN_DIR="${2:?}"; shift 2 ;;
|
||||
--tail) TAIL="${2:?}"; shift 2 ;;
|
||||
--grep) PATTERN="${2:?}"; shift 2 ;;
|
||||
--save) SAVE="${2:?}"; shift 2 ;;
|
||||
--follow) FOLLOW="true"; shift ;;
|
||||
--no-follow) FOLLOW="false"; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) echo "nieznany argument: $1" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$BACKEND" ] || { echo "podaj --backend" >&2; exit 1; }
|
||||
# Zapis do pliku bez sensu w trybie śledzenia - zrzut ma się skończyć.
|
||||
[ -n "$SAVE" ] && FOLLOW="false"
|
||||
|
||||
log_file_for() {
|
||||
case "$1" in
|
||||
vllm-metal) echo "$RUN_DIR/vllm-metal.log" ;;
|
||||
ollama) echo "$RUN_DIR/ollama.log" ;;
|
||||
mock) echo "$RUN_DIR/mock.log" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Strumień logów na stdout, zależnie od backendu.
|
||||
emit() {
|
||||
if [ "$BACKEND" = "vllm-gpu" ]; then
|
||||
command -v docker >/dev/null 2>&1 || { echo "brak dockera - a backend vllm-gpu działa w kontenerze" >&2; exit 1; }
|
||||
if [ "$FOLLOW" = "true" ]; then
|
||||
docker compose -f "$COMPOSE_FILE" --profile vllm-gpu logs --follow --tail "$TAIL"
|
||||
else
|
||||
docker compose -f "$COMPOSE_FILE" --profile vllm-gpu logs --tail "$TAIL"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
local file
|
||||
file="$(log_file_for "$BACKEND")"
|
||||
if [ -z "$file" ]; then
|
||||
echo "backend '$BACKEND' nie ma znanego źródła logów" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "brak pliku logu: $file" >&2
|
||||
echo "backend prawdopodobnie nie był uruchamiany - zacznij od: task llm:up" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$FOLLOW" = "true" ]; then
|
||||
# --follow=name odtwarza plik po rotacji/nadpisaniu, np. przy restarcie backendu
|
||||
tail -n "$TAIL" -F "$file"
|
||||
else
|
||||
tail -n "$TAIL" "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
filter() {
|
||||
if [ -n "$PATTERN" ]; then
|
||||
# --line-buffered: przy śledzeniu linie mają się pojawiać od razu, a nie po zapełnieniu bufora
|
||||
grep --line-buffered -Ei "$PATTERN" || true
|
||||
else
|
||||
cat
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "$SAVE" ]; then
|
||||
mkdir -p "$(dirname "$SAVE")"
|
||||
emit | filter > "$SAVE"
|
||||
echo "zapisano $(wc -l < "$SAVE" | tr -d ' ') linii do $SAVE"
|
||||
elif [ "$FOLLOW" = "true" ]; then
|
||||
emit | filter
|
||||
else
|
||||
# Jednorazowy zrzut: pusty wynik przy filtrze jest dwuznaczny (brak dopasowań
|
||||
# czy brak logów?), więc mówimy to wprost zamiast milczeć.
|
||||
output="$(emit | filter)"
|
||||
if [ -z "$output" ] && [ -n "$PATTERN" ]; then
|
||||
echo "brak linii pasujących do '$PATTERN' w ostatnich $TAIL liniach logu" >&2
|
||||
else
|
||||
printf '%s\n' "$output"
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user