61 lines
2.0 KiB
Python
61 lines
2.0 KiB
Python
from datetime import datetime
|
|
import asyncio
|
|
import json
|
|
|
|
async def get_systemd_logs(lines: int = 200, service_name: str = "smartwave") -> list[dict]:
|
|
"""
|
|
Asynchronously fetches the last N log entries from a systemd service
|
|
using native asyncio subprocess execution.
|
|
"""
|
|
cmd = (
|
|
"journalctl",
|
|
"-u", service_name,
|
|
"-n", str(lines),
|
|
"-o", "json",
|
|
"--no-pager"
|
|
)
|
|
|
|
try:
|
|
# Create non-blocking child process
|
|
proc = await asyncio.create_subprocess_exec(
|
|
*cmd,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE
|
|
)
|
|
|
|
# Await completion and read output non-blockingly
|
|
stdout_bytes, stderr_bytes = await proc.communicate()
|
|
|
|
if proc.returncode != 0:
|
|
print(f"[Logs] journalctl error (code {proc.returncode}): {stderr_bytes.decode('utf-8', errors='replace')}")
|
|
return []
|
|
|
|
structured_logs = []
|
|
stdout_text = stdout_bytes.decode("utf-8", errors="replace")
|
|
|
|
for line in stdout_text.strip().split("\n"):
|
|
if not line:
|
|
continue
|
|
try:
|
|
entry = json.loads(line)
|
|
|
|
raw_ts = entry.get("__REALTIME_TIMESTAMP")
|
|
timestamp_iso = None
|
|
if raw_ts:
|
|
timestamp_iso = datetime.fromtimestamp(int(raw_ts) / 1_000_000).isoformat()
|
|
|
|
structured_logs.append({
|
|
"timestamp": timestamp_iso,
|
|
"timestamp_us": int(raw_ts) if raw_ts else None,
|
|
"message": entry.get("MESSAGE", ""),
|
|
"priority": entry.get("PRIORITY"),
|
|
"pid": entry.get("_PID"),
|
|
})
|
|
except (json.JSONDecodeError, ValueError):
|
|
continue
|
|
|
|
return structured_logs
|
|
|
|
except Exception as e:
|
|
print(f"[Logs] Failed to read systemd logs for '{service_name}': {e}")
|
|
return [] |