This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new bd4d5de674b [v3-2-test] Fix backport test for log line number gap fix
(#65039) (#65187)
bd4d5de674b is described below
commit bd4d5de674b7731395c32fecc373e996b23a0336
Author: Daniel Seo <[email protected]>
AuthorDate: Tue Apr 14 08:49:10 2026 -0400
[v3-2-test] Fix backport test for log line number gap fix (#65039) (#65187)
* [v3-2-test] 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
---------
(cherry picked from commit 55162a373963947089c8153ad58c127719836625)
Co-authored-by: Daniel Seo <[email protected]>
Co-authored-by: hseo36 <[email protected]>
* Fix backport test to work with v3-2-test log architecture
The cherry-picked test from main used the new flat ParsedLogEntry
group rendering (#63467) which doesn't exist on v3-2-test. Rewrite
to query line number anchors directly from the DOM.
---------
Co-authored-by: hseo36 <[email protected]>
---
.../ui/src/components/renderStructuredLog.tsx | 2 +-
.../ui/src/pages/TaskInstance/Logs/Logs.test.tsx | 21 +++++++++++++++++++++
airflow-core/src/airflow/ui/src/queries/useLogs.tsx | 16 +++++++++++++++-
3 files changed, 37 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 253bdd0bc3d..ae182f812e3 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
@@ -133,4 +133,25 @@ describe("Task log grouping", () => {
await waitFor(() => expect(screen.queryByText(/Marking task as
SUCCESS/iu)).not.toBeVisible());
}, 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 logContainer = screen.getByTestId("virtual-scroll-container");
+ const lineNumbers =
[...logContainer.querySelectorAll<HTMLAnchorElement>("a[id]")]
+ .map((el) => parseInt(el.id, 10))
+ .filter((num) => !isNaN(num))
+ .sort((numA, numB) => numA - numB);
+
+ expect(lineNumbers.length).toBeGreaterThan(0);
+ expect(lineNumbers[0]).toBe(0);
+ expect(new Set(lineNumbers).size).toBe(lineNumbers.length);
+ lineNumbers.forEach((num, idx) => {
+ expect(num).toBe(idx);
+ });
+ }, 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 1a828967414..50090d5fe53 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,