This is an automated email from the ASF dual-hosted git repository.
henry3260 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 51411acb7e5 Fix empty audit log dag_id for DAG.cli() subcommands
(#73379)
51411acb7e5 is described below
commit 51411acb7e579fbda882503d469128bccfb54783
Author: Y-C <[email protected]>
AuthorDate: Tue Oct 20 10:47:45 2026 +0800
Fix empty audit log dag_id for DAG.cli() subcommands (#73379)
The Dag file is its own entry point on this path, so the parser behind
DAG.cli() drops dag_id - there is nothing for the user to type. But
action_cli snapshots the namespace and writes the Log row before the
handler runs, so every audit row for python my_dag.py dags pause and its
five sibling subcommands lands with dag_id NULL: the event cannot be
filtered by Dag, and in multi-team mode team_name is resolved from
dag_id, so it also drops out of the owning team's view.
The back-fills already inside set_is_paused, dag_list_dag_runs and
dag_list_jobs cannot cover this. They run after that insert has been
committed, and they write to the namespace rather than to the metrics
dict that was built from it.
Every one of the six subcommands receives the Dag positionally and
reads args.dag_id only behind a dag or ... short-circuit, so supplying
it changes no control flow - only what the audit row records.
Co-authored-by: Eason09053360
<[email protected]>
---
airflow-core/tests/unit/utils/test_cli_util.py | 16 ++++++++++++++++
task-sdk/src/airflow/sdk/definitions/dag.py | 3 +++
2 files changed, 19 insertions(+)
diff --git a/airflow-core/tests/unit/utils/test_cli_util.py
b/airflow-core/tests/unit/utils/test_cli_util.py
index 98cbad6fbcf..6c9bb2a2eb2 100644
--- a/airflow-core/tests/unit/utils/test_cli_util.py
+++ b/airflow-core/tests/unit/utils/test_cli_util.py
@@ -1012,6 +1012,22 @@ class TestCliUtil:
assert log.team_name == "payments"
+ def test_dag_cli_records_the_dag_id_in_the_audit_log(self):
+ """``DAG.cli()``'s parser drops ``dag_id``, so the Dag has to supply
it before dispatch."""
+ dag = DAG("dag_cli_audit_log")
+
+ with (
+ mock.patch.object(sys, "argv", ["dag_cli_audit_log.py", "tasks",
"list"]),
+ mock.patch("airflow.utils.session.create_session") as
mock_create_session,
+ ):
+ dag.cli()
+
+ audit_session = mock_create_session.return_value.__enter__.return_value
+ model, rows = audit_session.bulk_insert_mappings.call_args.args
+ assert model is Log
+ assert rows[0]["event"] == "cli_task_list"
+ assert rows[0]["dag_id"] == "dag_cli_audit_log"
+
def test_setup_locations_relative_pid_path(self):
relative_pid_path = "fake.pid"
pid_full_path = os.path.join(os.getcwd(), relative_pid_path)
diff --git a/task-sdk/src/airflow/sdk/definitions/dag.py
b/task-sdk/src/airflow/sdk/definitions/dag.py
index 0594ac80658..b0e227c5c85 100644
--- a/task-sdk/src/airflow/sdk/definitions/dag.py
+++ b/task-sdk/src/airflow/sdk/definitions/dag.py
@@ -1211,6 +1211,9 @@ class DAG:
parser = cli_parser.get_parser(dag_parser=True)
args = parser.parse_args()
+ # This parser drops ``dag_id``, but the audit log is written from the
namespace before the
+ # command runs, so a handler back-filling it later is already too late.
+ args.dag_id = self.dag_id
args.func(args, self)
@classmethod