|
15 | 15 | </template> |
16 | 16 | <div> |
17 | 17 | <el-select @change="searchLogs" style="width: 30%; float: left" v-model="logSearch.mode"> |
| 18 | + <template #prefix>{{ $t('container.fetch') }}</template> |
18 | 19 | <el-option v-for="item in timeOptions" :key="item.label" :value="item.value" :label="item.label" /> |
19 | 20 | </el-select> |
| 21 | + <el-input |
| 22 | + @change="searchLogs" |
| 23 | + class="margin-button" |
| 24 | + style="width: 20%; float: left" |
| 25 | + v-model.number="logSearch.tail" |
| 26 | + > |
| 27 | + <template #prefix> |
| 28 | + <div style="margin-left: 2px">{{ $t('container.lines') }}</div> |
| 29 | + </template> |
| 30 | + </el-input> |
20 | 31 | <div class="margin-button" style="float: left"> |
21 | | - <el-checkbox border v-model="logSearch.isWatch">{{ $t('commons.button.watch') }}</el-checkbox> |
| 32 | + <el-checkbox border @change="searchLogs" v-model="logSearch.isWatch"> |
| 33 | + {{ $t('commons.button.watch') }} |
| 34 | + </el-checkbox> |
22 | 35 | </div> |
23 | 36 | <el-button class="margin-button" @click="onDownload" icon="Download"> |
24 | 37 | {{ $t('file.download') }} |
|
53 | 66 | </template> |
54 | 67 |
|
55 | 68 | <script lang="ts" setup> |
56 | | -import { cleanContainerLog, logContainer } from '@/api/modules/container'; |
| 69 | +import { cleanContainerLog } from '@/api/modules/container'; |
57 | 70 | import i18n from '@/lang'; |
58 | 71 | import { dateFormatForName } from '@/utils/util'; |
59 | | -import { nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue'; |
| 72 | +import { onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue'; |
60 | 73 | import { Codemirror } from 'vue-codemirror'; |
61 | 74 | import { javascript } from '@codemirror/lang-javascript'; |
62 | 75 | import { oneDark } from '@codemirror/theme-one-dark'; |
63 | 76 | import DrawerHeader from '@/components/drawer-header/index.vue'; |
64 | 77 | import { ElMessageBox } from 'element-plus'; |
65 | | -import { MsgSuccess } from '@/utils/message'; |
| 78 | +import { MsgError, MsgSuccess } from '@/utils/message'; |
66 | 79 | import screenfull from 'screenfull'; |
67 | 80 | import { GlobalStore } from '@/store'; |
68 | 81 |
|
69 | 82 | const extensions = [javascript(), oneDark]; |
70 | 83 |
|
71 | 84 | const logVisiable = ref(false); |
72 | 85 |
|
73 | | -const logInfo = ref(); |
| 86 | +const logInfo = ref<string>(''); |
74 | 87 | const view = shallowRef(); |
75 | 88 | const handleReady = (payload) => { |
76 | 89 | view.value = payload.view; |
77 | 90 | }; |
78 | 91 | const globalStore = GlobalStore(); |
| 92 | +const terminalSocket = ref<WebSocket>(); |
79 | 93 |
|
80 | 94 | const logSearch = reactive({ |
81 | 95 | isWatch: false, |
82 | 96 | container: '', |
83 | 97 | containerID: '', |
84 | 98 | mode: 'all', |
| 99 | + tail: 100, |
85 | 100 | }); |
86 | | -let timer: NodeJS.Timer | null = null; |
87 | 101 |
|
88 | 102 | const timeOptions = ref([ |
89 | 103 | { label: i18n.global.t('container.all'), value: 'all' }, |
@@ -115,22 +129,32 @@ screenfull.on('change', () => { |
115 | 129 | }); |
116 | 130 | const handleClose = async () => { |
117 | 131 | logVisiable.value = false; |
118 | | - clearInterval(Number(timer)); |
119 | | - timer = null; |
| 132 | + terminalSocket.value.close(); |
120 | 133 | }; |
121 | 134 | watch(logVisiable, (val) => { |
122 | 135 | if (screenfull.isEnabled && !val) screenfull.exit(); |
123 | 136 | }); |
124 | 137 | const searchLogs = async () => { |
125 | | - const res = await logContainer(logSearch); |
126 | | - logInfo.value = res.data || ''; |
127 | | - nextTick(() => { |
| 138 | + if (!Number(logSearch.tail) || Number(logSearch.tail) <= 0) { |
| 139 | + MsgError(global.i18n.$t('container.linesHelper')); |
| 140 | + return; |
| 141 | + } |
| 142 | + terminalSocket.value?.close(); |
| 143 | + logInfo.value = ''; |
| 144 | + const href = window.location.href; |
| 145 | + const protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss'; |
| 146 | + const host = href.split('//')[1].split('/')[0]; |
| 147 | + terminalSocket.value = new WebSocket( |
| 148 | + `${protocol}://${host}/containers/search/log?container=${logSearch.containerID}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}`, |
| 149 | + ); |
| 150 | + terminalSocket.value.onmessage = (event) => { |
| 151 | + logInfo.value += event.data; |
128 | 152 | const state = view.value.state; |
129 | 153 | view.value.dispatch({ |
130 | 154 | selection: { anchor: state.doc.length, head: state.doc.length }, |
131 | 155 | scrollIntoView: true, |
132 | 156 | }); |
133 | | - }); |
| 157 | + }; |
134 | 158 | }; |
135 | 159 |
|
136 | 160 | const onDownload = async () => { |
@@ -163,20 +187,15 @@ interface DialogProps { |
163 | 187 | const acceptParams = (props: DialogProps): void => { |
164 | 188 | logVisiable.value = true; |
165 | 189 | logSearch.containerID = props.containerID; |
166 | | - logSearch.mode = 'all'; |
| 190 | + logSearch.tail = 100; |
| 191 | + logSearch.mode = '10m'; |
167 | 192 | logSearch.isWatch = false; |
168 | 193 | logSearch.container = props.container; |
169 | 194 | searchLogs(); |
170 | | - timer = setInterval(() => { |
171 | | - if (logSearch.isWatch) { |
172 | | - searchLogs(); |
173 | | - } |
174 | | - }, 1000 * 5); |
175 | 195 | }; |
176 | 196 |
|
177 | 197 | onBeforeUnmount(() => { |
178 | | - clearInterval(Number(timer)); |
179 | | - timer = null; |
| 198 | + terminalSocket.value?.close(); |
180 | 199 | }); |
181 | 200 |
|
182 | 201 | defineExpose({ |
|
0 commit comments