mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-07-28 12:33:23 +02:00
refactor: enhance logging and error handling in API endpoints and HTTP server
This commit is contained in:
@@ -38,6 +38,8 @@ debian/openhop-repeater.substvars
|
||||
env/
|
||||
ENV/
|
||||
|
||||
.ruff_cache
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import re
|
||||
import secrets
|
||||
import time
|
||||
from concurrent.futures import TimeoutError as FutureTimeoutError
|
||||
from datetime import datetime, timezone
|
||||
from typing import Callable, Optional
|
||||
|
||||
@@ -1587,6 +1588,7 @@ class APIEndpoints:
|
||||
return self._error("Event loop not available")
|
||||
import asyncio
|
||||
|
||||
future = None
|
||||
future = asyncio.run_coroutine_threadsafe(self.send_advert_func(), self.event_loop)
|
||||
result = future.result(timeout=10)
|
||||
return (
|
||||
@@ -1594,11 +1596,22 @@ class APIEndpoints:
|
||||
if result
|
||||
else self._error("Failed to send advert")
|
||||
)
|
||||
except FutureTimeoutError:
|
||||
logger.error(
|
||||
"Error sending advert: timeout waiting for advert transmission to complete",
|
||||
exc_info=True,
|
||||
)
|
||||
try:
|
||||
if future is not None:
|
||||
future.cancel()
|
||||
except Exception:
|
||||
pass
|
||||
return self._error("Timed out waiting for advert transmission after 10 seconds")
|
||||
except cherrypy.HTTPError:
|
||||
# Re-raise HTTP errors (like 405 Method Not Allowed) without logging
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending advert: {e}", exc_info=True)
|
||||
logger.error("Error sending advert: %s", e, exc_info=True)
|
||||
return self._error(e)
|
||||
|
||||
@cherrypy.expose
|
||||
|
||||
@@ -94,7 +94,10 @@ class LogBuffer(logging.Handler):
|
||||
}
|
||||
|
||||
if record.exc_info:
|
||||
entry["exception"] = self.formatException(record.exc_info)
|
||||
formatter = self.formatter or logging.Formatter()
|
||||
entry["exception"] = self._sanitize_log_text(
|
||||
formatter.formatException(record.exc_info)
|
||||
)
|
||||
|
||||
with self._lock:
|
||||
self.logs.append(entry)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, mock_open, patch
|
||||
from concurrent.futures import TimeoutError as FutureTimeoutError
|
||||
|
||||
import cherrypy
|
||||
import pytest
|
||||
@@ -1279,6 +1280,15 @@ def test_send_advert_paths(cherrypy_ctx):
|
||||
bad = api.send_advert()
|
||||
assert bad["success"] is False
|
||||
|
||||
future_timeout = MagicMock()
|
||||
future_timeout.result.side_effect = FutureTimeoutError()
|
||||
with patch("asyncio.run_coroutine_threadsafe", return_value=future_timeout):
|
||||
timeout = api.send_advert()
|
||||
|
||||
assert timeout["success"] is False
|
||||
assert "Timed out waiting for advert transmission" in timeout["error"]
|
||||
future_timeout.cancel.assert_called_once()
|
||||
|
||||
|
||||
def test_set_mode_and_set_duty_cycle_paths(cherrypy_ctx):
|
||||
request, _ = cherrypy_ctx
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import io
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
@@ -47,6 +48,29 @@ def test_log_buffer_emit_redacts_sensitive_values():
|
||||
assert "raw_message" not in entry
|
||||
|
||||
|
||||
def test_log_buffer_emit_includes_exception_text_without_crashing():
|
||||
buf = hs.LogBuffer(max_lines=5)
|
||||
try:
|
||||
raise RuntimeError("boom password=secret123")
|
||||
except RuntimeError:
|
||||
rec = logging.LogRecord(
|
||||
"x",
|
||||
logging.ERROR,
|
||||
__file__,
|
||||
20,
|
||||
"failure while sending advert",
|
||||
(),
|
||||
sys.exc_info(),
|
||||
)
|
||||
|
||||
buf.emit(rec)
|
||||
|
||||
assert len(buf.logs) == 1
|
||||
assert "exception" in buf.logs[0]
|
||||
assert "RuntimeError" in buf.logs[0]["exception"]
|
||||
assert "secret123" not in buf.logs[0]["exception"]
|
||||
|
||||
|
||||
def test_doc_endpoint_routes_and_openapi_json_paths(monkeypatch):
|
||||
api = SimpleNamespace(docs=lambda: "docs-html")
|
||||
doc = hs.DocEndpoint(api)
|
||||
|
||||
Reference in New Issue
Block a user