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,