[diffusion] UX: suppress excessive logging from httpx and httpcore (#20452)

This commit is contained in:
Mick
2026-03-13 14:43:09 +08:00
committed by GitHub
parent 9c8777c80f
commit b638b25b22
3 changed files with 123 additions and 127 deletions

View File

@@ -11,6 +11,7 @@ in a functional manner, reducing the need for explicit parameter passing.
from __future__ import annotations
import logging
import os
import pprint
from copy import deepcopy
@@ -21,11 +22,11 @@ import PIL.Image
import torch
from sglang.multimodal_gen.configs.sample.sampling_params import SamplingParams
from sglang.multimodal_gen.runtime.server_args import (
ServerArgs,
from sglang.multimodal_gen.runtime.server_args import ServerArgs
from sglang.multimodal_gen.runtime.utils.logging_utils import (
_sanitize_for_logging,
init_logger,
)
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
from sglang.multimodal_gen.runtime.utils.perf_logger import RequestMetrics
from sglang.multimodal_gen.utils import align_to
@@ -288,20 +289,22 @@ class Req:
else:
target_width = -1
# sanitize prompts for info-level logging
sanitized_prompt = _sanitize_for_logging(self.prompt, key_hint="prompt")
sanitized_neg_prompt = _sanitize_for_logging(
self.negative_prompt, key_hint="negative_prompt"
)
if logger.isEnabledFor(logging.DEBUG):
display_prompt = self.prompt
display_neg_prompt = self.negative_prompt
else:
display_prompt = _sanitize_for_logging(self.prompt, key_hint="prompt")
display_neg_prompt = _sanitize_for_logging(
self.negative_prompt, key_hint="negative_prompt"
)
# Log sampling parameters
debug_str = f"""Sampling params:
width: {target_width}
height: {target_height}
num_frames: {self.num_frames}
fps: {self.fps}
prompt: {sanitized_prompt}
neg_prompt: {sanitized_neg_prompt}
prompt: {display_prompt}
neg_prompt: {display_neg_prompt}
seed: {self.seed}
infer_steps: {self.num_inference_steps}
num_outputs_per_prompt: {self.num_outputs_per_prompt}
@@ -313,7 +316,7 @@ class Req:
save_output: {self.save_output}
output_file_path: {self.output_file_path()}
""" # type: ignore[attr-defined]
logger.debug(debug_str)
logger.info(debug_str)
@dataclass

View File

@@ -6,7 +6,6 @@
import argparse
import dataclasses
import inspect
import json
import math
import os
@@ -37,6 +36,7 @@ from sglang.multimodal_gen.runtime.utils.common import (
is_valid_ipv6_address,
)
from sglang.multimodal_gen.runtime.utils.logging_utils import (
_sanitize_for_logging,
configure_logger,
init_logger,
)
@@ -49,119 +49,6 @@ from sglang.multimodal_gen.utils import (
logger = init_logger(__name__)
def _is_torch_tensor(obj: Any) -> tuple[bool, Any]:
"""Return (is_tensor, torch_module_or_None) without importing torch at module import time."""
try:
import torch # type: ignore
return isinstance(obj, torch.Tensor), torch
except Exception:
return False, None
def _sanitize_for_logging(obj: Any, key_hint: str | None = None) -> Any:
"""Recursively convert objects to JSON-serializable forms for concise logging.
Rules:
- Drop any field/dict key named 'param_names_mapping'.
- Render Enums using their value.
- Render torch.Tensor as a compact summary; if key name is 'scaling_factor', include stats.
- Dataclasses are expanded to dicts and sanitized recursively.
- Callables/functions are rendered as their qualified name.
- Redact sensitive fields like 'prompt' and 'negative_prompt' (only show length).
- Fallback to str(...) for unknown types.
"""
# Handle simple types quickly
if obj is None or isinstance(obj, (str, int, float, bool)):
# redact sensitive prompt fields
if key_hint in ("prompt", "negative_prompt"):
if isinstance(obj, str):
return f"<redacted, len={len(obj)}>"
return obj
# Enum -> value for readability
if isinstance(obj, Enum):
return obj.value
# torch.Tensor handling (lazy import)
is_tensor, torch_mod = _is_torch_tensor(obj)
if is_tensor:
try:
ten = obj.detach().cpu()
if key_hint == "scaling_factor":
# Provide a compact, single-line summary for scaling_factor
stats = {
"shape": list(ten.shape),
"dtype": str(ten.dtype),
}
# Stats might fail for some dtypes; guard individually
try:
stats["min"] = float(ten.min().item())
except Exception:
pass
try:
stats["max"] = float(ten.max().item())
except Exception:
pass
try:
stats["mean"] = float(ten.float().mean().item())
except Exception:
pass
return {"tensor": "scaling_factor", **stats}
# Generic tensor summary
return {"tensor": True, "shape": list(ten.shape), "dtype": str(ten.dtype)}
except Exception:
return "<tensor>"
# Dataclasses -> dict
if dataclasses.is_dataclass(obj):
result: dict[str, Any] = {}
for f in dataclasses.fields(obj):
if not f.repr:
continue
name = f.name
if "names_mapping" in name: # drop noisy mappings
continue
try:
value = getattr(obj, name)
except Exception:
continue
result[name] = _sanitize_for_logging(value, key_hint=name)
return result
# Dicts -> sanitize keys/values; drop 'param_names_mapping'
if isinstance(obj, dict):
result_dict: dict[str, Any] = {}
for k, v in obj.items():
try:
key_str = str(k)
except Exception:
key_str = "<key>"
if key_str == "param_names_mapping":
continue
result_dict[key_str] = _sanitize_for_logging(v, key_hint=key_str)
return result_dict
# Sequences/Sets -> list
if isinstance(obj, (list, tuple, set)):
return [_sanitize_for_logging(x, key_hint=key_hint) for x in obj]
# Functions / Callables -> qualified name
try:
if inspect.isroutine(obj) or inspect.isclass(obj):
module = getattr(obj, "__module__", "")
qn = getattr(obj, "__qualname__", getattr(obj, "__name__", "<callable>"))
return f"{module}.{qn}" if module else qn
except Exception:
pass
# Fallback: string representation
try:
return str(obj)
except Exception:
return "<unserializable>"
class Backend(str, Enum):
"""
Enumeration for different model backends.

View File

@@ -6,12 +6,15 @@
import argparse
import contextlib
import dataclasses
import datetime
import inspect
import logging
import os
import sys
import time
from contextlib import contextmanager
from enum import Enum
from functools import lru_cache, partial
from logging import Logger
from types import MethodType
@@ -281,6 +284,107 @@ def init_logger(name: str) -> _SGLDiffusionLogger:
logger = init_logger(__name__)
def _is_torch_tensor(obj: Any) -> tuple[bool, Any]:
"""Return (is_tensor, torch_module_or_None) without importing torch at module import time."""
try:
import torch # type: ignore
return isinstance(obj, torch.Tensor), torch
except Exception:
return False, None
def _sanitize_for_logging(obj: Any, key_hint: str | None = None) -> Any:
"""Recursively convert objects to JSON-serializable forms for concise logging.
Rules:
- Drop any field/dict key named 'param_names_mapping'.
- Render Enums using their value.
- Render torch.Tensor as a compact summary; if key name is 'scaling_factor', include stats.
- Dataclasses are expanded to dicts and sanitized recursively.
- Callables/functions are rendered as their qualified name.
- Redact sensitive fields like 'prompt' and 'negative_prompt' (only show length).
- Fallback to str(...) for unknown types.
"""
if obj is None or isinstance(obj, (str, int, float, bool)):
if key_hint in ("prompt", "negative_prompt"):
if isinstance(obj, str):
return f"<redacted, len={len(obj)}>"
return obj
if isinstance(obj, Enum):
return obj.value
is_tensor, torch_mod = _is_torch_tensor(obj)
if is_tensor:
try:
ten = obj.detach().cpu()
if key_hint == "scaling_factor":
stats = {
"shape": list(ten.shape),
"dtype": str(ten.dtype),
}
try:
stats["min"] = float(ten.min().item())
except Exception:
pass
try:
stats["max"] = float(ten.max().item())
except Exception:
pass
try:
stats["mean"] = float(ten.float().mean().item())
except Exception:
pass
return {"tensor": "scaling_factor", **stats}
return {"tensor": True, "shape": list(ten.shape), "dtype": str(ten.dtype)}
except Exception:
return "<tensor>"
if dataclasses.is_dataclass(obj):
result: dict[str, Any] = {}
for f in dataclasses.fields(obj):
if not f.repr:
continue
name = f.name
if "names_mapping" in name:
continue
try:
value = getattr(obj, name)
except Exception:
continue
result[name] = _sanitize_for_logging(value, key_hint=name)
return result
if isinstance(obj, dict):
result_dict: dict[str, Any] = {}
for k, v in obj.items():
try:
key_str = str(k)
except Exception:
key_str = "<key>"
if key_str == "param_names_mapping":
continue
result_dict[key_str] = _sanitize_for_logging(v, key_hint=key_str)
return result_dict
if isinstance(obj, (list, tuple, set)):
return [_sanitize_for_logging(x, key_hint=key_hint) for x in obj]
try:
if inspect.isroutine(obj) or inspect.isclass(obj):
module = getattr(obj, "__module__", "")
qn = getattr(obj, "__qualname__", getattr(obj, "__name__", "<callable>"))
return f"{module}.{qn}" if module else qn
except Exception:
pass
try:
return str(obj)
except Exception:
return "<unserializable>"
def _trace_calls(log_path, root_dir, frame, event, arg=None):
if event in ["call", "return"]:
# Extract the filename, line number, function name, and the code object
@@ -403,6 +507,8 @@ def globally_suppress_loggers():
"python_multipart.multipart",
"filelock",
"urllib3",
"httpx",
"httpcore",
]
for name in target_names:
@@ -457,7 +563,7 @@ def log_generation_timer(
"Processing prompt %d/%d: %s",
request_idx,
total_requests,
prompt[:100],
_sanitize_for_logging(prompt, key_hint="prompt"),
)
timer = GenerationTimer()