Files
FishServer/fish_api/app/logging_config.py
zaiun xu 5e1b2117c1 feat(fish_api): SQLite 快照投递、日志与 watch 空闲告警
- 新增 SQLite:measure/health 快照、delivery_cursor 单消费者 pop;clear/start_fresh 可清空库
- biomass GET 仅返回约定 data 字段,X-Fish-Biomass-New 表示是否有新快照;poller 读响应头
- loguru 桥接 uvicorn,子进程 stdout 流式输出;format_json_pretty 与算法摘要日志
- measure/action watch 无新任务时限流 WARNING;watch_idle 共用逻辑
- 依赖 loguru;新增 db、logging_config、subprocess_run、watch_idle、启动脚本

FishMeasure: 更新 fish_video_weight_evaluation 与 predict_weigth_from_svo2;移除未用 refbox/segmentation 脚本
Made-with: Cursor
2026-04-09 11:54:30 +08:00

83 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""将 loguru 与标准 logging 桥接,使 uvicorn / FastAPI / Starlette 日志走同一套格式。"""
from __future__ import annotations
import json
import logging
import os
import sys
from typing import Any
from loguru import logger
_loguru_sink_configured = False
def format_json_pretty(
data: Any,
*,
indent: int = 2,
max_chars: int | None = 24_000,
) -> str:
"""将对象格式化为带缩进、保留中文的 JSON 字符串,供 loguru 多行输出。
``max_chars`` 用于避免单条日志过大;超长时截断并标注。
"""
try:
s = json.dumps(data, ensure_ascii=False, indent=indent, default=str)
except (TypeError, ValueError):
return repr(data)
if max_chars is not None and len(s) > max_chars:
return s[:max_chars] + "\n... (truncated, max_chars=" + str(max_chars) + ")"
return s
class InterceptHandler(logging.Handler):
"""将标准 logging 记录转发到 loguru。"""
def emit(self, record: logging.LogRecord) -> None:
try:
level: str | int = logger.level(record.levelname).name
except ValueError:
level = record.levelno
logger.opt(depth=6, exception=record.exc_info).log(level, record.getMessage())
def setup_logging() -> None:
"""配置 loguru sink并把 uvicorn / fastapi 等 logger 接到 loguru。
可在模块 import 时与 lifespan 启动时各调用一次;后者用于覆盖 uvicorn 启动后写入的 handler。
"""
global _loguru_sink_configured
level = os.environ.get("LOG_LEVEL", "INFO").upper()
if not _loguru_sink_configured:
logger.remove()
logger.add(
sys.stderr,
level=level,
colorize=sys.stderr.isatty(),
format=(
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<level>{message}</level>"
),
)
_loguru_sink_configured = True
intercept = InterceptHandler()
logging.root.handlers = [intercept]
logging.root.setLevel(logging.DEBUG)
for name in (
"uvicorn",
"uvicorn.error",
"uvicorn.access",
"uvicorn.asgi",
"fastapi",
"starlette",
"starlette.requests",
):
lg = logging.getLogger(name)
lg.handlers.clear()
lg.propagate = True