from __future__ import annotations import json import logging import os import sys from datetime import datetime, timezone from typing import Any LOG_FIELDS = ( "event", "request_id", "segment_id", "feedback_session_id", "status", "latency_ms", "audio_size_bytes", "audio_duration_ms", "audio_format", "error_class", ) class JsonFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: payload: dict[str, Any] = { "timestamp": datetime.now(timezone.utc).isoformat(timespec="milliseconds"), "level": record.levelname, "service": "funasr", "environment": os.getenv("APP_ENV", "development"), "app_version": os.getenv("APP_VERSION", "unknown"), "git_sha": os.getenv("GIT_SHA", "unknown"), "message": record.getMessage(), } for field in LOG_FIELDS: value = getattr(record, field, None) if value is not None: payload[field] = value return json.dumps(payload, ensure_ascii=False, default=str) class JsonLogStream: encoding = "utf-8" def __init__(self, level: int) -> None: self.level = level self.buffer = "" def write(self, message: str) -> int: self.buffer += message while "\n" in self.buffer: line, self.buffer = self.buffer.split("\n", 1) self._emit(line) return len(message) def flush(self) -> None: if self.buffer: self._emit(self.buffer) self.buffer = "" def isatty(self) -> bool: return False def _emit(self, message: str) -> None: message = message.strip() if message: logging.getLogger("funasr-service").log(self.level, message) def configure_logging() -> None: handler = logging.StreamHandler(sys.__stdout__) handler.setFormatter(JsonFormatter()) root = logging.getLogger() root.handlers.clear() root.addHandler(handler) root.setLevel(logging.INFO) for name, logger in logging.Logger.manager.loggerDict.items(): if isinstance(logger, logging.Logger): logger.handlers.clear() logger.propagate = name != "uvicorn.access" def redirect_standard_streams() -> None: sys.stdout = JsonLogStream(logging.INFO) # type: ignore[assignment] sys.stderr = JsonLogStream(logging.ERROR) # type: ignore[assignment] def log_event(level: int, event: str, message: str, **fields: Any) -> None: logging.getLogger("funasr-service").log(level, message, extra={"event": event, **fields})