fix(logs): deep-review round 3 — a failure nobody could record is one scrubbed line

- The download and edit failure lines go back after their DB write: a job the
  write could not fail is not a failed job, and the worker's child_exit line
  already says it failed.
- The download child's runner is the last stop before Python's hook: an
  escaping exception is one download.child.failed line with its chain scrubbed,
  then exit 1, instead of a raw stderr traceback carrying the URL.
- A crash the database will not record carries its stack once, and the
  fail-write line has an event code.
- The job's own generic-extractor name is taken out as a literal (formats.hiding),
  so brackets and colons in a direct link's file name no longer leak; the
  pattern stays as the fallback. `Downloading just the video <id>` is covered.
- Tests: scrubbed_stack's cause, suppress, unprintable and text-format paths,
  the image steps' failure kinds, the watchdog error through the real
  _fail_running, exact poster/storyboard messages, ffmpeg skip marker.
This commit is contained in:
2026-09-18 23:44:42 +02:00
parent 0b672ed6e7
commit 89c77ae6b5
9 changed files with 407 additions and 81 deletions
+65
View File
@@ -318,3 +318,68 @@ def test_a_failing_request_is_logged_by_status_and_an_unhandled_error_is_logged_
assert payload["level"] == "ERROR"
assert payload["event"] == "http.request"
assert "RuntimeError" in payload["exc"]
# --- a stack whose messages are data (`logs.scrubbed_stack`) -----------------------------------
def _upper(exc: BaseException) -> str:
return str(exc).upper()
def _raised(make):
try:
make()
except Exception as exc: # noqa: BLE001 — whatever the case raised is the case
return exc
raise AssertionError("the case did not raise")
def test_a_raise_from_is_printed_as_its_direct_cause():
def make():
try:
raise KeyError("inner")
except KeyError as inner:
raise ValueError("outer") from inner
stack = logs.scrubbed_stack(_raised(make), _upper)["scrubbed_stack"]
assert "The above exception was the direct cause" in stack
assert "During handling" not in stack
assert "'INNER'" in stack and "OUTER" in stack
def test_a_raise_from_none_hides_the_link_its_author_hid():
def make():
try:
raise KeyError("hidden")
except KeyError:
raise ValueError("shown") from None
stack = logs.scrubbed_stack(_raised(make), _upper)["scrubbed_stack"]
assert "HIDDEN" not in stack and "SHOWN" in stack
def test_a_chain_that_loops_back_ends():
a, b = ValueError("a"), ValueError("b")
a.__context__, b.__context__ = b, a
assert "A" in logs.scrubbed_stack(a, _upper)["scrubbed_stack"]
def test_a_message_that_cannot_be_rendered_does_not_fail_the_report():
def refuse(exc):
raise RuntimeError("no")
assert (
logs.scrubbed_stack(ValueError("x"), refuse)["scrubbed_stack"]
== "ValueError: <unprintable>"
)
def test_the_text_format_carries_the_scrubbed_stack_too():
title = "a private title" # a variable: a frame prints its source line, which is code
exc = ValueError(title)
record = logging.LogRecord("siftlode.x", logging.WARNING, __file__, 1, "failed", None, None)
for key, value in logs.scrubbed_stack(exc, lambda e: "…").items():
setattr(record, key, value)
line = logs.TextFormatter().format(record)
assert "ValueError: …" in line and "private" not in line
assert "scrubbed_stack" not in line # the stack, not an attribute