Spaces:
Running on Zero
Running on Zero
Add detailed export/traceback logging to diagnose post-pipeline UI errors (#20)
Browse filesThe Space logs cut off right after the pipeline stages finish, with no
trace of what happens next in generate_video() (export_to_video and the
run_inference call boundary), even though the UI shows a generic Error.
Log frame metadata and full tracebacks at those points so the next
failure is diagnosable from the Space logs alone.
- app.py +18 -1
- logging_utils.py +30 -0
app.py
CHANGED
|
@@ -13,6 +13,10 @@ from PIL import Image
|
|
| 13 |
|
| 14 |
from logging_utils import (
|
| 15 |
LogUploader,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
print_infer_done,
|
| 17 |
print_infer_error,
|
| 18 |
print_infer_start,
|
|
@@ -262,8 +266,10 @@ def generate_video(
|
|
| 262 |
progress,
|
| 263 |
)
|
| 264 |
except Exception as e:
|
|
|
|
| 265 |
duration = _time.perf_counter() - t0
|
| 266 |
print_infer_error(e, duration)
|
|
|
|
| 267 |
if _log_uploader.enabled:
|
| 268 |
_spawn_log(
|
| 269 |
resized_image, None, None, prompt, negative_prompt, current_seed, steps,
|
|
@@ -274,9 +280,20 @@ def generate_video(
|
|
| 274 |
|
| 275 |
duration = _time.perf_counter() - t0
|
| 276 |
|
|
|
|
|
|
|
|
|
|
| 277 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 278 |
video_path = tmpfile.name
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
if _log_uploader.enabled:
|
| 282 |
# Interpolation/upscale failures are already caught-and-warned (not raised) inside
|
|
|
|
| 13 |
|
| 14 |
from logging_utils import (
|
| 15 |
LogUploader,
|
| 16 |
+
print_export_done,
|
| 17 |
+
print_export_error,
|
| 18 |
+
print_export_start,
|
| 19 |
+
print_frames_info,
|
| 20 |
print_infer_done,
|
| 21 |
print_infer_error,
|
| 22 |
print_infer_start,
|
|
|
|
| 266 |
progress,
|
| 267 |
)
|
| 268 |
except Exception as e:
|
| 269 |
+
import traceback as _tb
|
| 270 |
duration = _time.perf_counter() - t0
|
| 271 |
print_infer_error(e, duration)
|
| 272 |
+
print(f"[infer] traceback:\n{_tb.format_exc()}", flush=True)
|
| 273 |
if _log_uploader.enabled:
|
| 274 |
_spawn_log(
|
| 275 |
resized_image, None, None, prompt, negative_prompt, current_seed, steps,
|
|
|
|
| 280 |
|
| 281 |
duration = _time.perf_counter() - t0
|
| 282 |
|
| 283 |
+
print_frames_info("final_frames", final_frames, final_fps)
|
| 284 |
+
print_frames_info("pre_upscale_frames", pre_upscale_frames, pre_upscale_fps)
|
| 285 |
+
|
| 286 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 287 |
video_path = tmpfile.name
|
| 288 |
+
print_export_start(video_path)
|
| 289 |
+
t_export = _time.perf_counter()
|
| 290 |
+
try:
|
| 291 |
+
export_to_video(final_frames, video_path, fps=final_fps, quality=6)
|
| 292 |
+
except Exception as e:
|
| 293 |
+
import traceback as _tb
|
| 294 |
+
print_export_error(e, _tb.format_exc())
|
| 295 |
+
raise
|
| 296 |
+
print_export_done(_time.perf_counter() - t_export)
|
| 297 |
|
| 298 |
if _log_uploader.enabled:
|
| 299 |
# Interpolation/upscale failures are already caught-and-warned (not raised) inside
|
logging_utils.py
CHANGED
|
@@ -59,6 +59,36 @@ def print_infer_error(error: Exception, elapsed: float) -> None:
|
|
| 59 |
print(f"[infer] FAILED: {type(error).__name__}: {error} | t={elapsed:.1f}s", flush=True)
|
| 60 |
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
def print_log_skipped(has_token: bool, has_repo: bool) -> None:
|
| 63 |
print(f"[log] skipped — token={'set' if has_token else 'missing'}, repo={'set' if has_repo else 'missing'}")
|
| 64 |
|
|
|
|
| 59 |
print(f"[infer] FAILED: {type(error).__name__}: {error} | t={elapsed:.1f}s", flush=True)
|
| 60 |
|
| 61 |
|
| 62 |
+
def print_frames_info(label: str, frames: Any, fps: Any) -> None:
|
| 63 |
+
if frames is None:
|
| 64 |
+
print(f"[export] {label}: frames=None, fps={fps}", flush=True)
|
| 65 |
+
return
|
| 66 |
+
n = len(frames)
|
| 67 |
+
first = frames[0] if n else None
|
| 68 |
+
kind = type(first).__name__ if first is not None else "n/a"
|
| 69 |
+
shape = getattr(first, "shape", None)
|
| 70 |
+
dtype = getattr(first, "dtype", None)
|
| 71 |
+
size = getattr(first, "size", None) # PIL Image
|
| 72 |
+
mode = getattr(first, "mode", None) # PIL Image
|
| 73 |
+
print(
|
| 74 |
+
f"[export] {label}: n={n}, fps={fps}, frame_type={kind}, shape={shape}, dtype={dtype}, "
|
| 75 |
+
f"pil_size={size}, pil_mode={mode}",
|
| 76 |
+
flush=True,
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def print_export_start(video_path: str) -> None:
|
| 81 |
+
print(f"[export] start -> {video_path}", flush=True)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def print_export_done(elapsed: float) -> None:
|
| 85 |
+
print(f"[export] done — {elapsed:.1f}s", flush=True)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def print_export_error(error: Exception, tb: str) -> None:
|
| 89 |
+
print(f"[export] FAILED: {type(error).__name__}: {error}\n{tb}", flush=True)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
def print_log_skipped(has_token: bool, has_repo: bool) -> None:
|
| 93 |
print(f"[log] skipped — token={'set' if has_token else 'missing'}, repo={'set' if has_repo else 'missing'}")
|
| 94 |
|