diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/event_logs.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/event_logs.py index d7355bc1294b1..8a1d429ad7027 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/event_logs.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/event_logs.py @@ -41,6 +41,9 @@ class EventLogResponse(BaseModel): dag_display_name: str | None = Field( validation_alias=AliasPath("dag_model", "dag_display_name"), default=None ) + task_display_name: str | None = Field( + validation_alias=AliasPath("task_instance", "task_display_name"), default=None + ) class EventLogCollectionResponse(BaseModel): diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml index 3dcb4924d96b1..8a631c1909678 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml +++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml @@ -10841,6 +10841,11 @@ components: - type: string - type: 'null' title: Dag Display Name + task_display_name: + anyOf: + - type: string + - type: 'null' + title: Task Display Name type: object required: - event_log_id diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py index 6e7603121e2ac..1bce6a65a50d7 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py @@ -21,6 +21,7 @@ from fastapi import Depends, HTTPException, status from sqlalchemy import select +from sqlalchemy.orm import joinedload from airflow.api_fastapi.common.db.common import ( SessionDep, @@ -57,7 +58,9 @@ def get_event_log( event_log_id: int, session: SessionDep, ) -> EventLogResponse: - event_log = session.scalar(select(Log).where(Log.id == event_log_id)) + event_log = session.scalar( + select(Log).where(Log.id == event_log_id).options(joinedload(Log.task_instance)) + ) if event_log is None: raise HTTPException(status.HTTP_404_NOT_FOUND, f"The Event Log with id: `{event_log_id}` not found") return event_log diff --git a/airflow-core/src/airflow/models/log.py b/airflow-core/src/airflow/models/log.py index 9347e4a17536c..645c3ee6c7783 100644 --- a/airflow-core/src/airflow/models/log.py +++ b/airflow-core/src/airflow/models/log.py @@ -56,6 +56,14 @@ class Log(Base): primaryjoin="Log.dag_id == DagModel.dag_id", ) + task_instance = relationship( + "TaskInstance", + viewonly=True, + foreign_keys=[task_id], + primaryjoin="Log.task_id == TaskInstance.task_id", + lazy="noload", + ) + __table_args__ = ( Index("idx_log_dttm", dttm), Index("idx_log_event", event), diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index 46df7ec9d2fdc..f9c93f976019f 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -3369,6 +3369,17 @@ export const $EventLogResponse = { } ], title: 'Dag Display Name' + }, + task_display_name: { + anyOf: [ + { + type: 'string' + }, + { + type: 'null' + } + ], + title: 'Task Display Name' } }, type: 'object', diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts index f499e9c75a422..1396090691c41 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts @@ -877,6 +877,7 @@ export type EventLogResponse = { owner: string | null; extra: string | null; dag_display_name?: string | null; + task_display_name?: string | null; }; /** diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py index ebc80625f1941..881ca6dc3108d 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py @@ -32,6 +32,7 @@ DAG_DISPLAY_NAME = "TEST_DAG_ID" DAG_RUN_ID = "TEST_DAG_RUN_ID" TASK_ID = "TEST_TASK_ID" +TASK_DISPLAY_NAME = "TEST_TASK_ID" DAG_EXECUTION_DATE = datetime(2024, 6, 15, 0, 0, tzinfo=timezone.utc) OWNER = "TEST_OWNER" OWNER_DISPLAY_NAME = "Test Owner" @@ -135,6 +136,7 @@ class TestGetEventLog(TestEventLogsEndpoint): "owner": OWNER_AIRFLOW, "run_id": DAG_RUN_ID, "task_id": TASK_ID, + "task_display_name": TASK_DISPLAY_NAME, }, ), ( @@ -148,6 +150,7 @@ class TestGetEventLog(TestEventLogsEndpoint): "owner": OWNER, "run_id": DAG_RUN_ID, "task_id": TASK_ID, + "task_display_name": TASK_DISPLAY_NAME, "try_number": 0, }, ), @@ -168,6 +171,7 @@ def test_get_event_log(self, test_client, setup, event_log_key, expected_status_ "dag_display_name": expected_body.get("dag_display_name"), "dag_id": expected_body.get("dag_id"), "task_id": expected_body.get("task_id"), + "task_display_name": expected_body.get("task_display_name"), "run_id": expected_body.get("run_id"), "map_index": event_log.map_index, "try_number": event_log.try_number, diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py b/airflow-ctl/src/airflowctl/api/datamodels/generated.py index cf57b74ed4e23..9e8f9f7c759f4 100644 --- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py +++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py @@ -462,6 +462,7 @@ class EventLogResponse(BaseModel): owner: Annotated[str | None, Field(title="Owner")] = None extra: Annotated[str | None, Field(title="Extra")] = None dag_display_name: Annotated[str | None, Field(title="Dag Display Name")] = None + task_display_name: Annotated[str | None, Field(title="Task Display Name")] = None class ExternalLogUrlResponse(BaseModel):