🔨 执行日志.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import type { IDisposable, ITerminalOptions } from 'xterm';
|
||||
import { Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { SearchAddon } from 'xterm-addon-search';
|
||||
import { CanvasAddon } from 'xterm-addon-canvas';
|
||||
|
||||
// appender 配置
|
||||
export const AppenderOption: ITerminalOptions = {
|
||||
theme: {
|
||||
foreground: '#FFFFFF',
|
||||
background: '#212529',
|
||||
selectionBackground: '#B5D5FF',
|
||||
},
|
||||
rightClickSelectsWord: true,
|
||||
disableStdin: true,
|
||||
cursorStyle: 'bar',
|
||||
cursorBlink: false,
|
||||
fastScrollModifier: 'alt',
|
||||
fontSize: 14,
|
||||
lineHeight: 1.08,
|
||||
convertEol: true,
|
||||
};
|
||||
|
||||
// dom 引用
|
||||
export interface LogDomRef {
|
||||
id: number;
|
||||
el: HTMLElement;
|
||||
}
|
||||
|
||||
// appender 配置
|
||||
export interface LogAppenderConf {
|
||||
id: number;
|
||||
el: HTMLElement;
|
||||
terminal: Terminal;
|
||||
addons: LogAddons;
|
||||
}
|
||||
|
||||
// appender 插件
|
||||
export interface LogAddons extends Record<string, IDisposable> {
|
||||
fit: FitAddon;
|
||||
canvas: CanvasAddon;
|
||||
search: SearchAddon;
|
||||
}
|
||||
|
||||
// 执行日志 appender 定义
|
||||
export interface ILogAppender {
|
||||
// 初始化
|
||||
init(refs: Array<LogDomRef>): Promise<void>;
|
||||
|
||||
// 自适应
|
||||
fit(): void;
|
||||
|
||||
// 关闭 client
|
||||
closeClient(): void;
|
||||
|
||||
// 关闭 view
|
||||
closeView(): void;
|
||||
|
||||
// 关闭
|
||||
close(): void;
|
||||
}
|
||||
148
orion-ops-ui/src/components/view/log-appender/log-appender.ts
Normal file
148
orion-ops-ui/src/components/view/log-appender/log-appender.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import type { ExecTailRequest } from '@/api/exec/exec';
|
||||
import { getExecLogTailToken } from '@/api/exec/exec';
|
||||
import type { ILogAppender, LogAddons, LogAppenderConf, LogDomRef } from './appender.const';
|
||||
import { AppenderOption } from './appender.const';
|
||||
import { Terminal } from 'xterm';
|
||||
import { webSocketBaseUrl } from '@/utils/env';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { createWebSocket } from '@/utils';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { SearchAddon } from 'xterm-addon-search';
|
||||
import { CanvasAddon } from 'xterm-addon-canvas';
|
||||
|
||||
// todo ping
|
||||
// todo SEARCH addon
|
||||
// todo font-size totop copy tobottom selectall clear
|
||||
|
||||
// 执行日志 appender 实现
|
||||
export default class LogAppender implements ILogAppender {
|
||||
|
||||
private config: ExecTailRequest;
|
||||
|
||||
private client?: WebSocket;
|
||||
|
||||
private appenderRel: Record<string, LogAppenderConf>;
|
||||
|
||||
private keepAliveTask?: number;
|
||||
|
||||
constructor(config: ExecTailRequest) {
|
||||
this.config = config;
|
||||
this.appenderRel = {};
|
||||
}
|
||||
|
||||
// 初始化
|
||||
async init(logDomRefs: Array<LogDomRef>) {
|
||||
// 初始化 appender
|
||||
this.initAppender(logDomRefs);
|
||||
// 初始化 client
|
||||
await this.openClient();
|
||||
}
|
||||
|
||||
// 初始化 appender
|
||||
initAppender(logDomRefs: Array<LogDomRef>) {
|
||||
// 打开 log-view
|
||||
for (let logDomRef of logDomRefs) {
|
||||
// 初始化 terminal
|
||||
const terminal = new Terminal(AppenderOption);
|
||||
// 初始化插件
|
||||
const addons = this.initAddons(terminal);
|
||||
// 打开终端
|
||||
terminal.open(logDomRef.el);
|
||||
// 自适应
|
||||
addons.fit.fit();
|
||||
this.appenderRel[logDomRef.id] = {
|
||||
...logDomRef,
|
||||
terminal,
|
||||
addons
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化插件
|
||||
initAddons(terminal: Terminal): LogAddons {
|
||||
const fit = new FitAddon();
|
||||
const search = new SearchAddon();
|
||||
const canvas = new CanvasAddon();
|
||||
terminal.loadAddon(fit);
|
||||
terminal.loadAddon(search);
|
||||
terminal.loadAddon(canvas);
|
||||
return {
|
||||
fit,
|
||||
search,
|
||||
canvas
|
||||
};
|
||||
}
|
||||
|
||||
// 初始化 client
|
||||
async openClient() {
|
||||
// 获取 token
|
||||
const { data } = await getExecLogTailToken(this.config);
|
||||
// 打开会话
|
||||
this.client = await createWebSocket(`${webSocketBaseUrl}/exec/log/${data}`);
|
||||
this.client.onerror = event => {
|
||||
Message.error('连接失败');
|
||||
console.error('log error', event);
|
||||
};
|
||||
this.client.onclose = event => {
|
||||
console.warn('log close', event);
|
||||
};
|
||||
this.client.onmessage = this.processMessage.bind(this);
|
||||
// 注册持久化
|
||||
this.keepAliveTask = setInterval(() => {
|
||||
if (this.client?.readyState === WebSocket.OPEN) {
|
||||
this.client?.send('p');
|
||||
}
|
||||
}, 15000);
|
||||
}
|
||||
|
||||
// 自适应
|
||||
fit(): void {
|
||||
Object.values(this.appenderRel).forEach(s => {
|
||||
s.addons?.fit?.fit();
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭 client
|
||||
closeClient(): void {
|
||||
// 关闭 ws
|
||||
if (this.client && this.client.readyState === WebSocket.OPEN) {
|
||||
this.client.close();
|
||||
}
|
||||
// 清理持久化
|
||||
clearInterval(this.keepAliveTask);
|
||||
}
|
||||
|
||||
// 关闭 view
|
||||
closeView(): void {
|
||||
Object.values(this.appenderRel).forEach(s => {
|
||||
s.terminal?.dispose();
|
||||
if (s.addons) {
|
||||
Object.values(s.addons).forEach(s => s.dispose());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭
|
||||
close(): void {
|
||||
this.closeClient();
|
||||
this.closeView();
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
processMessage({ data }: MessageEvent<string>) {
|
||||
// pong
|
||||
if (data === 'p') {
|
||||
return;
|
||||
}
|
||||
const separatorIndex = data.indexOf('|');
|
||||
const id = data.substring(0, separatorIndex);
|
||||
const text = data.substring(separatorIndex + 1, data.length);
|
||||
// 获取 appender
|
||||
const appender = this.appenderRel[id];
|
||||
if (!appender) {
|
||||
return;
|
||||
}
|
||||
appender.terminal.write(text);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user