diff --git a/geant/gap_ansible/plugins/callback_plugins/filtering_syslog.py b/geant/gap_ansible/plugins/callback_plugins/filtering_syslog.py
new file mode 100644
index 0000000000000000000000000000000000000000..7e8cf052375086a5b8b435f066963c7f02464870
--- /dev/null
+++ b/geant/gap_ansible/plugins/callback_plugins/filtering_syslog.py
@@ -0,0 +1,70 @@
+# callback_plugins/filtering_syslog.py
+import syslog
+import uuid
+from datetime import datetime
+from ansible.plugins.callback import CallbackBase
+from ansible.utils.display import Display
+
+display = Display()
+
+class CallbackModule(CallbackBase):
+ """
+ Logs all task results to syslog with timestamp and run ID.
+ One line per result. Only prints tasks tagged with 'print_action'.
+ Always prints and logs playbook summary.
+ """
+
+ CALLBACK_VERSION = 2.0
+ CALLBACK_TYPE = 'stdout'
+ CALLBACK_NAME = 'filtering_syslog'
+
+ def __init__(self):
+ super().__init__()
+ syslog.openlog(ident="ansible", logoption=syslog.LOG_PID, facility=syslog.LOG_USER)
+ self.run_id = str(uuid.uuid4())[:8] # Shorten for readability
+
+ def _log_to_syslog(self, message: str):
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ syslog.syslog(syslog.LOG_INFO, f"[{timestamp}] [run_id:{self.run_id}] {message}")
+
+ def _should_print(self, task):
+ return hasattr(task, "tags") and "print_action" in task.tags
+
+ def _pretty_print(self, result, status):
+ task_name = result.task_name or result._task.get_name()
+ host = result._host.get_name()
+
+ display.banner(f"TASK [{task_name}]")
+ display.display(f"{host} | {status}", color="green" if status == "ok" else "red")
+ display.display(self._dump_results(result._result))
+
+ def v2_runner_on_ok(self, result):
+ task = result.task_name or result._task.get_name()
+ host = result._host.get_name()
+ self._log_to_syslog(f"{host} | OK | Task: {task}")
+ if self._should_print(result._task):
+ self._pretty_print(result, "ok")
+
+ def v2_runner_on_failed(self, result, ignore_errors=False):
+ task = result.task_name or result._task.get_name()
+ host = result._host.get_name()
+ self._log_to_syslog(f"{host} | FAILED | Task: {task}")
+ if self._should_print(result._task):
+ self._pretty_print(result, "failed")
+
+ def v2_runner_on_skipped(self, result):
+ task = result.task_name or result._task.get_name()
+ host = result._host.get_name()
+ self._log_to_syslog(f"{host} | SKIPPED | Task: {task}")
+ if self._should_print(result._task):
+ self._pretty_print(result, "skipped")
+
+ def v2_playbook_on_stats(self, stats):
+ summary = {}
+ for host in stats.processed.keys():
+ s = stats.summarize(host)
+ summary_line = f"{host} | SUMMARY | ok={s['ok']} changed={s['changed']} unreachable={s['unreachable']} failed={s['failures']} skipped={s['skipped']}"
+ self._log_to_syslog(summary_line)
+ display.display(summary_line)
+
+ display.banner("PLAYBOOK SUMMARY")