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,

Reply via email to