Make service health-check self-diagnosing
When a service group's cog stays disabled the log now says WHY: _service_health returns the actual connection error (ConnectionError = refused/down, ConnectTimeout = firewall/slow, gaierror = DNS/wrong host) instead of a bare 'unreachable', and on_ready logs the resolved FILE/LIBRARIAN/RADIO addresses so an env-var that never reached the process (address falls back to the 192.168.1.15:5000 default) is obvious at a glance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #9.
This commit is contained in:
@@ -120,13 +120,15 @@ SERVICE_EXTENSION_GROUPS = {
|
|||||||
SERVICE_RECHECK_SECONDS = 300
|
SERVICE_RECHECK_SECONDS = 300
|
||||||
|
|
||||||
|
|
||||||
def _service_alive(url: str) -> bool:
|
def _service_health(url: str):
|
||||||
"""True when the service answers HTTP at all (any status code counts)."""
|
"""Return None when the service answers HTTP at all (any status counts),
|
||||||
|
otherwise the connection error explaining WHY it's unreachable (refused vs
|
||||||
|
timeout vs DNS - the difference points straight at the cause)."""
|
||||||
try:
|
try:
|
||||||
requests.get(url, timeout=3)
|
requests.get(url, timeout=3)
|
||||||
return True
|
return None
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException as exc:
|
||||||
return False
|
return exc
|
||||||
|
|
||||||
|
|
||||||
async def _load_extension_safe(name: str) -> bool:
|
async def _load_extension_safe(name: str) -> bool:
|
||||||
@@ -152,12 +154,14 @@ async def _load_service_groups() -> bool:
|
|||||||
missing = [e for e in group["extensions"] if e not in client.extensions]
|
missing = [e for e in group["extensions"] if e not in client.extensions]
|
||||||
if not missing:
|
if not missing:
|
||||||
continue
|
continue
|
||||||
alive = await asyncio.to_thread(_service_alive, group["health_url"])
|
err = await asyncio.to_thread(_service_health, group["health_url"])
|
||||||
if not alive:
|
if err is not None:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Service '%s' unreachable (%s) - cogs stay disabled: %s",
|
"Service '%s' unreachable (%s) [%s: %s] - cogs stay disabled: %s",
|
||||||
service,
|
service,
|
||||||
group["health_url"],
|
group["health_url"],
|
||||||
|
type(err).__name__,
|
||||||
|
err,
|
||||||
", ".join(missing),
|
", ".join(missing),
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@@ -204,6 +208,16 @@ async def on_ready():
|
|||||||
for extension in CORE_EXTENSIONS:
|
for extension in CORE_EXTENSIONS:
|
||||||
await _load_extension_safe(extension)
|
await _load_extension_safe(extension)
|
||||||
|
|
||||||
|
# Log the ACTUALLY-resolved service addresses. When one shows the built-in
|
||||||
|
# default (192.168.1.15:5000) it means the matching CONJURER_* env var never
|
||||||
|
# reached the process - the single most common cause of "service unreachable"
|
||||||
|
# confusion. Printing them makes env-vs-default obvious at a glance.
|
||||||
|
logger.info(
|
||||||
|
"Resolved service addresses -> musician(file): %s | librarian: %s | radio: %s",
|
||||||
|
FILE_SERVICE_ADDRESS,
|
||||||
|
LIBRARIAN_SERVICE_ADDRESS,
|
||||||
|
RADIO_SERVICE_ADDRESS,
|
||||||
|
)
|
||||||
await _load_service_groups()
|
await _load_service_groups()
|
||||||
logger.info("Sensors: online")
|
logger.info("Sensors: online")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user