From 2ffccf3b22b373debaf9c126a388fb75fe2ee7a3 Mon Sep 17 00:00:00 2001 From: fuliqiang <1348994179@qq.com> Date: Tue, 16 Jul 2024 00:09:28 +0800 Subject: [PATCH 1/9] =?UTF-8?q?job-log=E7=BB=84=E4=BB=B6=E6=8A=BD=E5=87=BA?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bigtop-manager-ui/.env.development | 3 +- bigtop-manager-ui/src/api/request.ts | 3 + bigtop-manager-ui/src/api/sse/index.ts | 11 +- .../src/components/job-info/job.vue | 145 +++++------------- .../src/components/job-info/log.vue | 120 +++++++++++++++ 5 files changed, 173 insertions(+), 109 deletions(-) create mode 100644 bigtop-manager-ui/src/components/job-info/log.vue diff --git a/bigtop-manager-ui/.env.development b/bigtop-manager-ui/.env.development index 65962102..d6c60b2c 100644 --- a/bigtop-manager-ui/.env.development +++ b/bigtop-manager-ui/.env.development @@ -17,5 +17,6 @@ NODE_ENV=development VITE_APP_BASE='/' #VITE_APP_BASE_URL='http://172.29.40.96:8080' -VITE_APP_BASE_URL='http://localhost:8080' +# VITE_APP_BASE_URL='http://localhost:8080' +VITE_APP_BASE_URL='http://bmdebug.v7.idcfengye.com' VITE_APP_BASE_API='/api' diff --git a/bigtop-manager-ui/src/api/request.ts b/bigtop-manager-ui/src/api/request.ts index 3a471b52..40b35a51 100644 --- a/bigtop-manager-ui/src/api/request.ts +++ b/bigtop-manager-ui/src/api/request.ts @@ -75,6 +75,9 @@ request.interceptors.response.use( } }, async (error: AxiosError) => { + if (error.code === AxiosError.ERR_CANCELED) { + return + } if (error.code === AxiosError.ERR_NETWORK) { message.error(i18n.global.t('common.error_network')) } else if (error.code === AxiosError.ETIMEDOUT) { diff --git a/bigtop-manager-ui/src/api/sse/index.ts b/bigtop-manager-ui/src/api/sse/index.ts index c07ad277..52ee663e 100644 --- a/bigtop-manager-ui/src/api/sse/index.ts +++ b/bigtop-manager-ui/src/api/sse/index.ts @@ -17,20 +17,25 @@ * under the License. */ +import axios, { type AxiosProgressEvent, type CancelTokenSource } from 'axios' import request from '@/api/request.ts' -import { AxiosProgressEvent } from 'axios' export const getLogs = ( clusterId: number, id: number, func: Function -): Promise => { - return request({ +): { promise: Promise; cancel: () => void } => { + const source: CancelTokenSource = axios.CancelToken.source() + + const promise = request({ method: 'get', url: `/sse/clusters/${clusterId}/tasks/${id}/log`, responseType: 'stream', timeout: 0, + cancelToken: source.token, onDownloadProgress: (progressEvent: AxiosProgressEvent) => func(progressEvent) }) + + return { promise, cancel: source.cancel } } diff --git a/bigtop-manager-ui/src/components/job-info/job.vue b/bigtop-manager-ui/src/components/job-info/job.vue index 9ca85680..ddf622c8 100644 --- a/bigtop-manager-ui/src/components/job-info/job.vue +++ b/bigtop-manager-ui/src/components/job-info/job.vue @@ -21,9 +21,7 @@ import { ref, watch, computed, reactive, toRaw, toRefs } from 'vue' import { useClusterStore } from '@/store/cluster' import { PaginationConfig } from 'ant-design-vue/es/pagination/Pagination' - import { CopyOutlined } from '@ant-design/icons-vue' import { storeToRefs } from 'pinia' - import { message } from 'ant-design-vue' import { JobVO, StageVO, @@ -34,14 +32,12 @@ import { getLogs } from '@/api/sse' import { getJobs } from '@/api/job' import { Pausable, useIntervalFn } from '@vueuse/core' - import { AxiosProgressEvent } from 'axios' + import { AxiosProgressEvent, Canceler } from 'axios' import { MONITOR_SCHEDULE_INTERVAL } from '@/utils/constant.ts' import CustomProgress from './custom-progress.vue' import Stage from './stage.vue' import Task from './task.vue' - import { copyText } from '@/utils/tools' - import { useI18n } from 'vue-i18n' - const { t } = useI18n() + import Job from './log.vue' const columns = [ { @@ -69,21 +65,22 @@ const clusterStore = useClusterStore() const { clusterId } = storeToRefs(clusterStore) - const props = withDefaults( - defineProps<{ - visible: boolean - outerData?: OuterData - }>(), - { - visible: false, - outerData: undefined - } - ) + interface Props { + visible: boolean + outerData?: OuterData + } + + const props = withDefaults(defineProps(), { + visible: false, + outerData: undefined + }) + const { visible, outerData } = toRefs(props) const emits = defineEmits(['update:visible', 'closed']) const loading = ref(false) + const canceler = ref() const stages = ref([]) const tasks = ref([]) const breadcrumbs = ref([{ name: 'Job Info' }]) @@ -91,7 +88,6 @@ const jobs = ref([]) const intervalId = ref() const logTextOrigin = ref('') - const logsInfoRef = ref(null) const currPage = ref([ 'isJobTable', 'isStageTable', @@ -110,15 +106,6 @@ return currPage.value[breadcrumbs.value.length - 1] }) - const logText = computed(() => { - return logTextOrigin.value - .split('\n\n') - .map((s) => { - return s.substring(5) - }) - .join('\n') - }) - watch(visible, (val) => { if (val) { loading.value = true @@ -189,35 +176,31 @@ } } - const getLogsInfo = (id: number) => { - getLogs(clusterId.value, id, ({ event }: AxiosProgressEvent) => { - logTextOrigin.value = event.target.responseText - logsInfoRef.value = document.querySelector('.logsInfoRef') - if (!logsInfoRef.value) { - return - } - ;(function smoothscroll() { - const { scrollTop, offsetHeight, scrollHeight } = logsInfoRef.value - if (scrollHeight - 10 > scrollTop + offsetHeight) { - window.requestAnimationFrame(smoothscroll) - logsInfoRef.value.scrollTo( - 0, - scrollTop + (scrollHeight - scrollTop - offsetHeight) / 2 - ) - } - })() - }) + const getLogProgress = ({ event }: AxiosProgressEvent) => { + logTextOrigin.value = event.target.responseText + // logsInfoRef.value = document.querySelector('.logsInfoRef') + // if (!logsInfoRef.value) { + // return + // } + // ;(function smoothscroll() { + // const { scrollTop, offsetHeight, scrollHeight } = logsInfoRef.value + // if (scrollHeight - 10 > scrollTop + offsetHeight) { + // window.requestAnimationFrame(smoothscroll) + // logsInfoRef.value.scrollTo( + // 0, + // scrollTop + (scrollHeight - scrollTop - offsetHeight) / 2 + // ) + // } + // })() } - const copyLogTextContent = (text: string) => { - copyText(text) - .then(() => { - message.success(`${t('common.copy_success')}`) - }) - .catch((err: Error) => { - message.error(`${t('common.copy_fail')}`) - console.log('err :>> ', err) - }) + const cancelSseConnect = () => { + console.log('1111 :>> ', 1111) + } + + const getLogsInfo = (id: number) => { + const { cancel } = getLogs(clusterId.value, id, getLogProgress) + canceler.value = cancel } const clickTask = (record: TaskVO) => { @@ -336,26 +319,10 @@ @@ -366,36 +333,4 @@ margin-bottom: 16px !important; } } - .logs { - height: 50vh; - display: flex; - flex-direction: column; - &_header { - font-size: 16px; - font-weight: 600; - margin: 0 0 10px 0; - display: flex; - justify-content: space-between; - - .copy-button { - margin-left: 3px; - } - } - &_info { - height: 100%; - overflow: auto; - background-color: #f5f5f5; - border-radius: 4px; - position: relative; - pre { - height: 100%; - margin: 0; - padding: 16px 14px; - box-sizing: border-box; - color: #444; - border-color: #eee; - line-height: 16px; - } - } - } diff --git a/bigtop-manager-ui/src/components/job-info/log.vue b/bigtop-manager-ui/src/components/job-info/log.vue new file mode 100644 index 00000000..6e64864a --- /dev/null +++ b/bigtop-manager-ui/src/components/job-info/log.vue @@ -0,0 +1,120 @@ + + + + + + + From ea49dd858fead270b2fe88c700f8e142d05d7d51 Mon Sep 17 00:00:00 2001 From: fuliqiang <1348994179@qq.com> Date: Tue, 16 Jul 2024 00:24:41 +0800 Subject: [PATCH 2/9] =?UTF-8?q?useTable=E7=BB=84=E5=90=88=E5=BC=8F?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/job-info/job.vue | 7 +- bigtop-manager-ui/src/composables/useTable.ts | 76 +++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 bigtop-manager-ui/src/composables/useTable.ts diff --git a/bigtop-manager-ui/src/components/job-info/job.vue b/bigtop-manager-ui/src/components/job-info/job.vue index ddf622c8..355c0474 100644 --- a/bigtop-manager-ui/src/components/job-info/job.vue +++ b/bigtop-manager-ui/src/components/job-info/job.vue @@ -195,7 +195,10 @@ } const cancelSseConnect = () => { - console.log('1111 :>> ', 1111) + if (!canceler.value) { + return + } + canceler.value() } const getLogsInfo = (id: number) => { @@ -321,7 +324,7 @@ diff --git a/bigtop-manager-ui/src/composables/useTable.ts b/bigtop-manager-ui/src/composables/useTable.ts new file mode 100644 index 00000000..3044882d --- /dev/null +++ b/bigtop-manager-ui/src/composables/useTable.ts @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ref, onUnmounted } from 'vue' +import type { TablePaginationConfig, TableColumnType } from 'ant-design-vue' + +const useBaseTable = ( + columns: TableColumnType[], + rows?: T[], + pagination?: TablePaginationConfig | false | undefined +) => { + const loading = ref(false) + const dataSource = ref(rows || []) + const columnsProp = ref(columns) + const paginateProp = ref({ + current: 1, + pageSize: 10, + total: dataSource.value.length, + size: 'small', + showSizeChanger: true, + pageSizeOptions: ['10', '20', '30', '40', '50'] + }) + + // merge pagination config + if (pagination) { + paginateProp.value = Object.assign(paginateProp.value, pagination) + } + + const onChange = (pagination: TablePaginationConfig) => { + paginateProp.value = Object.assign(paginateProp.value, pagination) + } + + const restState = () => { + loading.value = false + dataSource.value = [] + paginateProp.value = { + current: 1, + pageSize: 10, + total: dataSource.value.length || 0, + size: 'small', + showSizeChanger: true, + pageSizeOptions: ['10', '20', '30', '40', '50'] + } + } + + onUnmounted(() => { + restState() + }) + + return { + columnsProp, + dataSource, + loading, + paginateProp, + onChange, + restState + } +} + +export default useBaseTable From 56e25fe19d3959cf9591e370710f1a031c865c44 Mon Sep 17 00:00:00 2001 From: fuliqiang <1348994179@qq.com> Date: Tue, 16 Jul 2024 00:40:27 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E4=BC=98=E5=8C=96task=E7=9A=84log=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=B1=95=E7=A4=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/job-info/job.vue | 51 ++----------- .../src/components/job-info/log.vue | 71 ++++++++++++++----- 2 files changed, 57 insertions(+), 65 deletions(-) diff --git a/bigtop-manager-ui/src/components/job-info/job.vue b/bigtop-manager-ui/src/components/job-info/job.vue index 355c0474..e2bf4b21 100644 --- a/bigtop-manager-ui/src/components/job-info/job.vue +++ b/bigtop-manager-ui/src/components/job-info/job.vue @@ -29,15 +29,13 @@ OuterData, Pagination } from '@/api/job/types.ts' - import { getLogs } from '@/api/sse' import { getJobs } from '@/api/job' import { Pausable, useIntervalFn } from '@vueuse/core' - import { AxiosProgressEvent, Canceler } from 'axios' import { MONITOR_SCHEDULE_INTERVAL } from '@/utils/constant.ts' import CustomProgress from './custom-progress.vue' import Stage from './stage.vue' import Task from './task.vue' - import Job from './log.vue' + import TaskLog from './log.vue' const columns = [ { @@ -80,14 +78,13 @@ const emits = defineEmits(['update:visible', 'closed']) const loading = ref(false) - const canceler = ref() const stages = ref([]) const tasks = ref([]) const breadcrumbs = ref([{ name: 'Job Info' }]) const currTaskInfo = ref() const jobs = ref([]) const intervalId = ref() - const logTextOrigin = ref('') + const logRef = ref | null>() const currPage = ref([ 'isJobTable', 'isStageTable', @@ -110,11 +107,7 @@ if (val) { loading.value = true Object.assign(paginationProps, initPagedProps()) - if (outerData.value) { - checkMetaOrigin(true) - } else { - checkMetaOrigin(false) - } + checkMetaOrigin(outerData.value ? true : false) loading.value = false } }) @@ -176,41 +169,10 @@ } } - const getLogProgress = ({ event }: AxiosProgressEvent) => { - logTextOrigin.value = event.target.responseText - // logsInfoRef.value = document.querySelector('.logsInfoRef') - // if (!logsInfoRef.value) { - // return - // } - // ;(function smoothscroll() { - // const { scrollTop, offsetHeight, scrollHeight } = logsInfoRef.value - // if (scrollHeight - 10 > scrollTop + offsetHeight) { - // window.requestAnimationFrame(smoothscroll) - // logsInfoRef.value.scrollTo( - // 0, - // scrollTop + (scrollHeight - scrollTop - offsetHeight) / 2 - // ) - // } - // })() - } - - const cancelSseConnect = () => { - if (!canceler.value) { - return - } - canceler.value() - } - - const getLogsInfo = (id: number) => { - const { cancel } = getLogs(clusterId.value, id, getLogProgress) - canceler.value = cancel - } - const clickTask = (record: TaskVO) => { breadcrumbs.value.push(record) currTaskInfo.value = record - logTextOrigin.value = '' - getLogsInfo(record.id) + logRef.value?.getLogsInfo(record.id) } const clickJob = (record: JobVO) => { @@ -322,10 +284,7 @@ diff --git a/bigtop-manager-ui/src/components/job-info/log.vue b/bigtop-manager-ui/src/components/job-info/log.vue index 6e64864a..3b06aa32 100644 --- a/bigtop-manager-ui/src/components/job-info/log.vue +++ b/bigtop-manager-ui/src/components/job-info/log.vue @@ -23,30 +23,57 @@ import { message } from 'ant-design-vue' import { ref, watch, onBeforeUnmount } from 'vue' import { CopyOutlined } from '@ant-design/icons-vue' - - interface Props { - logMeta: string - } - - const props = withDefaults(defineProps(), { - logMeta: '' - }) + import { AxiosProgressEvent, Canceler } from 'axios' + import { getLogs } from '@/api/sse' + import { storeToRefs } from 'pinia' + import { useClusterStore } from '@/store/cluster' const { t } = useI18n() + const clusterStore = useClusterStore() + const { clusterId } = storeToRefs(clusterStore) const logsInfoRef = ref(null) const logText = ref(null) + const logMeta = ref('') + const canceler = ref() + + watch(logMeta, (val) => { + logText.value = val + .split('\n\n') + .map((s) => { + return s.substring(5) + }) + .join('\n') + }) + + const getLogsInfo = (id: number) => { + const { cancel } = getLogs(clusterId.value, id, getLogProgress) + canceler.value = cancel + } + + const getLogProgress = ({ event }: AxiosProgressEvent) => { + logMeta.value = event.target.responseText + // logsInfoRef.value = document.querySelector('.logsInfoRef') + // if (!logsInfoRef.value) { + // return + // } + // ;(function smoothscroll() { + // const { scrollTop, offsetHeight, scrollHeight } = logsInfoRef.value + // if (scrollHeight - 10 > scrollTop + offsetHeight) { + // window.requestAnimationFrame(smoothscroll) + // logsInfoRef.value.scrollTo( + // 0, + // scrollTop + (scrollHeight - scrollTop - offsetHeight) / 2 + // ) + // } + // })() + } - watch( - () => props.logMeta, - (val) => { - logText.value = val - .split('\n\n') - .map((s) => { - return s.substring(5) - }) - .join('\n') + const cancelSseConnect = () => { + if (!canceler.value) { + return } - ) + canceler.value() + } const copyLogTextContent = (text: string | null) => { if (!text) { @@ -62,7 +89,13 @@ }) } - onBeforeUnmount(() => {}) + onBeforeUnmount(() => { + cancelSseConnect() + }) + + defineExpose({ + getLogsInfo + })