102 lines
3.0 KiB
Python
102 lines
3.0 KiB
Python
"""Logs daemon handler.
|
|
|
|
Reads system logs and journal entries.
|
|
"""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from daemon.iface import (
|
|
GET_LOGS_APP,
|
|
GET_LOGS_DNSMASQ,
|
|
GET_LOGS_JOURNAL,
|
|
GET_LOGS_NGINX_ACCESS,
|
|
GET_LOGS_NGINX_ERROR,
|
|
)
|
|
from daemon.server import NotFoundError, registry
|
|
from lib.common import run_proc
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PROJECT_DIR = Path(__file__).resolve().parent.parent.parent
|
|
_APP_LOG_FILE = PROJECT_DIR / "data" / "logs" / "vacuum-wall.log"
|
|
_MAX_LINES = 200
|
|
|
|
|
|
def _tail_file(path: str, n: int = _MAX_LINES, sudo: bool = False) -> str:
|
|
"""Return the last N lines of a file, optionally via sudo.
|
|
|
|
Args:
|
|
path: Absolute path to the file to read.
|
|
n: Number of trailing lines to return.
|
|
sudo: Whether to use sudo to access the file.
|
|
|
|
Returns:
|
|
Truncated file content or an error message string.
|
|
"""
|
|
try:
|
|
if sudo:
|
|
result = run_proc(["cat", path], sudo=True)
|
|
lines = result.stdout.splitlines(keepends=True)
|
|
else:
|
|
with open(path) as f:
|
|
lines = f.readlines()
|
|
return "".join(lines[-n:])
|
|
except FileNotFoundError:
|
|
raise NotFoundError("log file not found") from None
|
|
except PermissionError:
|
|
raise RuntimeError("permission denied") from None
|
|
|
|
|
|
def _sudo_journalctl(unit: str, n: int = _MAX_LINES) -> str:
|
|
"""Return recent journalctl output for a systemd unit via sudo.
|
|
|
|
Args:
|
|
unit: Systemd unit name to query.
|
|
n: Number of journal lines to return.
|
|
|
|
Returns:
|
|
Journal output or an error message string.
|
|
"""
|
|
try:
|
|
result = run_proc(
|
|
["journalctl", "--unit=" + unit, "-n", str(n)],
|
|
sudo=True,
|
|
check=False,
|
|
timeout=10,
|
|
)
|
|
output = result.stdout.strip()
|
|
return output if output else f"(no journal entries for {unit})\n"
|
|
except Exception as exc:
|
|
raise RuntimeError(f"error reading journal: {exc}") from exc
|
|
|
|
|
|
@registry.register(GET_LOGS_JOURNAL)
|
|
def journal(_request, _body) -> str:
|
|
"""GET /logs/journal — return vacuum-wall daemon journal entries."""
|
|
return _sudo_journalctl("vacuum-wall")
|
|
|
|
|
|
@registry.register(GET_LOGS_NGINX_ACCESS)
|
|
def nginx_access(_request, _body) -> str:
|
|
"""GET /logs/nginx/access — return recent nginx access log lines."""
|
|
return _tail_file("/var/log/nginx/access.log", sudo=True)
|
|
|
|
|
|
@registry.register(GET_LOGS_NGINX_ERROR)
|
|
def nginx_error(_request, _body) -> str:
|
|
"""GET /logs/nginx/error — return recent nginx error log lines."""
|
|
return _tail_file("/var/log/nginx/error.log", sudo=True)
|
|
|
|
|
|
@registry.register(GET_LOGS_DNSMASQ)
|
|
def dnsmasq_log(_request, _body) -> str:
|
|
"""GET /logs/dnsmasq — return recent dnsmasq journal entries."""
|
|
return _sudo_journalctl("dnsmasq")
|
|
|
|
|
|
@registry.register(GET_LOGS_APP)
|
|
def app_log(_request, _body) -> str:
|
|
"""GET /logs/app — return recent application log lines."""
|
|
return _tail_file(str(_APP_LOG_FILE))
|