more logs

This commit is contained in:
parent f61801e855
commit 2715c56b4f
2 changed files with 105 additions and 0 deletions

View file

@ -1,4 +1,46 @@
import ReactDOM from "react-dom/client";
import App from "./App";
const backendBase =
(import.meta.env.VITE_BACKEND_URL as string | undefined) ?? "http://localhost:8000";
const sendFrontLog = (message: string, level: "info" | "warn" | "error" = "info") => {
fetch(`${backendBase}/logs`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, level, source: "frontend" }),
}).catch(() => {});
};
window.addEventListener("error", (event) => {
const err = event.error instanceof Error ? event.error : null;
const details = err?.stack || err?.message || String(event.message || "unknown error");
sendFrontLog(`window.error: ${details}`, "error");
});
window.addEventListener("unhandledrejection", (event) => {
const reason = event.reason instanceof Error ? event.reason.stack || event.reason.message : String(event.reason);
sendFrontLog(`unhandledrejection: ${reason}`, "error");
});
const _consoleError = console.error.bind(console);
console.error = (...args: unknown[]) => {
try {
sendFrontLog(`console.error: ${args.map(String).join(" ")}`, "error");
} catch {
// ignore
}
_consoleError(...args);
};
const _consoleWarn = console.warn.bind(console);
console.warn = (...args: unknown[]) => {
try {
sendFrontLog(`console.warn: ${args.map(String).join(" ")}`, "warn");
} catch {
// ignore
}
_consoleWarn(...args);
};
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(<App />);

View file

@ -1,5 +1,7 @@
import argparse
import os
import sys
from pathlib import Path
from typing import Optional
import uvicorn
@ -12,19 +14,80 @@ def _env(name: str, default: str) -> str:
return v if v is not None and v != "" else default
class _TeeStream:
def __init__(self, primary, secondary):
self._primary = primary
self._secondary = secondary
def write(self, data):
try:
self._primary.write(data)
except Exception:
pass
try:
self._secondary.write(data)
except Exception:
pass
return len(data)
def flush(self):
try:
self._primary.flush()
except Exception:
pass
try:
self._secondary.flush()
except Exception:
pass
def isatty(self):
try:
return self._primary.isatty()
except Exception:
return False
def _ensure_uvicorn_logs_to_file(log_path: Path) -> None:
import logging
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_path, encoding="utf-8")
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s"))
for name in ("uvicorn", "uvicorn.error", "uvicorn.access"):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
if not any(
isinstance(h, logging.FileHandler) and getattr(h, "baseFilename", None) == str(log_path)
for h in logger.handlers
):
logger.addHandler(file_handler)
logger.propagate = True
def main(argv: Optional[list[str]] = None) -> int:
parser = argparse.ArgumentParser(prog="gnss", description="GNSS Backend (FastAPI)")
parser.add_argument("--host", default=_env("GNSS_HOST", "0.0.0.0"))
parser.add_argument("--port", type=int, default=int(_env("GNSS_PORT", "8000")))
parser.add_argument("--log-level", default=_env("GNSS_LOG_LEVEL", "info"))
parser.add_argument("--log-file", default=_env("GNSS_BACKEND_LOG_FILE", "logs/backend.log"))
args = parser.parse_args(argv)
log_path = Path(args.log_file).expanduser()
log_path.parent.mkdir(parents=True, exist_ok=True)
backend_log_file = log_path.open("a", encoding="utf-8", buffering=1)
sys.stdout = _TeeStream(sys.stdout, backend_log_file) # type: ignore[assignment]
sys.stderr = _TeeStream(sys.stderr, backend_log_file) # type: ignore[assignment]
_ensure_uvicorn_logs_to_file(log_path)
uvicorn.run(
app,
host=args.host,
port=args.port,
log_level=args.log_level,
access_log=True,
log_config=None,
workers=1,
)
return 0