Единый формат логов и понятное сообщение о занятом порте

Uvicorn заводил свои обработчики с propagate=False, поэтому его строки шли
мимо нашего формата и рвались посреди слова. log_config=None отдаёт всё
общему обработчику.

Занятый порт сервис теперь замечает до запуска и объясняет, что делать,
вместо строки uvicorn про Errno 10048.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vladimir Bryzgalov
2026-08-16 17:05:38 +05:00
co-authored by Claude Opus 5
parent d4c8f72e6d
commit 6812220d8f
2 changed files with 69 additions and 4 deletions
+31 -4
View File
@@ -2,6 +2,7 @@
import logging
import os
import shutil
import socket
import sys
import tempfile
import threading
@@ -386,12 +387,36 @@ def _setup_console() -> None:
pass
def _port_busy(host: str, port: int) -> bool:
"""Проверяет, слушает ли кто-то уже этот порт.
Проверяем подключением, а не пробной привязкой: привязка оставляет сокет
в TIME_WAIT и может сама помешать запуску через секунду.
"""
target = "127.0.0.1" if host in ("0.0.0.0", "::", "") else host
with socket.socket() as probe:
probe.settimeout(0.5)
try:
return probe.connect_ex((target, port)) == 0
except OSError:
return False
def run() -> None:
import uvicorn
_setup_console()
install_log_buffer()
colored = setup_logging(logging.INFO)
setup_logging(logging.INFO)
if _port_busy(settings.host, settings.port):
# Uvicorn напишет про это «[Errno 10048] ... bind on address», из чего
# неочевидно, что виноват уже работающий экземпляр сервиса.
print(f"\n Порт {settings.port} уже занят - похоже, сервис уже запущен."
"\n Проверьте свёрнутые окна talkscore-asr либо найдите процесс:"
f"\n netstat -ano | findstr :{settings.port}"
"\n taskkill /PID <номер из последней колонки> /F\n")
raise SystemExit(1)
if not settings.token:
print("\n В config.toml пустой токен - сервис никого не пустит."
@@ -414,10 +439,12 @@ def run() -> None:
f"\n Потоков: {settings.effective_threads()}"
f"\n Проверка: curl http://localhost:{settings.port}/health"
"\n Остановить: Ctrl+C\n")
# Красим только если разбор ANSI удалось включить: иначе консоль напечатает
# сами последовательности вместо цвета.
# log_config=None отключает собственную настройку логов uvicorn. Иначе он
# заводит свои обработчики с propagate=False, его строки идут мимо нашего
# формата и рвутся посреди слова, а наши выглядят иначе - в одном окне два
# разных формата.
uvicorn.run(app, host=settings.host, port=settings.port, log_level="info",
use_colors=colored)
log_config=None)
if __name__ == "__main__":