This is an automated email from the ASF dual-hosted git repository.
bbovenzi 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 55162a37396 UI: Fix log line number gaps caused by group markers
(#65039)
55162a37396 is described below
commit 55162a373963947089c8153ad58c127719836625
Author: Daniel Seo <[email protected]>
AuthorDate: Mon Apr 13 18:22:14 2026 -0400
UI: Fix log line number gaps caused by group markers (#65039)
* Fix log line number gaps caused by group markers (#47888)
Group markers (::group:: / ::endgroup::) consumed line number indices
but were removed during group processing, leaving gaps in the displayed
line numbers (e.g. 0, 2, 3 instead of 0, 1, 2).
Pre-scan the log data to assign sequential display line numbers that
skip group markers, and pin wrapped line numbers to the top with
alignItems flex-start.
* fix test
---------
Co-authored-by: hseo36 <[email protected]>
---
.../ui/src/components/renderStructuredLog.tsx | 2 +-
.../ui/src/pages/TaskInstance/Logs/Logs.test.tsx | 32 ++++++++++++++++++++++
.../src/airflow/ui/src/queries/useLogs.tsx | 16 ++++++++++-
3 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
b/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
index f3febe79687..38b430ebb55 100644
--- a/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
+++ b/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
@@ -274,7 +274,7 @@ const renderStructuredLogImpl = ({
}
return (
- <chakra.div display="flex" key={index} lineHeight={1.5}>
+ <chakra.div alignItems="flex-start" display="flex" key={index}
lineHeight={1.5}>
<RouterLink
id={index.toString()}
key={`line_${index}`}
diff --git
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
index cae7f847caa..7ea36a05582 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
@@ -353,4 +353,36 @@ describe("Task log search", () => {
// Auto-expand should make it visible.
await waitFor(() => expect(screen.getByText(/starting attempt 1 of
3/iu)).toBeInTheDocument());
}, 10_000);
+
+ it("skips group markers when assigning line numbers", async () => {
+ render(
+ <AppWrapper
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
/>,
+ );
+
+ await waitForLogs();
+
+ const expectRenderedLineNumber = async (pattern: RegExp,
expectedLineNumber: number) => {
+ const row = (await
screen.findByText(pattern)).closest('[data-testid^="virtualized-item-"]');
+
+ expect(row).not.toBeNull();
+
+ const anchor = row?.querySelector<HTMLAnchorElement>("a[id]");
+
+ expect(anchor).not.toBeNull();
+
+ expect(Number(anchor?.id)).toBe(expectedLineNumber);
+ };
+
+ const summaryPre = screen.getByTestId("summary-Pre task execution logs");
+
+ fireEvent.click(summaryPre);
+
+ const summaryDependency = await screen.findByTestId("summary-Dependency
check details");
+
+ fireEvent.click(summaryDependency);
+
+ await expectRenderedLineNumber(/dep_context=non-requeueable/iu, 0);
+ await expectRenderedLineNumber(/dep_context=requeueable/iu, 1);
+ await expectRenderedLineNumber(/starting attempt 1 of 3/iu, 2);
+ }, 10_000);
});
diff --git a/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
b/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
index 5c4862499d0..d44fca9cd2b 100644
--- a/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
+++ b/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
@@ -75,6 +75,20 @@ const parseLogs = ({
const logLink = taskInstance ?
`${getTaskInstanceLink(taskInstance)}?try_number=${tryNumber}` : "";
try {
+ let lineNumber = 0;
+ const lineNumbers = data.map((datum) => {
+ const text = typeof datum === "string" ? datum : datum.event;
+
+ if (text.includes("::group::") || text.includes("::endgroup::")) {
+ return undefined;
+ }
+ const current = lineNumber;
+
+ lineNumber += 1;
+
+ return current;
+ });
+
parsedLines = data
.map((datum, index) => {
if (typeof datum !== "string" && "logger" in datum) {
@@ -86,7 +100,7 @@ const parseLogs = ({
}
return renderStructuredLog({
- index,
+ index: lineNumbers[index] ?? index,
logLevelFilters,
logLink,
logMessage: datum,