mirror of https://github.com/alibaba/arthas.git
chore(web ui):ref tunnel modules (#2331)
This commit is contained in:
parent
aaeb2d2fd4
commit
b252ca2b1b
|
|
@ -1,2 +1,3 @@
|
|||
# NODE_ENV=production
|
||||
VITE_AGENT=true
|
||||
VITE_ARTHAS_PORT=7777
|
||||
VITE_ARTHAS_PROXY_PORT=8080
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
# NODE_ENV=production
|
||||
VITE_AGENT=false
|
||||
VITE_ARTHAS_PORT=8563
|
||||
VITE_ARTHAS_PROXY_PORT=8563
|
||||
|
|
@ -6,7 +6,7 @@ declare module '*.vue' {
|
|||
export default component
|
||||
}
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_AGENT:string
|
||||
readonly VITE_ARTHAS_PORT:string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 542 B After Width: | Height: | Size: 542 B |
|
|
@ -1,21 +1,31 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { onMounted, ref, computed } from "vue";
|
||||
import { Terminal } from "xterm"
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { WebglAddon } from "xterm-addon-webgl"
|
||||
import { MenuAlt2Icon } from "@heroicons/vue/outline"
|
||||
import fullPic from "~/assert/fullsc.png"
|
||||
import arthasLogo from "~/assert/arthas.png"
|
||||
const { isTunnel = false } = defineProps<{
|
||||
isTunnel?: boolean
|
||||
}>()
|
||||
|
||||
let ws: WebSocket | undefined;
|
||||
|
||||
let intervalReadKey = -1
|
||||
const DEFAULT_SCROLL_BACK = 1000
|
||||
const MAX_SCROLL_BACK = 9999999
|
||||
const MIN_SCROLL_BACK = 1
|
||||
const ARTHAS_PORT = '8563'
|
||||
const ARTHAS_PORT = isTunnel ? "7777" : "8563"
|
||||
const ip = ref("")
|
||||
const port = ref('')
|
||||
const iframe = ref(true)
|
||||
const fullSc = ref(true)
|
||||
const agentID = ref('')
|
||||
const isTunnel = import.meta.env.VITE_AGENT === 'true'
|
||||
const outputHerf = computed(() => {
|
||||
console.log(agentID.value)
|
||||
return isTunnel?`proxy/${agentID.value}/arthas-output/`:`/arthas-output/`
|
||||
})
|
||||
// const isTunnel = import.meta.env.MODE === 'tunnel'
|
||||
const fitAddon = new FitAddon();
|
||||
const webglAddon = new WebglAddon();
|
||||
let xterm = new Terminal({ allowProposedApi: true })
|
||||
|
|
@ -23,6 +33,7 @@ let xterm = new Terminal({ allowProposedApi: true })
|
|||
onMounted(() => {
|
||||
ip.value = getUrlParam('ip') ?? window.location.hostname;
|
||||
port.value = getUrlParam('port') ?? ARTHAS_PORT;
|
||||
if (isTunnel) agentID.value = getUrlParam("agentId") ?? ""
|
||||
let _iframe = getUrlParam('iframe')
|
||||
if (_iframe && _iframe.trim() !== 'false') iframe.value = false
|
||||
|
||||
|
|
@ -42,12 +53,22 @@ function getUrlParam(name: string) {
|
|||
return urlparam.get(name)
|
||||
}
|
||||
|
||||
|
||||
function getWsUri() {
|
||||
const host = `${ip.value}:${port.value}`
|
||||
if (!isTunnel) return `ws://${host}/ws`;
|
||||
const path = getUrlParam("path") ?? 'ws'
|
||||
const _targetServer = getUrlParam("targetServer")
|
||||
let protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
const uri = `${protocol}${host}/${encodeURIComponent(path)}?method=connectArthas&id=${agentID.value}`
|
||||
if (_targetServer != null) {
|
||||
return uri + '&targetServer=' + encodeURIComponent(_targetServer);
|
||||
}
|
||||
return uri
|
||||
}
|
||||
/** init websocket **/
|
||||
function initWs(silent: boolean) {
|
||||
let path = 'ws://' + ip.value + ':' + port.value + '/ws';
|
||||
console.log(path)
|
||||
ws = new WebSocket(path);
|
||||
let uri = getWsUri()
|
||||
ws = new WebSocket(uri);
|
||||
ws.onerror = function () {
|
||||
ws ?? ws!.close();
|
||||
ws = undefined;
|
||||
|
|
@ -69,12 +90,21 @@ function initWs(silent: boolean) {
|
|||
}
|
||||
};
|
||||
ws?.send(JSON.stringify({ action: 'resize', cols, rows }));
|
||||
window.setInterval(function () {
|
||||
intervalReadKey = window.setInterval(function () {
|
||||
if (ws != null && ws.readyState === 1) {
|
||||
ws.send(JSON.stringify({ action: 'read', data: "" }));
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
ws.onclose = function (message) {
|
||||
if (intervalReadKey != -1) {
|
||||
window.clearInterval(intervalReadKey)
|
||||
intervalReadKey = -1
|
||||
}
|
||||
if (message.code === 2000) {
|
||||
alert(message.reason);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** init xterm **/
|
||||
|
|
@ -103,18 +133,31 @@ function isValidNumber(scrollNumber: number) {
|
|||
scrollNumber <= MAX_SCROLL_BACK;
|
||||
}
|
||||
|
||||
/** begin connect **/
|
||||
function startConnect(silent: boolean = false) {
|
||||
const connectGuard = (silent: boolean): boolean => {
|
||||
if (ip.value.trim() === '' || port.value.trim() === '') {
|
||||
alert('Ip or port can not be empty');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (isTunnel && agentID.value == '') {
|
||||
if (silent) {
|
||||
return false;
|
||||
}
|
||||
alert('AgentId can not be empty');
|
||||
return false;
|
||||
}
|
||||
if (ws) {
|
||||
alert('Already connected');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// init webSocket
|
||||
initWs(silent);
|
||||
return true
|
||||
}
|
||||
/** begin connect **/
|
||||
function startConnect(silent: boolean = false) {
|
||||
if (connectGuard(silent)) {
|
||||
// init webSocket
|
||||
initWs(silent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
|
|
@ -126,7 +169,6 @@ function disconnect() {
|
|||
xterm.dispose();
|
||||
fitAddon.dispose()
|
||||
webglAddon.dispose()
|
||||
// $('#fullSc').hide();
|
||||
fullSc.value = false
|
||||
alert('Connection was closed successfully!');
|
||||
} catch {
|
||||
|
|
@ -157,16 +199,37 @@ function requestFullScreen(element: HTMLElement) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-[100vh] resize-none">
|
||||
<nav v-if="iframe" class="navbar bg-base-100 flex-row">
|
||||
<div class="flex-1">
|
||||
<a href="https://github.com/alibaba/arthas" target="_blank" title="" class="mr-2 w-20"><img src="/arthas.png"
|
||||
alt="Arthas" title="Welcome to Arthas web console" class=""></a>
|
||||
<ul class="menu menu-horizontal p-0">
|
||||
<div class="flex flex-col h-[100vh] w-[100vw] resize-none">
|
||||
<nav v-if="iframe" class="navbar bg-base-100 md:flex-row flex-col w-[100vw]">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown dropdown-start 2xl:hidden">
|
||||
<label tabindex="0" class="btn btn-ghost btn-sm">
|
||||
<MenuAlt2Icon class="w-6 h-6"></MenuAlt2Icon>
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content menu shadow bg-base-100">
|
||||
<li>
|
||||
<a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://arthas.aliyun.com/doc"
|
||||
target="_blank">Documentation
|
||||
<span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm"
|
||||
href="https://arthas.aliyun.com/doc/arthas-tutorials.html" target="_blank">Online
|
||||
Tutorials</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://github.com/alibaba/arthas"
|
||||
target="_blank">Github</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="https://github.com/alibaba/arthas" target="_blank" title="" class="mr-2 w-20"><img
|
||||
:src="arthasLogo" alt="Arthas" title="Welcome to Arthas web console"></a>
|
||||
|
||||
<ul class="menu menu-vertical 2xl:menu-horizontal hidden">
|
||||
<li>
|
||||
<a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://arthas.aliyun.com/doc"
|
||||
target="_blank">Documentation
|
||||
|
|
@ -182,9 +245,14 @@ function requestFullScreen(element: HTMLElement) {
|
|||
target="_blank">Github</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<form class="navbar-end">
|
||||
<div class="flex">
|
||||
<div class="navbar-center ">
|
||||
<div class=" xl:flex-row form-control"
|
||||
:class="{
|
||||
'xl:flex-row':isTunnel,
|
||||
'lg:flex-row':!isTunnel
|
||||
}">
|
||||
<label class="input-group input-group-sm mr-2">
|
||||
<span>IP</span>
|
||||
<input type="text" placeholder="please enter ip address" class="input input-bordered input-sm "
|
||||
|
|
@ -194,28 +262,35 @@ function requestFullScreen(element: HTMLElement) {
|
|||
<span>Port</span>
|
||||
<input type="text" placeholder="please enter port" class="input input-sm input-bordered" v-model="port" />
|
||||
</label>
|
||||
<label v-if="isTunnel" class="input-group input-group-sm mr-2">
|
||||
<span>AgentId</span>
|
||||
<input type="text" placeholder="please enter AgentId" class="input input-sm input-bordered"
|
||||
v-model="agentID" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group btn-group-horizontal">
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="btn-group 2xl:btn-group-horizontal btn-group-horizontal"
|
||||
:class="{
|
||||
'md:btn-group-vertical':isTunnel
|
||||
}">
|
||||
<button
|
||||
class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
|
||||
@click.prevent="startConnect(true)">Connect</button>
|
||||
<button
|
||||
class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
|
||||
@click.prevent="disconnect">Disconnect</button>
|
||||
<a v-if="!isTunnel" class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
|
||||
href="arthas-output/" target="_blank">Arthas Output</a>
|
||||
<a class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
|
||||
:href="outputHerf" target="_blank">Arthas Output</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
<div class="w-full h-0 flex-auto bg-black overscroll-auto" id="terminal-card">
|
||||
<!-- <div class="h-full overflow-visible" id="terminal-card"> -->
|
||||
<div id="terminal" class="w-full h-full"></div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<div title="fullscreen" id="fullSc" class="fullSc" v-if="fullSc">
|
||||
<button id="fullScBtn" @click="xtermFullScreen"><img src="/fullsc.png"></button>
|
||||
<button id="fullScBtn" @click="xtermFullScreen"><img :src="fullPic"></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Arthas Tutorials</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
</div>
|
||||
<script src="./src/agents.ts" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Arthas Tutorials</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
</div>
|
||||
|
||||
<script src="./src/apps.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue';
|
||||
type AgentId = string
|
||||
const agentInfos: ([AgentId, AgentInfo])[] = reactive([])
|
||||
function getUrlParam(name: string) {
|
||||
const urlparam = new URLSearchParams(window.location.search)
|
||||
return urlparam.get(name)
|
||||
}
|
||||
function tunnelWebConsoleLink(agentId: string, targetServer: string) {
|
||||
return `/?targetServer=${targetServer}&agentId=${agentId}`;
|
||||
}
|
||||
|
||||
const fetchMyApps = () => {
|
||||
const appName = getUrlParam("app") ?? ""
|
||||
let url = `/api/tunnelAgentInfo?app=${appName}`
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data: Record<AgentId, AgentInfo>) => {
|
||||
for (const key in data) {
|
||||
agentInfos.push(
|
||||
[key, data[key] as AgentInfo]
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
.catch((error) => console.error('api error ' + error))
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchMyApps()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table class="table table-normal w-[100vw]">
|
||||
<thead>
|
||||
<tr >
|
||||
<th class="normal-case">IP</th>
|
||||
<th class="normal-case">AgentId</th>
|
||||
<th class="normal-case">Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(agentInfoRecord) in agentInfos" :key="agentInfoRecord[0]" class="hover">
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm"
|
||||
:href="tunnelWebConsoleLink(agentInfoRecord[0], agentInfoRecord[1].clientConnectHost)">{{
|
||||
agentInfoRecord[1].host
|
||||
}}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ agentInfoRecord[0] }}
|
||||
</td>
|
||||
<td>
|
||||
{{ agentInfoRecord[1].arthasVersion }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue';
|
||||
|
||||
const apps: string[] = reactive([])
|
||||
function fetchMyApps() {
|
||||
fetch('/api/tunnelApps')
|
||||
.then((response) => response.json())
|
||||
.then((data: string[]) => {
|
||||
data.length > 0 && data.forEach(app => apps.push(app))
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('api error ' + error)
|
||||
})
|
||||
}
|
||||
function detailLink(appName: string) {
|
||||
return "agents.html?app=" + appName;
|
||||
}
|
||||
onMounted(() => {
|
||||
fetchMyApps()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table class="table w-[100vw] table-normal">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="normal-case">name of application</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="app in apps" :key="app" class="hover">
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm normal-case" :href="detailLink(app)">{{ app }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { createApp } from "vue";
|
||||
import App from "./Agent.vue";
|
||||
const app = createApp(App);
|
||||
import "~/main.css";
|
||||
app
|
||||
.mount("#app");
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import App from "./Apps.vue"
|
||||
import { createApp } from "vue"
|
||||
import "~/main.css";
|
||||
createApp(App)
|
||||
.mount("#app");
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { createApp, h } from "vue";
|
||||
import App from "~/component/Console.vue";
|
||||
const app = createApp(h(App, { isTunnel: true }));
|
||||
import "xterm/css/xterm.css";
|
||||
import "~/main.css";
|
||||
app
|
||||
.mount("#app");
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
type AgentInfo = {
|
||||
clientConnectHost:string,
|
||||
host:string,
|
||||
port:number,
|
||||
arthasVersion:string
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html lang="en" data-theme="corporate">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Arthas Console</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./src/main.ts" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import App from "~/component/Console.vue";
|
||||
const app = createApp(App);
|
||||
import "xterm/css/xterm.css"
|
||||
import "./main.css"
|
||||
import "~/main.css"
|
||||
app
|
||||
.mount("#app");
|
||||
|
|
@ -18,14 +18,9 @@ onBeforeUnmount(() => {
|
|||
<template>
|
||||
<div class=" h-screen flex flex-col">
|
||||
<nav-header class="h-[10vh]"></nav-header>
|
||||
<div class=" flex-auto h-[90vh] overflow-auto">
|
||||
<div class="flex flex-row h-full">
|
||||
<!-- <nav-aside ></nav-aside> -->
|
||||
<div class="flex-auto overflow-auto h-[90vh] w-[90vw]">
|
||||
<div class=" flex-auto h-[90vh] overflow-auto w-[100vw]">
|
||||
<router-view>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useMachine } from '@xstate/vue';
|
||||
import { computed, onBeforeMount, Ref, ref, watchEffect } from 'vue';
|
||||
import { RefreshIcon, LogoutIcon, LoginIcon, MenuIcon, XCircleIcon } from '@heroicons/vue/outline';
|
||||
import { RefreshIcon, LogoutIcon, LoginIcon, MenuIcon, MenuAlt2Icon } from '@heroicons/vue/outline';
|
||||
import { fetchStore } from '@/stores/fetch';
|
||||
import machine from "@/machines/consoleMachine"
|
||||
import { publicStore } from '@/stores/public';
|
||||
|
|
@ -10,6 +10,7 @@ import { PuzzleIcon, TerminalIcon, ViewGridIcon } from "@heroicons/vue/outline"
|
|||
import { DesktopComputerIcon } from "@heroicons/vue/solid"
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import permachine from '@/machines/perRequestMachine';
|
||||
import pic from "~/assert/arthas.png"
|
||||
const fetchM = useMachine(machine)
|
||||
const publicS = publicStore()
|
||||
const { send } = fetchM
|
||||
|
|
@ -130,9 +131,9 @@ const tabs = [
|
|||
},
|
||||
{
|
||||
|
||||
name:'terminal',
|
||||
url:'terminal',
|
||||
icon:TerminalIcon
|
||||
name: 'terminal',
|
||||
url: 'terminal',
|
||||
icon: TerminalIcon
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -148,23 +149,38 @@ const toNext = (url: string) => {
|
|||
window.open("/", "_blank")
|
||||
} else router.push(url)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class=" h-[10vh] border-b-2 navbar">
|
||||
<div class=" navbar-start flex items-stretch ">
|
||||
<!-- <div class=" indicator mx-3"> -->
|
||||
<nav class=" h-[10vh] border-b-2 navbar bg-base-100">
|
||||
<div class=" navbar-start flex items-stretch">
|
||||
<div class="dropdown dropdown-start hover xl:hidden">
|
||||
<label tabindex="0" class="btn btn-ghost m-1">
|
||||
<MenuAlt2Icon class="w-6 h-6"></MenuAlt2Icon>
|
||||
</label>
|
||||
<ul tabindex="0" class="menu menu-vertical dropdown-content bg-base-100 shadow rounded-box">
|
||||
<li v-for="(tab, idx) in tabs" :key="idx" @click="toNext(tab.url)">
|
||||
<a :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
|
||||
<component :is="tab.icon" class="w-4 h-4" />
|
||||
{{
|
||||
tab.name
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="flex items-center justify-center mx-2" href="https://arthas.aliyun.com/doc/commands.html"
|
||||
target="_blank">
|
||||
<img src="/arthas.png" alt="logo" class="w-32" />
|
||||
<img :src="pic" alt="logo" class="w-32" />
|
||||
</a>
|
||||
<span class="badge badge-ghost self-end text-sm">v{{ version }}</span>
|
||||
<!-- </div> -->
|
||||
<span class="badge badge-ghost self-end badge-sm">v{{ version }}</span>
|
||||
</div>
|
||||
<div class="navbar-center">
|
||||
<ul class="menu menu-horizontal p-0">
|
||||
|
||||
<ul class="menu menu-horizontal hidden xl:flex">
|
||||
<li v-for="(tab, idx) in tabs" :key="idx" @click="toNext(tab.url)">
|
||||
<a class="break-all" :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
|
||||
<a :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
|
||||
<component :is="tab.icon" class="w-4 h-4" />
|
||||
{{
|
||||
tab.name
|
||||
|
|
@ -181,18 +197,16 @@ const toNext = (url: string) => {
|
|||
<MenuIcon class=" w-6 h-6"></MenuIcon>
|
||||
</label>
|
||||
<ul tabindex="0" class="menu dropdown-content p-2 shadow-xl bg-base-200 rounded-box w-40">
|
||||
<li class="" v-for="(v,i) in tools" :key="i">
|
||||
<a @click.prevent="v[1]">{{v[0]}}</a>
|
||||
<li class="" v-for="(v, i) in tools" :key="i">
|
||||
<a @click.prevent="v[1]">{{ v[0] }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class=" btn btn-ghost"
|
||||
:class="{ 'btn-primary': !fetchS.online, 'btn-error': fetchS.online }">
|
||||
<button class=" btn btn-ghost" :class="{ 'btn-primary': !fetchS.online, 'btn-error': fetchS.online }">
|
||||
<LogoutIcon class="h-6 w-6" @click="logout" v-if="fetchS.online" />
|
||||
<login-icon class="h-6 w-6" @click="login" v-else />
|
||||
</button>
|
||||
<button class="btn-ghost btn"
|
||||
@click="reset">
|
||||
<button class="btn-ghost btn" @click="reset">
|
||||
<refresh-icon class="h-6 w-6" :class="restBtnclass" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { createApp } from "vue";
|
||||
import "./index.css";
|
||||
import "~/main.css"
|
||||
import router from "./router/index";
|
||||
import { createPinia } from "pinia";
|
||||
import App from "./App.vue";
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export default function transformStackTrace(trace: StackTrace) {
|
||||
return `${trace.className}.${trace.methodName} (${trace.fileName}: ${trace.lineNumber})`;
|
||||
}
|
||||
|
|
@ -441,52 +441,63 @@ onBeforeUnmount(async () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-2 pointer-events-auto flex flex-col h-full">
|
||||
<div class="input-btn-style mb-4 h-32 flex flex-wrap flex-col items-start overflow-auto min-h-[6rem]">
|
||||
<div v-for="(cv, ci) in runtimeInfo" :key="ci" class="flex mb-1 pr-2">
|
||||
<span class="bg-primary-focus text-primary-content border border-primary-focus w-44 px-2 rounded-l">
|
||||
{{ cv[0] }}
|
||||
</span>
|
||||
<span class="bg-base-200 border border-primary-focus rounded-r px-2 flex-1">
|
||||
{{cv[1]}}
|
||||
<div class="p-2 pointer-events-auto h-full overflow-auto">
|
||||
<div class="card bg-base-100 border mb-4 compact h-auto">
|
||||
<div class="card-body flex-wrap flex-row">
|
||||
<span v-for="(cv, ci) in runtimeInfo" :key="ci" class="badge badge-outline badge-primary">
|
||||
|
||||
{{ cv[0] }}:
|
||||
{{ cv[1] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <CmdResMenu title="threads" :map="threads" class="w-full flex justify-center" /> -->
|
||||
<div class="flex justify-evenly mb-4 flex-1 h-80">
|
||||
<div id="heapMemory" class="w-80 h-80 flex-1 input-btn-style mr-4"></div>
|
||||
<div id="nonheapMemory" class="w-80 h-80 flex-1 input-btn-style mr-4"></div>
|
||||
<div id="bufferPoolMemory" class="w-80 h-80 flex-1 input-btn-style"></div>
|
||||
<div class="card border mr-4 flex-1 bg-base-100">
|
||||
<div id="heapMemory" class="w-80 h-80 card-body "></div>
|
||||
</div>
|
||||
<div class="card border mr-4 flex-1 bg-base-100">
|
||||
<div id="nonheapMemory" class="w-80 h-80 card-body"></div>
|
||||
</div>
|
||||
<div class="card border flex-1 bg-base-100">
|
||||
<div id="bufferPoolMemory" class="w-80 h-80 card-body"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full flex justify-start items-start flex-1">
|
||||
<div id="gc-info" class="w-[40rem] h-80 input-btn-style p-2 mr-4"></div>
|
||||
<div class="input-btn-style flex-1 h-80 overflow-auto w-0">
|
||||
<div class="flex justify-end mb-2">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline" @click="decrease">-</button>
|
||||
<button class="btn btn-sm btn-outline border-x-0" @click="setPri">limit:{{pri}}</button>
|
||||
<button class="btn btn-sm btn-outline" @click="increase">+</button>
|
||||
<div class="card bg-base-100 border mr-4">
|
||||
<div id="gc-info" class="w-[40rem] h-80 card-body p-2 "></div>
|
||||
</div>
|
||||
<div class="card flex-1 h-80 overflow-auto w-0 border bg-base-100">
|
||||
<div class="card-body">
|
||||
<div class="flex justify-end mb-2">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline" @click="decrease">-</button>
|
||||
<button class="btn btn-sm btn-outline border-x-0" @click="setPri">limit:{{ pri }}</button>
|
||||
<button class="btn btn-sm btn-outline" @click="increase">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th v-for="(v, i) in keyList" :key="i" class="normal-case">{{ v }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(map, i) in tableResults" :key="i" class="hover">
|
||||
<th>{{ i + 1 }}</th>
|
||||
<td v-for="(key, j) in keyList" :key="j">
|
||||
{{ map.get(key) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th v-for="(v,i) in keyList" :key="i" class="normal-case">{{v}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(map, i) in tableResults" :key="i" class="hover">
|
||||
<th>{{i + 1}}</th>
|
||||
<td v-for="(key,j) in keyList" :key="j">
|
||||
{{map.get(key)}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -7,9 +7,6 @@ const routes: { cmd: string, url: string }[] = [
|
|||
}, {
|
||||
cmd: "classInfo",
|
||||
url: "classInfo"
|
||||
// }, {
|
||||
// cmd: "perfcounter",
|
||||
// url: "perfcounter"
|
||||
}, {
|
||||
cmd: "classLoader",
|
||||
url: "classLoader"
|
||||
|
|
@ -6,14 +6,11 @@ import { fetchStore } from '@/stores/fetch';
|
|||
import { useMachine, useInterpret } from '@xstate/vue';
|
||||
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
|
||||
import Enhancer from '@/components/show/Enhancer.vue';
|
||||
import {transfromStore} from "@/stores/resTransform"
|
||||
import transformStackTrace from '@/utils/transform';
|
||||
const fetchM = useInterpret(permachine)
|
||||
const pollingM = useMachine(machine)
|
||||
const fetchS = fetchStore()
|
||||
// const publicS = publicStore()
|
||||
const transS = transfromStore()
|
||||
const { getCommonResEffect } = fetchS
|
||||
// const {getCommonResEffect} = publicStore()
|
||||
|
||||
const loop = fetchS.pullResultsLoop(pollingM)
|
||||
const tableResults = reactive([] as Map<string, string>[])
|
||||
|
|
@ -26,8 +23,8 @@ const keyList = [
|
|||
"classloader",
|
||||
"threadId",
|
||||
"threadName",]
|
||||
// const enhancer = reactive(new Map())
|
||||
const enhancer = ref(undefined as EnchanceResult | undefined)
|
||||
|
||||
getCommonResEffect(pollingM, body => {
|
||||
if (body.results.length > 0) {
|
||||
body.results.forEach(result => {
|
||||
|
|
@ -40,7 +37,7 @@ getCommonResEffect(pollingM, body => {
|
|||
let val: string | string[] = ""
|
||||
if (k === "stackTrace") {
|
||||
let stackTrace = result[k]
|
||||
val = stackTrace.map((trace) => transS.transformStackTrace(trace))
|
||||
val = stackTrace.map((trace) => transformStackTrace(trace))
|
||||
} else {
|
||||
val = result[k as Exclude<keyof typeof result, "jobId" | "type" | "stackTrace">].toString()
|
||||
}
|
||||
|
|
@ -58,10 +55,8 @@ getCommonResEffect(pollingM, body => {
|
|||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
// fetchM.send("INIT")
|
||||
pollingM.send("INIT")
|
||||
fetchS.asyncInit()
|
||||
// loop.open()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
loop.close()
|
||||
|
|
@ -7,7 +7,6 @@ import CmdResMenu from '@/components/show/CmdResMenu.vue';
|
|||
import { fetchStore } from '@/stores/fetch';
|
||||
import { interpret } from 'xstate';
|
||||
import permachine from '@/machines/perRequestMachine';
|
||||
const classInfoM = useMachine(machine)
|
||||
const classMethodInfoM = useMachine(machine)
|
||||
const dumpM = useMachine(machine)
|
||||
const classDetailMap = reactive(new Map<string, string[]>())
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, onUnmounted, reactive, ref } from 'vue';
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue';
|
||||
import { computed, onBeforeMount, reactive, ref } from 'vue';
|
||||
import { publicStore } from "@/stores/public"
|
||||
import { fetchStore } from '@/stores/fetch';
|
||||
import { interpret } from 'xstate';
|
||||
import CmdResMenu from '@/components/show/CmdResMenu.vue';
|
||||
import transformMachine from '@/machines/transformConfigMachine';
|
||||
// import ClassInput from '@/components/input/ClassInput.vue';
|
||||
import permachine from '@/machines/perRequestMachine';
|
||||
import Tree from '@/components/show/Tree.vue';
|
||||
const fetchS = fetchStore()
|
||||
|
|
@ -208,131 +206,135 @@ const resetClassloader = () => {
|
|||
<template>
|
||||
<div class="flex flex-col h-full justify-between">
|
||||
<div class="flex h-[40vh]">
|
||||
<div class="input-btn-style h-full p-4 mb-2 flex flex-col transition-all duration-500" :class='{
|
||||
"w-full":loaderCache.hash === "",
|
||||
"w-2/3":loaderCache.hash !== ""
|
||||
<div class="card rounded-box border transition-all duration-500 bg-base-100 mr-2 " :class='{
|
||||
"w-full": loaderCache.hash === "",
|
||||
"w-2/3": loaderCache.hash !== ""
|
||||
}'>
|
||||
<!-- 后置为了让用户能注意到右上角的refreshicon -->
|
||||
<div class="h-[5vh] mb-4 justify-end flex">
|
||||
<button @click="resetClassloader" class="btn btn-primary btn-sm mr-1">reset</button>
|
||||
<button @click="getClassLoaderTree" class="btn btn-primary btn-sm">refresh</button>
|
||||
</div>
|
||||
<div class="card-body h-full p-4 mb-2 ">
|
||||
<!-- 后置为了让用户能注意到右上角的refreshicon -->
|
||||
<div class="h-[5vh] mb-4 justify-end flex">
|
||||
<button @click="resetClassloader" class="btn btn-primary btn-sm mr-1">reset</button>
|
||||
<button @click="getClassLoaderTree" class="btn btn-primary btn-sm">refresh</button>
|
||||
</div>
|
||||
|
||||
<div class="overflow-auto w-full flex-1">
|
||||
<div v-for="(tree,i) in classLoaderTree" :key="i">
|
||||
<Tree :root="tree">
|
||||
<template #meta="{ data, active }">
|
||||
<!-- <div class="flex items-center"> -->
|
||||
<div class="bg-info px-2 rounded-r rounded-br mr-2 text-info-content" :class='{
|
||||
"hover:opacity-50":active,
|
||||
"bg-success text-success-content":loaderCache.hash=== data[2]
|
||||
}'>
|
||||
{{data[1]}}
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</template>
|
||||
<template #others="{data}">
|
||||
<div class="items-center flex">
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus text-primary-content border border-primary-focus px-2 rounded-l ">
|
||||
count :
|
||||
</span>
|
||||
<span class="bg-base-200 border border-primary-focus rounded-r px-1 ">
|
||||
{{data[0]}}
|
||||
</span>
|
||||
<div class="overflow-auto w-full flex-1">
|
||||
<div v-for="(tree, i) in classLoaderTree" :key="i">
|
||||
<Tree :root="tree">
|
||||
<template #meta="{ data, active }">
|
||||
<!-- <div class="flex items-center"> -->
|
||||
<div class="bg-info px-2 rounded-r rounded-br mr-2 text-info-content" :class='{
|
||||
"hover:opacity-50": active,
|
||||
"bg-success text-success-content": loaderCache.hash === data[2]
|
||||
}'>
|
||||
{{ data[1] }}
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
hash :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r flex-1 px-1 border border-primary-focus">
|
||||
{{data[2]}}
|
||||
</span>
|
||||
</template>
|
||||
<template #others="{ data }">
|
||||
<div class="items-center flex">
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus text-primary-content border border-primary-focus px-2 rounded-l ">
|
||||
count :
|
||||
</span>
|
||||
<span class="bg-base-200 border border-primary-focus rounded-r px-1 ">
|
||||
{{ data[0] }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
hash :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r flex-1 px-1 border border-primary-focus">
|
||||
{{ data[2] }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- <div class="">count:{{data[0]}}</div> -->
|
||||
<button @click="selectClassLoader({ name: data[1], hash: data[2], count: data[0] })"
|
||||
class="btn btn-primary btn-xs btn-outline opacity-0 group-hover:opacity-100"
|
||||
v-if="data[2] !== 'null'">
|
||||
select classloader
|
||||
</button>
|
||||
</div>
|
||||
<!-- <div class="">count:{{data[0]}}</div> -->
|
||||
<button @click="selectClassLoader({name:data[1],hash:data[2],count:data[0]})"
|
||||
class="btn btn-primary btn-xs btn-outline opacity-0 group-hover:opacity-100"
|
||||
v-if="data[2]!== 'null'">
|
||||
select classloader
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</Tree>
|
||||
</template>
|
||||
</Tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" ml-2 overflow-y-scroll transition-all duration-500" :class='{
|
||||
"w-0":loaderCache.hash === "",
|
||||
"input-btn-style w-1/3":loaderCache.hash !==""
|
||||
<div class="card rounded-box border bg-base-100" :class='{
|
||||
"w-0 border-none": loaderCache.hash === "",
|
||||
"input-btn-style w-1/3": loaderCache.hash !== ""
|
||||
}'>
|
||||
<div class="card-body ml-2 overflow-y-scroll transition-all duration-500">
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="overflow-auto">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
selected classLoader:
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{loaderCache.name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
loadedcount :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{loaderCache.count}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
hash :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{loaderCache.hash}}
|
||||
</span>
|
||||
<div class="mb-2">
|
||||
<div class="overflow-auto">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
selected classLoader:
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{ loaderCache.name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
loadedcount :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{ loaderCache.count }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<span class="bg-primary-focus px-2 rounded-l text-primary-content border border-primary-focus">
|
||||
hash :
|
||||
</span>
|
||||
<span class="bg-base-200 rounded-r px-1 border border-primary-focus">
|
||||
{{ loaderCache.hash }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="loaderCache.hash.trim() !== ''">
|
||||
<div class="flex mb-2 w-full">
|
||||
<div class=" cursor-default
|
||||
flex-auto
|
||||
overflow-hidden rounded-lg bg-white text-left border
|
||||
focus-within:outline outline-2
|
||||
hover:shadow-md transition mr-2">
|
||||
<input class="w-full border-none py-2 pl-3 pr-10 leading-5 text-gray-900 focus-visible:outline-none"
|
||||
v-model="classVal" />
|
||||
</div>
|
||||
<button @click="loadClass" class="btn btn-primary btn-sm btn-outline">load class</button>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<div class=" cursor-default
|
||||
flex-auto
|
||||
overflow-hidden rounded-lg bg-white text-left border
|
||||
focus-within:outline outline-2
|
||||
hover:shadow-md transition mr-2">
|
||||
<input class="w-full border-none py-2 pl-3 pr-10 leading-5 text-gray-900 focus-visible:outline-none"
|
||||
v-model="resourceVal" />
|
||||
</div>
|
||||
<button @click="loadResource" class="btn btn-primary btn-sm btn-outline">load resource</button>
|
||||
</div>
|
||||
<div class="h-0 border my-2"></div>
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-xl flex-1 flex justify-center">urls</h3><button class="btn btn-primary btn-sm"
|
||||
@click="getUrlStats">refresh</button>
|
||||
</div>
|
||||
<ul class="mt-2 w-full flex flex-col">
|
||||
<li v-for="(url, i) in selectedClassLoadersUrlStats" :key="i"
|
||||
class="bg-blue-200 mb-2 p-2 break-all w-full">
|
||||
{{ url }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
<template v-if="loaderCache.hash.trim() !== ''">
|
||||
<div class="flex mb-2 w-full">
|
||||
<div class=" cursor-default
|
||||
flex-auto
|
||||
overflow-hidden rounded-lg bg-white text-left border
|
||||
focus-within:outline outline-2
|
||||
hover:shadow-md transition mr-2">
|
||||
<input class="w-full border-none py-2 pl-3 pr-10 leading-5 text-gray-900 focus-visible:outline-none"
|
||||
v-model="classVal" />
|
||||
</div>
|
||||
<button @click="loadClass" class="btn btn-primary btn-sm btn-outline">load class</button>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<div class=" cursor-default
|
||||
flex-auto
|
||||
overflow-hidden rounded-lg bg-white text-left border
|
||||
focus-within:outline outline-2
|
||||
hover:shadow-md transition mr-2">
|
||||
<input class="w-full border-none py-2 pl-3 pr-10 leading-5 text-gray-900 focus-visible:outline-none"
|
||||
v-model="resourceVal" />
|
||||
</div>
|
||||
<button @click="loadResource" class="btn btn-primary btn-sm btn-outline">load resource</button>
|
||||
</div>
|
||||
<div class="h-0 border my-2"></div>
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-xl flex-1 flex justify-center">urls</h3><button class="btn btn-primary btn-sm"
|
||||
@click="getUrlStats">refresh</button>
|
||||
</div>
|
||||
<ul class="mt-2 w-full flex flex-col">
|
||||
<li v-for="(url,i) in selectedClassLoadersUrlStats" :key="i" class="bg-blue-200 mb-2 p-2 break-all w-full">
|
||||
{{url}}</li>
|
||||
</ul>
|
||||
</template>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下面的3格 -->
|
||||
<div class="w-full flex-auto flex h-[40vh] mt-2">
|
||||
<div class="input-btn-style w-1/3 mr-2 h-full flex">
|
||||
<div class="overflow-y-scroll h-ful w-full">
|
||||
<div class="card w-1/3 mr-2 bg-base-100 border">
|
||||
<div class="card-body overflow-y-scroll">
|
||||
<div class=" mb-2 flex items-center justify-end">
|
||||
<h3 class="text-xl flex-1 flex justify-center">urlStats</h3>
|
||||
<button class="btn btn-primary btn-sm" @click="getAllUrlStats">refresh</button>
|
||||
|
|
@ -341,38 +343,40 @@ const resetClassloader = () => {
|
|||
<CmdResMenu :title="v[0]" :map="v[1]" button-width="w-full" :button-accent="v[2] === loaderCache.hash">
|
||||
</CmdResMenu>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col h-full w-2/3">
|
||||
<div class="input-btn-style w-full mr-2 h-full">
|
||||
<div class="overflow-auto flex-1 h-full">
|
||||
<div class="flex justify-end mb-2">
|
||||
<button class="btn btn-primary btn-sm" @click="getCategorizedByClassType">refresh</button>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<table class="table w-full table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class=" normal-case" v-for="(v,i) in keyList" :key="i" :class="{'group-first:z-0':i==0}">{{v}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
<tr v-for="(map, i) in tableResults" :key="i">
|
||||
<th>{{i + 1}}</th>
|
||||
<template v-for="(key, j) in keyList">
|
||||
<td class="">
|
||||
{{map.get(key)}}
|
||||
</td>
|
||||
</template>
|
||||
<div class="card h-full w-2/3 border bg-base-100">
|
||||
<div class="card-body overflow-auto">
|
||||
<div class="flex justify-around mb-2">
|
||||
<h3 class="text-xl">statistics categorized by class type</h3>
|
||||
<button class="btn btn-primary btn-sm" @click="getCategorizedByClassType">refresh</button>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<table class="table w-full table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class=" normal-case" v-for="(v, i) in keyList" :key="i" :class="{ 'group-first:z-0': i == 0 }">
|
||||
{{ v }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
<tr v-for="(map, i) in tableResults" :key="i">
|
||||
<th>{{ i + 1 }}</th>
|
||||
<template v-for="(key, j) in keyList">
|
||||
<td class="">
|
||||
{{ map.get(key) }}
|
||||
</td>
|
||||
</template>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,14 +8,12 @@ import TodoList from '@/components/input/TodoList.vue';
|
|||
import {
|
||||
Listbox, ListboxButton, ListboxOptions, ListboxOption
|
||||
} from '@headlessui/vue';
|
||||
import { transfromStore } from "@/stores/resTransform"
|
||||
import permachine from '@/machines/perRequestMachine';
|
||||
import { watch } from 'fs';
|
||||
import transformStackTrace from '@/utils/transform';
|
||||
|
||||
const fetchS = fetchStore()
|
||||
const FetchService = useInterpret(permachine)
|
||||
const stackTrace = reactive([] as string[])
|
||||
const transformS = transfromStore()
|
||||
const count = ref(0)
|
||||
const leastTime = ref(200)
|
||||
const isBlock = ref(false)
|
||||
|
|
@ -63,15 +61,10 @@ const infoCount = ref({
|
|||
TERMINATED: 0
|
||||
} as ThreadStateCount)
|
||||
const tableResults = reactive([] as Map<string, string>[])
|
||||
const limitResults = tableResults
|
||||
// watchEffect(()=>{
|
||||
// if(count.value > 0) {
|
||||
// tableResults.length = count.value
|
||||
// }
|
||||
// })
|
||||
|
||||
const tableFilter = computed(() => {
|
||||
// 原本的数组
|
||||
let res = count.value > 0 ? tableResults.filter((v, i)=>i < count.value) : tableResults
|
||||
let res = count.value > 0 ? tableResults.filter((v, i) => i < count.value) : tableResults
|
||||
if (includesVal.size === 0) return res;
|
||||
// 导入过滤条件
|
||||
includesVal.forEach((v1) => {
|
||||
|
|
@ -181,7 +174,7 @@ const setleast = publiC.inputDialogFactory(
|
|||
},
|
||||
(input) => input.value.toString(),
|
||||
)
|
||||
const {increase, decrease} = publiC.numberCondition(count,{min:0})
|
||||
const { increase, decrease } = publiC.numberCondition(count, { min: 0 })
|
||||
const getSpecialThreads = (threadid: number = -1) => {
|
||||
let threadName = threadid > 0 ? `${threadid}` : ""
|
||||
fetchS.baseSubmit(FetchService, {
|
||||
|
|
@ -191,7 +184,7 @@ const getSpecialThreads = (threadid: number = -1) => {
|
|||
const result = (res as CommonRes).body.results[0]
|
||||
if (result.type === "thread") {
|
||||
stackTrace.length = 0
|
||||
result.threadInfo.stackTrace.forEach(stack => stackTrace.unshift(transformS.transformStackTrace(stack)))
|
||||
result.threadInfo.stackTrace.forEach(stack => stackTrace.unshift(transformStackTrace(stack)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -203,10 +196,10 @@ const getSpecialThreads = (threadid: number = -1) => {
|
|||
<div class="flex justify-end items-center h-[10vh]">
|
||||
|
||||
<TodoList title="filter" :val-set="includesVal" class=" mr-2"></TodoList>
|
||||
<button class="btn ml-2 btn-sm btn-outline" @click="setleast">sample interval:{{leastTime}}</button>
|
||||
<button class="btn ml-2 btn-sm btn-outline" @click="setleast">sample interval:{{ leastTime }}</button>
|
||||
<div class="btn-group ml-2" v-show="!isBlock">
|
||||
<button class="btn btn-outline btn-sm" @click.prevent="decrease">-</button>
|
||||
<button class="btn btn-outline btn-sm border-x-0" @click.prevent="setlimit">top threads:{{count}}</button>
|
||||
<button class="btn btn-outline btn-sm border-x-0" @click.prevent="setlimit">top threads:{{ count }}</button>
|
||||
<button class="btn btn-outline btn-sm" @click.prevent="increase">+</button>
|
||||
</div>
|
||||
<Listbox v-model="threadState">
|
||||
|
|
@ -214,10 +207,10 @@ const getSpecialThreads = (threadid: number = -1) => {
|
|||
<ListboxButton class="btn w-40 btn-sm btn-outline">state {{ threadState.name }}</ListboxButton>
|
||||
<ListboxOptions
|
||||
class=" z-10 absolute w-40 mt-2 border overflow-hidden rounded-md hover:shadow-xl transition bg-base-100">
|
||||
<ListboxOption v-for="(am,i) in statelist" :key="i" :value="am" v-slot="{active, selected}">
|
||||
<ListboxOption v-for="(am, i) in statelist" :key="i" :value="am" v-slot="{ active, selected }">
|
||||
<div class=" p-2 transition " :class="{
|
||||
'bg-neutral text-neutral-content': active,
|
||||
'bg-neutral-focus text-neutral-content': selected,
|
||||
'bg-neutral text-neutral-content': active,
|
||||
'bg-neutral-focus text-neutral-content': selected,
|
||||
}">
|
||||
{{ am.name }}
|
||||
</div>
|
||||
|
|
@ -231,55 +224,58 @@ const getSpecialThreads = (threadid: number = -1) => {
|
|||
</label>
|
||||
<button class="btn btn-primary btn-sm btn-outline" @click="getThreads"> get threads </button>
|
||||
</div>
|
||||
<div class="w-full h-[50vh] input-btn-style my-2 p-4 flex flex-col">
|
||||
<div class="flex h-[8vh] flex-wrap flex-auto">
|
||||
<div v-for="(v, i) in Object.entries(infoCount)" :key="i" class="mr-2">
|
||||
<span class="text-primary-content border border-primary-focus bg-primary-focus w-44 px-2 rounded-l">
|
||||
{{ v[0] }}
|
||||
</span>
|
||||
<span class="border border-primary-focus bg-base-200 rounded-r flex-1 px-1">
|
||||
{{v[1]}}
|
||||
</span>
|
||||
<div class="w-full h-[50vh] my-2 card rounded-box compact border">
|
||||
<div class="card-body overflow-auto">
|
||||
<div class="h-[8vh] flex-wrap flex-auto flex-row flex">
|
||||
<div v-for="(v, i) in Object.entries(infoCount)" :key="i" class="mr-2">
|
||||
<span class="text-primary-content border border-primary-focus bg-primary-focus w-44 px-2 rounded-l">
|
||||
{{ v[0] }}
|
||||
</span>
|
||||
<span class="border border-primary-focus bg-base-200 rounded-r flex-1 px-1">
|
||||
{{ v[1] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto h-[40vh] w-full">
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class=""></th>
|
||||
<template v-if="count === 0">
|
||||
<th class="normal-case" v-for="(v, i) in keyList" :key="i">{{ v }}</th>
|
||||
</template>
|
||||
<template v-else>
|
||||
<th class="normal-case" v-for="(v, i) in statsList" :key="i">{{ v }}</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(map, i) in tableFilter" :key="i">
|
||||
<th class="2"><button class="btn-outline btn-primary btn btn-sm"
|
||||
@click="getSpecialThreads(parseInt(map.get('id')!))" v-if="map.get('id') !== '-1'">
|
||||
get stackTrace
|
||||
</button></th>
|
||||
<template v-if="count === 0">
|
||||
<td class="" v-for="(key, j) in keyList" :key="j">
|
||||
{{ map.get(key) }}
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td class="" v-for="(key, j) in statsList" :key="j">
|
||||
{{ map.get(key) }}
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto h-[40vh] w-full">
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class=""></th>
|
||||
<template v-if="count === 0">
|
||||
<th class="normal-case" v-for="(v,i) in keyList" :key="i">{{v}}</th>
|
||||
</template>
|
||||
<template v-else>
|
||||
<th class="normal-case" v-for="(v,i) in statsList" :key="i">{{v}}</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(map, i) in tableFilter" :key="i">
|
||||
<th class="2"><button class="btn-outline btn-primary btn btn-sm"
|
||||
@click="getSpecialThreads(parseInt(map.get('id')!))" v-if="map.get('id')!=='-1'">
|
||||
get stackTrace
|
||||
</button></th>
|
||||
<template v-if="count === 0">
|
||||
<td class="" v-for="(key,j) in keyList" :key="j">
|
||||
{{map.get(key)}}
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td class="" v-for="(key,j) in statsList" :key="j">
|
||||
{{map.get(key)}}
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-btn-style flex-auto overflow-auto">
|
||||
<h2 class="text-lg">stackTrace</h2>
|
||||
<div v-for="(stack, i) in stackTrace" class="mb-2" :key="i">{{stack}} </div>
|
||||
|
||||
<div class=" flex-auto card rounded-box compact border">
|
||||
<div class="card-body overflow-auto ">
|
||||
<h2 class="text-lg">stackTrace</h2>
|
||||
<div v-for="(stack, i) in stackTrace" class="mb-2" :key="i">{{ stack }} </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
"private": false,
|
||||
"version": "0.1.4",
|
||||
"scripts": {
|
||||
"dev": "vite --port 8000 --mode ui",
|
||||
"dev:tunnel": "vite --port 8000 --mode tunnel",
|
||||
"build": "vue-tsc --noEmit && vite build --mode ui",
|
||||
"dev:ui": "vite --port 8000 --mode ui",
|
||||
"build": "vue-tsc --noEmit && vite build --mode tunnel && vite build --mode ui",
|
||||
"build:tunnel": "vue-tsc --noEmit && vite build --mode tunnel",
|
||||
"preview": "vite preview"
|
||||
"build:ui": "vue-tsc --noEmit && vite build --mode ui",
|
||||
"preview:ui": "vite preview --mode ui",
|
||||
"preview:tunnel": "vite preview --mode tunnel"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.6.6",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./ui/index.html",
|
||||
"./index.html",
|
||||
"./src/*.{vue,js,ts,jsx,tsx}",
|
||||
"./ui/src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
"all/**/*.{vue,ts,css}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,18 @@
|
|||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*":["ui/src/*"]
|
||||
"@/*": ["all/ui/ui/src/*"],
|
||||
"~/*": ["all/share/*"]
|
||||
},
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": [
|
||||
"ui/src/**/*.ts", "ui/src/**/*.d.ts", "ui/src/**/*.tsx", "ui/src/**/*.vue","ui/tests/**/*.ts",
|
||||
"src/**/*.vue", "src/**/*.ts","src/**/*.d.ts"
|
||||
"all/**/*.vue",
|
||||
"all/**/*.ts",
|
||||
"all/**/*.d.ts",
|
||||
"all/env.d.ts"
|
||||
],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"references": [
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@
|
|||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
"include": ["./vite.config.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.button-style {
|
||||
@apply bg-blue-400 hover:opacity-50 transition rounded-md p-2
|
||||
}
|
||||
.input-btn-style {
|
||||
@apply border p-2 rounded-xl hover:shadow-md transition border-gray-300
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const transfromStore = defineStore("transformStore", {
|
||||
actions: {
|
||||
transformStackTrace(trace: StackTrace) {
|
||||
return `${trace.className}.${trace.methodName} (${trace.fileName}: ${trace.lineNumber})`;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -9,28 +9,45 @@ export default defineConfig(({ mode }) => {
|
|||
`${env.VITE_ARTHAS_PROXY_IP}:${env.VITE_ARTHAS_PROXY_PORT}`;
|
||||
|
||||
console.log("Arthas proxy :", proxyTarget);
|
||||
let outDir, input
|
||||
console.log(env.VITE_AGENT)
|
||||
if (env.VITE_AGENT === "true") {
|
||||
outDir = `./dist/tunnel`
|
||||
let outDir, input, root, proxy;
|
||||
|
||||
if (mode === "tunnel") {
|
||||
outDir = path.resolve(__dirname, `dist/tunnel`);
|
||||
root = "./all/tunnel";
|
||||
input = {
|
||||
tunnel: path.resolve(__dirname, "index.html")
|
||||
}
|
||||
} else {
|
||||
outDir = `./dist`
|
||||
tunnel: path.resolve(__dirname, "all/tunnel/index.html"),
|
||||
apps: path.resolve(__dirname, "all/tunnel/apps.html"),
|
||||
agents: path.resolve(__dirname, "all/tunnel/agents.html"),
|
||||
};
|
||||
proxy = {
|
||||
"/api": {
|
||||
target: `http://${proxyTarget}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
};
|
||||
} else if (mode === "ui") {
|
||||
outDir = path.resolve(__dirname, `dist/ui`);
|
||||
root = "./all/ui";
|
||||
input = {
|
||||
main: path.resolve(__dirname, "index.html"),
|
||||
ui: path.resolve(__dirname, "ui/index.html"),
|
||||
}
|
||||
main: path.resolve(__dirname, "all/ui/index.html"),
|
||||
ui: path.resolve(__dirname, "all/ui/ui/index.html"),
|
||||
};
|
||||
proxy = {
|
||||
"/api": {
|
||||
target: `http://${proxyTarget}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
plugins: [vue({
|
||||
reactivityTransform: true,
|
||||
reactivityTransform: path.resolve(__dirname, "all/ui"),
|
||||
})],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./ui/src/"),
|
||||
"@": path.resolve(__dirname, "all/ui/ui/src"),
|
||||
"~": path.resolve(__dirname, "all/share"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
|
|
@ -53,13 +70,10 @@ export default defineConfig(({ mode }) => {
|
|||
"__VUE_OPTIONS_API__": false,
|
||||
},
|
||||
base: "/",
|
||||
publicDir: path.resolve(__dirname, "all/share/public"),
|
||||
root,
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: `http://${proxyTarget}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
proxy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -71,12 +71,21 @@
|
|||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>vite build</id>
|
||||
<id>vite build:ui</id>
|
||||
<goals>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>vite build</arguments>
|
||||
<arguments>vite build --mode ui</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>vite build:tunnel</id>
|
||||
<goals>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>vite build --mode tunnel</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
|||
Loading…
Reference in New Issue