[PATCH 16/26] lucid: honor show_task_path_on_failure option
Daniel Gomez <[email protected]> Tue, 19 May 2026 15:28:13 +0200
| Newsgroups | dev.linux.lists.kdevops |
|---|---|
| Message-ID | <[email protected]> |
From: Daniel Gomez <[email protected]> When a task fails, knowing the source file and line of the failing task makes diagnosis dramatically faster, which is why the default Ansible stdout callback exposes show_task_path_on_failure. Lucid silently ignored the option, forcing users to re-run with higher verbosity or to grep through roles by hand when something broke. This change caches the option in set_options and, for failed or unreachable results, appends a task path line to the displayed output when the option is set or when verbosity is at least two. The path is included with the existing stdout, stderr, msg, and exception blocks so it routes with the rest of the failure output, including to stderr when display_failed_stderr is on. When there is no body to display the path is still surfaced on its own so the user is not left guessing. The log file also gains the task path for every failure regardless of the option, because the log is intentionally maximum fidelity and should remain self-sufficient for post-hoc debugging. Generated-by: Claude AI Signed-off-by: Daniel Gomez <[email protected]> --- callback_plugins/lucid.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/callback_plugins/lucid.py b/callback_plugins/lucid.py index 443f314a..68663f13 100644 --- a/callback_plugins/lucid.py +++ b/callback_plugins/lucid.py @@ -142,6 +142,7 @@ class CallbackModule(CallbackBase): self.display_failed_stderr = self.get_option("display_failed_stderr") self.check_mode_markers = self.get_option("check_mode_markers") self.show_custom_stats = self.get_option("show_custom_stats") + self.show_task_path_on_failure = self.get_option("show_task_path_on_failure") # Determine display mode based on configuration is_interactive = self._detect_interactive() @@ -764,11 +765,30 @@ class CallbackModule(CallbackBase): stderr=use_stderr, ) + # Resolve the failing task's source path when we are meant to show + # it: either the user opted in via show_task_path_on_failure, or they + # asked for -vv+ verbosity which always exposes the path in the + # reference default callback. + task_path = "" + if status in ("failed", "unreachable") and ( + self.show_task_path_on_failure or self._display.verbosity >= 2 + ): + try: + task_path = result._task.get_path() or "" + except AttributeError: + task_path = "" + # Show output if conditions met if show_output: - self._display_output(result, stderr=use_stderr) + self._display_output(result, stderr=use_stderr, task_path=task_path) + elif task_path: + # Still surface the failing task's source location even when the + # body is suppressed (e.g. no stdout/stderr/msg to display). + self._display_message( + f"task path: {task_path}", color=C.COLOR_VERBOSE, stderr=use_stderr + ) - def _display_output(self, result, stderr: bool = False): + def _display_output(self, result, stderr: bool = False, task_path: str = ""): """Display stdout/stderr/msg from task result""" output = [] res = result._result @@ -793,6 +813,11 @@ class CallbackModule(CallbackBase): if "exception" in res and res["exception"]: output.append(f"\nEXCEPTION:\n{res['exception']}") + # Task source path for failures, mirroring the default callback's + # show_task_path_on_failure behavior. + if task_path: + output.append(f"\ntask path: {task_path}") + if output: with self.output_lock: self._display.display("".join(output), stderr=stderr) @@ -838,6 +863,17 @@ class CallbackModule(CallbackBase): if status == "failed" and "exception" in res: self._write_to_log(f"\nEXCEPTION:\n{res['exception']}\n") + # Always record the failing task's source path in the log, regardless + # of the show_task_path_on_failure option or verbosity, so the log + # stays self-sufficient for post-hoc debugging of failures. + if status in ("failed", "unreachable"): + try: + task_path = result._task.get_path() + except AttributeError: + task_path = None + if task_path: + self._write_to_log(f"task path: {task_path}") + def _display_recap(self, stats): """Display final statistics""" self._display_message("\nPLAY RECAP", C.COLOR_HIGHLIGHT) -- 2.53.0