From 1104ed936cbec0f91e88459ea2324a5a42ce7817 Mon Sep 17 00:00:00 2001 From: q Date: Sat, 18 Apr 2026 15:37:04 +0300 Subject: [PATCH] Remove logviewer, clean up docker-compose logging sections Co-Authored-By: Claude Sonnet 4.6 --- docker-compose.yml | 18 ---- logviewer/analyze.py | 179 ----------------------------------- logviewer/docker-compose.yml | 12 --- 3 files changed, 209 deletions(-) delete mode 100755 logviewer/analyze.py delete mode 100644 logviewer/docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml index 96c4b49..c29667a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,15 +44,6 @@ services: - qdrant ports: - "8001:8000" - logging: - driver: json-file - options: - max-size: "20m" - max-file: "5" - tag: "index-service" - labels: "service" - labels: - - "service=index-service" search: build: @@ -64,12 +55,3 @@ services: condition: service_completed_successfully ports: - "8002:8000" - logging: - driver: json-file - options: - max-size: "20m" - max-file: "5" - tag: "search-service" - labels: "service" - labels: - - "service=search-service" diff --git a/logviewer/analyze.py b/logviewer/analyze.py deleted file mode 100755 index 291442e..0000000 --- a/logviewer/analyze.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python3 -""" -CLI tool to query and analyze Docker logs from index-service and search-service. - -Usage: - python3 analyze.py # tail both services, live - python3 analyze.py --service search # filter by service - python3 analyze.py --level ERROR # filter by log level - python3 analyze.py --grep "qdrant" # grep in log message - python3 analyze.py --last 100 # last N lines per service - python3 analyze.py --since 10m # since N minutes/hours ago (e.g. 10m, 2h) - python3 analyze.py --stats # show request count / error rate summary -""" - -import argparse -import json -import re -import subprocess -import sys -from datetime import datetime, timezone - -SERVICES = { - "index": "hackaton-index-1", - "search": "hackaton-search-1", -} - - -def _run(cmd: list[str], **kwargs) -> subprocess.CompletedProcess: - return subprocess.run(cmd, capture_output=True, text=True, **kwargs) - - -def _detect_container_name(service: str) -> str: - """Find the actual running container name for a service.""" - candidates = [ - f"hackaton-{service}-1", - f"hackaton_{service}_1", - f"{service}-1", - f"{service}_1", - ] - result = _run(["docker", "ps", "--format", "{{.Names}}"]) - running = result.stdout.splitlines() - for name in candidates: - if name in running: - return name - # fallback: try matching by label - result2 = _run( - ["docker", "ps", "--filter", f"label=service={service}-service", "--format", "{{.Names}}"] - ) - names = result2.stdout.strip().splitlines() - if names: - return names[0] - return candidates[0] - - -def _docker_logs(container: str, since: str | None, last: int) -> list[str]: - cmd = ["docker", "logs", "--timestamps"] - if since: - cmd += ["--since", since] - if last: - cmd += ["--tail", str(last)] - cmd.append(container) - result = _run(cmd) - lines = (result.stdout + result.stderr).splitlines() - return lines - - -def _parse_line(raw: str) -> dict: - """Try to extract structured fields from a log line.""" - # Docker prepends an RFC3339 timestamp - ts_match = re.match(r"^(\d{4}-\d{2}-\d{2}T[\d:.+Z-]+)\s+(.*)", raw) - ts = "" - message = raw - if ts_match: - ts = ts_match.group(1) - message = ts_match.group(2) - - level = "INFO" - for lvl in ("CRITICAL", "ERROR", "WARNING", "WARN", "INFO", "DEBUG"): - if lvl in message.upper(): - level = lvl if lvl != "WARN" else "WARNING" - break - - return {"ts": ts, "level": level, "message": message, "raw": raw} - - -def _color(level: str) -> str: - return { - "ERROR": "\033[31m", - "CRITICAL": "\033[35m", - "WARNING": "\033[33m", - "INFO": "\033[0m", - "DEBUG": "\033[36m", - }.get(level, "\033[0m") - - -RESET = "\033[0m" - - -def cmd_tail(args: argparse.Namespace) -> None: - services = [args.service] if args.service else list(SERVICES.keys()) - for svc in services: - container = _detect_container_name(svc) - lines = _docker_logs(container, args.since, args.last) - print(f"\n{'='*60}") - print(f" {svc.upper()} SERVICE ({container})") - print(f"{'='*60}") - count = 0 - for raw in lines: - parsed = _parse_line(raw) - if args.level and parsed["level"] != args.level.upper(): - continue - if args.grep and args.grep.lower() not in parsed["message"].lower(): - continue - color = _color(parsed["level"]) - print(f"{color}{parsed['raw']}{RESET}") - count += 1 - print(f" → {count} lines shown") - - -def cmd_stats(args: argparse.Namespace) -> None: - services = [args.service] if args.service else list(SERVICES.keys()) - for svc in services: - container = _detect_container_name(svc) - lines = _docker_logs(container, args.since, args.last) - - counts: dict[str, int] = {"ERROR": 0, "WARNING": 0, "INFO": 0, "DEBUG": 0, "CRITICAL": 0} - requests = 0 - errors_5xx = 0 - - for raw in lines: - parsed = _parse_line(raw) - lvl = parsed["level"] - counts[lvl] = counts.get(lvl, 0) + 1 - msg = parsed["message"] - if re.search(r'"(GET|POST|PUT|DELETE|PATCH)\s', msg): - requests += 1 - if re.search(r'" 5\d\d ', msg): - errors_5xx += 1 - - print(f"\n{'='*50}") - print(f" STATS: {svc.upper()} SERVICE") - print(f"{'='*50}") - print(f" Total lines : {sum(counts.values())}") - print(f" HTTP requests: {requests}") - print(f" 5xx errors : {errors_5xx}") - print(f" ERROR lines : {counts['ERROR']}") - print(f" WARNING lines: {counts['WARNING']}") - print(f" INFO lines : {counts['INFO']}") - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Analyze Docker logs for index-service and search-service", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__doc__, - ) - parser.add_argument("--service", choices=list(SERVICES.keys()), help="Filter by service") - parser.add_argument("--level", help="Filter by log level (ERROR, WARNING, INFO, DEBUG)") - parser.add_argument("--grep", help="Substring filter on log message") - parser.add_argument("--last", type=int, default=200, help="Last N lines per service (default 200)") - parser.add_argument("--since", help="Show logs since duration (e.g. 10m, 2h, 30s)") - parser.add_argument("--stats", action="store_true", help="Show stats summary instead of log lines") - - args = parser.parse_args() - - try: - if args.stats: - cmd_stats(args) - else: - cmd_tail(args) - except KeyboardInterrupt: - print("\nInterrupted.") - except FileNotFoundError: - print("ERROR: docker not found in PATH", file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/logviewer/docker-compose.yml b/logviewer/docker-compose.yml deleted file mode 100644 index 834e47b..0000000 --- a/logviewer/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - dozzle: - image: amir20/dozzle:latest - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - ports: - - "9999:8080" - environment: - DOZZLE_FILTER: "label=service" - DOZZLE_LEVEL: info - DOZZLE_ENABLE_ACTIONS: "false" - restart: unless-stopped