seanghaeli opened a new pull request, #76560:
URL: https://github.com/apache/airflow/pull/76560
Building on #76527, which gives each asset watcher its own log file. This
adds `GET /api/v2/triggers/{trigger_id}/logs` to read any trigger's logs,
whatever its kind: a deferred task's trigger lines from the task's log, a
callback's from the callback's log, and a watcher's from its own files. Stacked
on #76527, so only the last commit is new until that merges.
- Returns a trigger's latest lines in the task log shape. It reads them live
from the triggerer, from remote logging and from the local disk, merged in time
order. Nothing is stored twice.
- A watcher's files are read newest first. Reading stops at `tail_lines`, 64
MiB or the 50 newest triggerer jobs, and an unreachable remote store costs one
timeout, not one per job.
- Access follows the owner's log permission: task logs on the Dag for a
deferred task or a callback. A watcher needs read access to one of its assets
and task logs on a Dag scheduled on it, directly or through an asset name or
URI reference. A trigger you can see but whose logs you can't read returns 403.
- With CloudWatch, reading a watcher's history back from remote also needs a
small amazon provider change that reads log streams by prefix (separate PR).
How to review: one commit. Read `get_trigger_logs` in
`routes/public/triggers.py`, then `utils/log/trigger_log_reader.py`. About 630
lines of source; the rest is generated code, docs and tests.
Breeze on Postgres with CloudWatch remote logging and the amazon provider
change merged in locally: a healthy watcher, a watcher with expired
credentials, a deferred task and a deadline callback, then FAB users with one
role each:
```text
# after this PR
=== GET /api/v2/triggers (admin)
total_entries=4
id=1 kind=asset_watcher status=failing host=530e4d5dd729 dag_id=None
fails=48 last_event=None | watchers
expired_credentials_asset/expired_credentials_watcher
last_error='ConnectionError: the security token included in the
request is expired'
id=2 kind=asset_watcher status=running host=530e4d5dd729 dag_id=None
fails=0 last_event=None | watchers healthy_asset/healthy_watcher
id=4 kind=callback status=running host=530e4d5dd729
dag_id=example_deadline_callback fails=0 last_event=2026-11-04T22:35:11.843344Z
| callback 01a1a8b3 dag=example_deadline_callback
run=manual__2026-11-04T22:34:57.853996+00:00 cb_state=running
id=3 kind=task status=running host=530e4d5dd729
dag_id=example_deferred_task fails=0 last_event=None | task
example_deferred_task.wait_for_trigger try=1 ti_state=deferred
=== GET /api/v2/triggers/{id}/logs?tail_lines=3 (admin): each trigger's own
lines
--- trigger 1 (asset_watcher, failing): watcher expired_credentials_watcher
HTTP 200
3 lines;
sources=['http://530e4d5dd729:8794/log/triggers/1/3/trigger.log',
'triggers/1/3/trigger.log']
2026-11-04T22:36:03.249751Z creds cleanup called
2026-11-04T22:36:04.250440Z Trigger ID 1 exited with error the
security token included in the request is expired
2026-11-04T22:36:04.250791Z Trigger exited without sending an event.
Dependent tasks will be failed.
lines of another trigger: 0
--- trigger 2 (asset_watcher, running): watcher healthy_watcher
HTTP 200
3 lines;
sources=['http://530e4d5dd729:8794/log/triggers/2/3/trigger.log',
'triggers/2/3/trigger.log']
2026-11-04T22:36:00.461555Z healthy poll 48
2026-11-04T22:36:02.462200Z healthy poll 49
2026-11-04T22:36:04.462986Z healthy poll 50
lines of another trigger: 0
--- trigger 3 (task, running): task example_deferred_task.wait_for_trigger
HTTP 200
3 lines;
sources=['http://530e4d5dd729:8794/log/dag_id=example_deferred_task/run_id=manual__2026-11-04T22:34:41.439301+00:00/task_id=wait_for_trigger/attempt=1.log.trigger.3.log',
'dag_id=example_deferred_task/run_id=manual__2026-11-04T22:34:41.439301+00:00/task_id=wait_for_trigger/attempt=1.log.trigger.3.log']
2026-11-04T22:36:00.658323Z wait_for_trigger poll 36
2026-11-04T22:36:02.659030Z wait_for_trigger poll 37
2026-11-04T22:36:04.659714Z wait_for_trigger poll 38
lines of another trigger: 0
--- trigger 4 (callback, running): callback of example_deadline_callback
HTTP 200
3 lines;
sources=['http://530e4d5dd729:8794/log/triggerer_callbacks/example_deadline_callback/manual__2026-11-04T22:34:57.853996+00:00/01a1a8b3-8509-7d23-af9c-c921ac1bc315/callback.log',
'triggerer_callbacks/example_deadline_callback/manual__2026-11-04T22:34:57.853996+00:00/01a1a8b3-8509-7d23-af9c-c921ac1bc315/callback.log']
2026-11-04T22:35:58.832372Z tick 17
2026-11-04T22:36:01.832909Z tick 18
2026-11-04T22:36:04.833463Z tick 19
lines of another trigger: 0
=== access rule (FAB): HTTP code of GET /api/v2/triggers/{id}/logs per user
admin 1:200 2:200 3:200 4:200
reads_deferred_task_dag 1:404 2:404 3:200 4:404
reads_deadline_dag 1:404 2:404 3:404 4:200
reads_assets_no_task_logs 1:403 2:403 3:404 4:404
reads_assets_and_task_logs 1:403 2:200 3:404 4:404
no_access 1:403 2:403 3:403 4:403
no token: 401
role r_task: ['can_read on DAG Runs', 'can_read on
DAG:example_deferred_task', 'can_read on Task Instances', 'can_read on Task
Logs']
role r_deadline: ['can_read on DAG Runs', 'can_read on
DAG:example_deadline_callback', 'can_read on Task Logs']
role r_asset_nologs: ['can_read on Assets', 'can_read on
DAG:to_healthy_consumer']
role r_asset_logs: ['can_read on Assets', 'can_read on
DAG:to_healthy_consumer', 'can_read on Task Logs']
role r_nothing: []
```
---
##### Was generative AI tooling used to co-author this PR?
<!--
If generative AI tooling has been used in the process of authoring this PR,
please
change below checkbox to `[X]` followed by the name of the tool, uncomment
the "Generated-by".
-->
- [X] Yes (please specify the tool below)
Generated-by: Claude Code (Opus 5.5) following [the
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]