Quiet routine per-tick service logs, surface real failures at WARNING

BaseService logged "Next run in Ns" at INFO on every single execution
of every service, including the 1-2s-interval pub/sub bridges
(presence/notification/live-view relays), drowning the console in
routine noise while actual failures (run_once errors, loop errors,
on_disable errors) were logged at the same INFO level and got lost in
it. log() now takes an optional level (default INFO, unchanged for
existing callers); the routine tick line drops to DEBUG and the three
failure paths move to WARNING. Added DEVPLACE_LOG_LEVEL (default INFO)
so DEBUG-level ticks stay available on demand without a code change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vnp7vzE4hvsytMo5YjszJm
This commit is contained in:
2026-09-08 20:47:16 +02:00
co-authored by Claude Sonnet 5
parent 36e5378f19
commit eec1b3e3de
4 changed files with 11 additions and 8 deletions
+2
View File
@@ -12,6 +12,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent
STATIC_DIR = BASE_DIR / "devplacepy" / "static"
TEMPLATES_DIR = BASE_DIR / "devplacepy" / "templates"
LOG_LEVEL = environ.get("DEVPLACE_LOG_LEVEL", "INFO").upper()
DATA_DIR = Path(environ.get("DEVPLACE_DATA_DIR", str(BASE_DIR / "data")))
UPLOADS_DIR = DATA_DIR / "uploads"
ATTACHMENTS_DIR = UPLOADS_DIR / "attachments"
+2 -1
View File
@@ -20,6 +20,7 @@ from devplacepy.config import (
PORT,
SERVICE_LOCK_FILE,
INIT_LOCK_FILE,
LOG_LEVEL,
ensure_data_dirs,
)
from devplacepy.database import (
@@ -130,7 +131,7 @@ from devplacepy.services.telegram import TelegramService
from devplacepy.services.telegram.outbox_service import TelegramOutboxService
logging.basicConfig(
level=logging.INFO,
level=getattr(logging, LOG_LEVEL, logging.INFO),
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)
+6 -6
View File
@@ -213,10 +213,10 @@ class BaseService(ABC):
def get_config(self) -> dict:
return {field.key: field.read() for field in self.all_fields()}
def log(self, message: str) -> None:
def log(self, message: str, level: int = logging.INFO) -> None:
stamp = datetime.now(timezone.utc).strftime("%H:%M:%S")
self.log_buffer.append(f"[{stamp}] {message}")
logger.info(f"[{self.name}] {message}")
logger.log(level, f"[{self.name}] {message}")
@abstractmethod
async def run_once(self) -> None:
@@ -265,7 +265,7 @@ class BaseService(ABC):
except asyncio.CancelledError:
break
except Exception as e:
self.log(f"Loop error: {e}")
self.log(f"Loop error: {e}", level=logging.WARNING)
try:
await asyncio.sleep(self.TICK_SECONDS)
except asyncio.CancelledError:
@@ -303,7 +303,7 @@ class BaseService(ABC):
try:
await self.on_disable()
except Exception as e:
self.log(f"on_disable error: {e}")
self.log(f"on_disable error: {e}", level=logging.WARNING)
async def _execute_run(self) -> None:
self.interval_seconds = self.current_interval()
@@ -315,13 +315,13 @@ class BaseService(ABC):
except asyncio.CancelledError:
raise
except Exception as e:
self.log(f"Error in run_once: {e}")
self.log(f"Error in run_once: {e}", level=logging.WARNING)
self.interval_seconds = self.current_interval()
self._next_due = datetime.now(timezone.utc) + timedelta(
seconds=self.interval_seconds
)
self._next_run = self._next_due.isoformat()
self.log(f"Next run in {self.interval_seconds}s")
self.log(f"Next run in {self.interval_seconds}s", level=logging.DEBUG)
self._persist_state(force=True)
def _handle_commands(self) -> None:
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "devplacepy"
version = "1.0.8"
version = "1.0.9"
description = "DevPlace - The Developer Social Network"
requires-python = ">=3.12"
dependencies = [