diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json b/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
index 8fbd4f2065362..e5a0dc12f5110 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
@@ -84,6 +84,8 @@
"assetEvent_other": "Created Asset Events"
},
"failedLogs": {
+ "hideLogs": "Hide Logs",
+ "showLogs": "Show Logs",
"title": "Recent Failed Task Logs",
"viewFullLogs": "View full logs"
}
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/TaskLogPreview.tsx b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/TaskLogPreview.tsx
index fe4ecd4285810..0c3809ca21d33 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/TaskLogPreview.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/TaskLogPreview.tsx
@@ -16,7 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, Flex, Link } from "@chakra-ui/react";
+import { Box, Flex, Link, Button } from "@chakra-ui/react";
+import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link as RouterLink } from "react-router-dom";
@@ -36,14 +37,18 @@ export const TaskLogPreview = ({
readonly wrap: boolean;
}) => {
const { t: translate } = useTranslation("dag");
+ const [isExpanded, setIsExpanded] = useState(false);
+
const { data, error, isLoading } = useLogs(
{
dagId: taskInstance.dag_id,
+ limit: 100,
logLevelFilters: ["error", "critical"],
taskInstance,
tryNumber: taskInstance.try_number,
},
{
+ enabled: isExpanded,
refetchInterval: false,
retry: false,
},
@@ -51,13 +56,18 @@ export const TaskLogPreview = ({
return (
-
+
{taskInstance.task_display_name}
+
@@ -66,15 +76,17 @@ export const TaskLogPreview = ({
-
-
-
+ {isExpanded ? (
+
+
+
+ ) : null}
);
};
diff --git a/airflow-core/src/airflow/ui/src/queries/useLogs.tsx b/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
index 760aec181f9e3..f2390554ae657 100644
--- a/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
+++ b/airflow-core/src/airflow/ui/src/queries/useLogs.tsx
@@ -20,6 +20,7 @@ import { chakra, Box } from "@chakra-ui/react";
import type { UseQueryOptions } from "@tanstack/react-query";
import dayjs from "dayjs";
import type { TFunction } from "i18next";
+import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import innerText from "react-innertext";
@@ -34,6 +35,7 @@ type Props = {
accept?: "*/*" | "application/json" | "application/x-ndjson";
dagId: string;
expanded?: boolean;
+ limit?: number;
logLevelFilters?: Array;
showSource?: boolean;
showTimestamp?: boolean;
@@ -184,6 +186,7 @@ export const useLogs = (
accept = "application/x-ndjson",
dagId,
expanded,
+ limit,
logLevelFilters,
showSource,
showTimestamp,
@@ -217,8 +220,25 @@ export const useLogs = (
},
);
+ // Log truncation is performed in the frontend because the backend
+ // does not support yet pagination / limits on logs reading endpoint
+ const truncatedData = useMemo(() => {
+ if (!data?.content || limit === undefined || limit <= 0) {
+ return data;
+ }
+
+ const streamingContent = parseStreamingLogContent(data);
+ const truncatedContent =
+ streamingContent.length > limit ? streamingContent.slice(-limit) : streamingContent;
+
+ return {
+ ...data,
+ content: truncatedContent,
+ };
+ }, [data, limit]);
+
const parsedData = parseLogs({
- data: parseStreamingLogContent(data),
+ data: parseStreamingLogContent(truncatedData),
expanded,
logLevelFilters,
showSource,