[diffusion] UX: suppress excessive logging from httpx and httpcore (#20452)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user