45 Commits

Author SHA1 Message Date
Fine0830
f9aa6600a7 fix: disable the query button without containers on the on-demand log widget. (#105) 2022-06-08 12:07:21 +08:00
Fine0830
3c37d7c197 fix: set the duration and scroll position for on-demand logs (#104) 2022-06-07 21:54:29 +08:00
Fine0830
c875c95c20 fix: activate the correct tab index after renaming a Tabs name (#103) 2022-06-06 16:36:04 +08:00
Fine0830
30069ce825 feat: visualize a on-demand log widget (#99) 2022-06-02 20:09:38 +08:00
dependabot[bot]
0a746a125b build(deps): bump eventsource from 1.1.0 to 1.1.1 (#102) 2022-06-02 10:00:40 +08:00
Fine0830
4d26728eb5 feat: add Percentage Of Root and Percentage Of Selected in the eBPF widget (#101) 2022-05-30 18:18:37 +08:00
Fine0830
74cb089271 feat: sort spans with startTime or spanId in a segment (#100) 2022-05-26 13:04:43 +08:00
Fine0830
b34c0b0c72 feat: add a Spanish option in the lang selector (#98) 2022-05-24 11:53:41 +08:00
Fine0830
45f896bf36 feat: visualize the OFF CPU eBPF profiling (#97) 2022-05-23 16:00:30 +08:00
Marc Navarro
f334985cf0 Add Spanish Translation (#96) 2022-05-22 15:07:51 +08:00
Fine0830
f2e75f2b9f fix: update query conditions for browser logs (#95) 2022-05-20 10:51:19 +08:00
Fine0830
b544decbbb feat: remove needTotal and total fields from query conditions (#94) 2022-05-19 19:29:02 +08:00
Fine0830
b953904c71 feat: remove the total filed from query conditions (#93) 2022-05-19 15:49:24 +08:00
Fine0830
7e0d716111 refactor: remove a timeline graph from the eBPF profiling widget (#91) 2022-05-18 20:47:18 +08:00
Fine0830
21523b8cb5 fix: polish the eBPF profiling widget (#90) 2022-05-18 17:40:40 +08:00
Fine0830
8c7fee4d86 feat: remove unexpected data for exporting dashboards (#89) 2022-05-16 21:38:39 +08:00
Fine0830
7f474984c4 fix: set duration time (#88) 2022-05-15 15:14:02 +08:00
Fine0830
918791f7ed fix: the page doesn't need to be re-rendered when the url changes (#87) 2022-05-13 14:40:18 +08:00
Fine0830
784c2e97b8 feat: support multiple trees for the flame graph (#86) 2022-05-12 20:57:37 +08:00
Fine0830
b492787027 feat: Implement tags auto-complete for Trace and Log (#85) 2022-05-12 17:00:07 +08:00
Fine0830
0e0f2704b3 fix graphql query (#84) 2022-05-11 20:02:45 +08:00
Fine0830
ca14a7d2c2 fix: set date to 0 and remove duration filed (#83) 2022-05-11 15:05:58 +08:00
Fine0830
49c4c96a6a remove process layer (#82) 2022-05-10 17:25:56 +08:00
Fine0830
a0b57d0a5a feat: remove the layer filed for instance query (#81) 2022-05-10 17:03:47 +08:00
Fine0830
024a0fe44c fix: set graph options(#80) 2022-05-10 11:07:36 +08:00
Fine0830
918b0551ad fix: set dropdown for the Tab widget, init instance relation selectors, update sankey graph (#79) 2022-05-09 20:18:20 +08:00
Fine0830
d93a7cead2 feat: mobile terminal adaptation (#78) 2022-05-09 14:54:08 +08:00
Fine0830
2a2500a28d fix: verify query params to avoid invalid queries (#77) 2022-05-05 19:12:32 +08:00
Fine0830
7d1bb43adb fix: view spans details and task logs (#76) 2022-05-04 14:28:27 +08:00
Fine0830
4c1884d552 fix the tag tips (#75) 2022-04-29 15:26:58 +08:00
Fine0830
f40e9633df avoid invalid querys (#74) 2022-04-27 21:38:31 +08:00
Fine0830
02f5c4b976 fix: optimize the trace widget (#73) 2022-04-26 19:32:23 +08:00
Fine0830
8a07b1d804 feat: Implement the eBPF profile widget on dashboard (#72) 2022-04-24 20:24:23 +08:00
Fine0830
393885324b fix utc (#71) 2022-04-21 12:39:06 +08:00
Rancho Lee
6bfb7915bb -- fix: viewLogs button query no data (#70) 2022-04-20 09:00:23 +08:00
Brandon Fergerson
be60d5c770 Update en.ts (#69) 2022-04-17 17:06:08 +08:00
Fine0830
3c68a4a327 build: remove compression plugin and config (#68) 2022-04-15 14:39:15 +08:00
Fine0830
f22208395a fix metrics index (#67) 2022-04-15 11:25:33 +08:00
Fine0830
26db1ec23e feat: add Avg calculations (#66) 2022-04-14 18:15:28 +08:00
Fine0830
69a9c6de13 feat: add an avg calculation for metrics of list graphs (#65) 2022-04-14 14:38:32 +08:00
Fine0830
2dd9df19d7 fix: refresh dashboards with current selectors (#64) 2022-04-12 16:29:33 +08:00
dependabot[bot]
5dfbbacd14 build(deps): bump follow-redirects from 1.14.7 to 1.14.9 (#62) 2022-04-12 09:49:49 +08:00
dependabot[bot]
deb59705ae build(deps): bump url-parse from 1.5.4 to 1.5.10 (#61) 2022-04-11 22:49:13 +08:00
Fine0830
3b4c3cc4ea build: split chunks, compress files, and auto import components on demand (#60) 2022-04-11 22:36:16 +08:00
hutaishi
1d83e14f22 Add Apache ShenYu (incubating) component LOGO (#59) 2022-04-10 21:12:47 +08:00
120 changed files with 4928 additions and 1154 deletions

View File

@@ -28,6 +28,8 @@ header:
- '.gitignore'
- '.prettierrc'
- '.browserslistrc'
- 'src/types/auto-imports.d.ts'
- 'src/types/components.d.ts'
- '**/*.md'
- '**/*.json'

21
.prettierignore Normal file
View File

@@ -0,0 +1,21 @@
#
# 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
#
# http://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.
#
/src/types/auto-imports.d.ts
/src/types/components.d.ts

585
package-lock.json generated
View File

@@ -1,19 +1,21 @@
{
"name": "skywalking-booster-ui",
"version": "0.1.0",
"version": "9.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "skywalking-booster-ui",
"version": "0.1.0",
"version": "9.1.0",
"dependencies": {
"axios": "^0.24.0",
"d3": "^7.3.0",
"d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1",
"echarts": "^5.2.2",
"element-plus": "^2.0.2",
"lodash": "^4.17.21",
"monaco-editor": "^0.27.0",
"pinia": "^2.0.5",
"vue": "^3.0.0",
"vue-grid-layout": "^3.0.0-beta1",
@@ -49,6 +51,7 @@
"eslint-plugin-vue": "^7.0.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.3",
"monaco-editor-webpack-plugin": "^4.1.2",
"node-sass": "^6.0.1",
"postcss-html": "^1.3.0",
"postcss-scss": "^4.0.2",
@@ -61,9 +64,20 @@
"stylelint-order": "^5.0.0",
"svg-sprite-loader": "^6.0.11",
"typescript": "~4.4.4",
"unplugin-auto-import": "^0.7.0",
"unplugin-vue-components": "^0.19.2",
"vue-jest": "^5.0.0-0"
}
},
"node_modules/@antfu/utils": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.5.1.tgz",
"integrity": "sha512-8Afo0+xvYe1K8Wm4xHTymfTkpzy36aaqDvhXIayUwl+mecMG9Xzl3XjXa6swG6Bk8FBeQ646RyvmsYt6+2Be9g==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@babel/code-frame": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
@@ -2798,6 +2812,19 @@
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/pluginutils": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
"integrity": "sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==",
"dev": true,
"dependencies": {
"estree-walker": "^2.0.1",
"picomatch": "^2.2.2"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/@soda/friendly-errors-webpack-plugin": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
@@ -7286,10 +7313,16 @@
"dev": true
},
"node_modules/chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -9074,6 +9107,16 @@
"node": ">=0.8"
}
},
"node_modules/cypress/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/d3": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz",
@@ -9276,6 +9319,21 @@
"node": ">=12"
}
},
"node_modules/d3-flame-graph": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/d3-flame-graph/-/d3-flame-graph-4.1.3.tgz",
"integrity": "sha512-NijuhJZhaTMwobVgwGQ67x9PovqMMHXBbs0FMHEGJvsWZGuL4M7OsB03v8mHdyVyHhnQYGsYnb5w021e9+R+RQ==",
"dependencies": {
"d3-array": "^3.1.1",
"d3-dispatch": "^3.0.1",
"d3-ease": "^3.0.1",
"d3-format": "^3.0.1",
"d3-hierarchy": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-transition": "^3.0.1"
}
},
"node_modules/d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@@ -9545,9 +9603,9 @@
}
},
"node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
@@ -11024,9 +11082,9 @@
}
},
"node_modules/eventsource": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz",
"integrity": "sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==",
"dev": true,
"dependencies": {
"original": "^1.0.0"
@@ -11509,9 +11567,9 @@
"dev": true
},
"node_modules/fast-glob": {
"version": "3.2.10",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz",
"integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==",
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@@ -11804,9 +11862,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [
{
"type": "individual",
@@ -17450,6 +17508,18 @@
"json5": "lib/cli.js"
}
},
"node_modules/local-pkg": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.1.tgz",
"integrity": "sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -18166,6 +18236,38 @@
"node": "*"
}
},
"node_modules/monaco-editor": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.27.0.tgz",
"integrity": "sha512-UhwP78Wb8w0ZSYoKXQNTV/0CHObp6NS3nCt51QfKE6sKyBo5PBsvuDOHoI2ooBakc6uIwByRLHVeT7+yXQe2fQ=="
},
"node_modules/monaco-editor-webpack-plugin": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.1.2.tgz",
"integrity": "sha512-snmHecygICKT0UlHhva+Cs2WaLPpxy3111xbvInhjjTr5m0xQTFHlmJ2QQDcB14Vzmm7f07uc1TtbvOpmL50BA==",
"dev": true,
"dependencies": {
"loader-utils": "^2.0.0"
},
"peerDependencies": {
"monaco-editor": "0.25.x || 0.26.x || 0.27.x",
"webpack": "^4.5.0 || 5.x"
}
},
"node_modules/monaco-editor-webpack-plugin/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -22369,6 +22471,16 @@
"request": "^2.34"
}
},
"node_modules/request/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -22400,12 +22512,12 @@
"dev": true
},
"node_modules/resolve": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
"integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.8.0",
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -23704,15 +23816,6 @@
"ms": "^2.1.1"
}
},
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/sort-keys": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
@@ -26397,6 +26500,156 @@
"node": ">= 0.8"
}
},
"node_modules/unplugin": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-0.6.1.tgz",
"integrity": "sha512-cQqRCgQ2v/Q4fPIWNVZ6sNIDdl5v8JXOnlsUOsGzT4fblTONoPWaytiYSpu5qJ9lvSDZYAQN6BRVo3XQoZMfUQ==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.3",
"webpack-sources": "^3.2.3",
"webpack-virtual-modules": "^0.4.3"
},
"peerDependencies": {
"esbuild": ">=0.13",
"rollup": "^2.50.0",
"vite": "^2.3.0",
"webpack": "4 || 5"
},
"peerDependenciesMeta": {
"esbuild": {
"optional": true
},
"rollup": {
"optional": true
},
"vite": {
"optional": true
},
"webpack": {
"optional": true
}
}
},
"node_modules/unplugin-auto-import": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-0.7.0.tgz",
"integrity": "sha512-40oC7bxzSyyvJI4mXmk8gDD7gKjR10gYkeWBu/UigK8TusXwUsJOjwvIr+REgzOMBGH/5pUiVMoBYEZrLejk3w==",
"dev": true,
"dependencies": {
"@antfu/utils": "^0.5.0",
"@rollup/pluginutils": "^4.2.0",
"local-pkg": "^0.4.1",
"magic-string": "^0.26.1",
"resolve": "^1.22.0",
"unplugin": "^0.6.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vueuse/core": "*"
},
"peerDependenciesMeta": {
"@vueuse/core": {
"optional": true
}
}
},
"node_modules/unplugin-auto-import/node_modules/magic-string": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
"integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
"dev": true,
"dependencies": {
"sourcemap-codec": "^1.4.8"
},
"engines": {
"node": ">=12"
}
},
"node_modules/unplugin-vue-components": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.19.2.tgz",
"integrity": "sha512-7DhQfTyHLyVIWR6VBQONLU6dDBOXtEYvZQYUpN9C+t11WOb5baIFoxfzDxkeFHTHGMhznyEOw6afHyV9JKWnig==",
"dev": true,
"dependencies": {
"@antfu/utils": "^0.5.0",
"@rollup/pluginutils": "^4.2.0",
"chokidar": "^3.5.3",
"debug": "^4.3.4",
"fast-glob": "^3.2.11",
"local-pkg": "^0.4.1",
"magic-string": "^0.26.1",
"minimatch": "^5.0.1",
"resolve": "^1.22.0",
"unplugin": "^0.6.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@babel/parser": "^7.15.8",
"@babel/traverse": "^7.15.4",
"vue": "2 || 3"
},
"peerDependenciesMeta": {
"@babel/parser": {
"optional": true
},
"@babel/traverse": {
"optional": true
}
}
},
"node_modules/unplugin-vue-components/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/unplugin-vue-components/node_modules/magic-string": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
"integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
"dev": true,
"dependencies": {
"sourcemap-codec": "^1.4.8"
},
"engines": {
"node": ">=12"
}
},
"node_modules/unplugin-vue-components/node_modules/minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/unplugin/node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true,
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/unquote": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
@@ -26535,9 +26788,9 @@
}
},
"node_modules/url-parse": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"dependencies": {
"querystringify": "^2.1.1",
@@ -26615,13 +26868,12 @@
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "bin/uuid"
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache": {
@@ -28074,6 +28326,16 @@
"node": ">= 6"
}
},
"node_modules/webpack-log/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/webpack-merge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
@@ -28102,6 +28364,12 @@
"node": ">=0.10.0"
}
},
"node_modules/webpack-virtual-modules": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz",
"integrity": "sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==",
"dev": true
},
"node_modules/webpack/node_modules/acorn": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
@@ -28746,6 +29014,12 @@
}
},
"dependencies": {
"@antfu/utils": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.5.1.tgz",
"integrity": "sha512-8Afo0+xvYe1K8Wm4xHTymfTkpzy36aaqDvhXIayUwl+mecMG9Xzl3XjXa6swG6Bk8FBeQ646RyvmsYt6+2Be9g==",
"dev": true
},
"@babel/code-frame": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
@@ -30755,6 +31029,16 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
},
"@rollup/pluginutils": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
"integrity": "sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==",
"dev": true,
"requires": {
"estree-walker": "^2.0.1",
"picomatch": "^2.2.2"
}
},
"@soda/friendly-errors-webpack-plugin": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
@@ -34463,9 +34747,9 @@
"dev": true
},
"chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"requires": {
"anymatch": "~3.1.2",
@@ -35884,6 +36168,12 @@
"psl": "^1.1.24",
"punycode": "^1.4.1"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
@@ -36035,6 +36325,21 @@
"d3-dsv": "1 - 3"
}
},
"d3-flame-graph": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/d3-flame-graph/-/d3-flame-graph-4.1.3.tgz",
"integrity": "sha512-NijuhJZhaTMwobVgwGQ67x9PovqMMHXBbs0FMHEGJvsWZGuL4M7OsB03v8mHdyVyHhnQYGsYnb5w021e9+R+RQ==",
"requires": {
"d3-array": "^3.1.1",
"d3-dispatch": "^3.0.1",
"d3-ease": "^3.0.1",
"d3-format": "^3.0.1",
"d3-hierarchy": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-transition": "^3.0.1"
}
},
"d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@@ -36241,9 +36546,9 @@
}
},
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
@@ -37385,9 +37690,9 @@
"dev": true
},
"eventsource": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz",
"integrity": "sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==",
"dev": true,
"requires": {
"original": "^1.0.0"
@@ -37787,9 +38092,9 @@
"dev": true
},
"fast-glob": {
"version": "3.2.10",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz",
"integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==",
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
@@ -38036,9 +38341,9 @@
}
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
},
"for-in": {
"version": "1.0.2",
@@ -42411,6 +42716,12 @@
}
}
},
"local-pkg": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.1.tgz",
"integrity": "sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==",
"dev": true
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -42994,6 +43305,33 @@
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
"dev": true
},
"monaco-editor": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.27.0.tgz",
"integrity": "sha512-UhwP78Wb8w0ZSYoKXQNTV/0CHObp6NS3nCt51QfKE6sKyBo5PBsvuDOHoI2ooBakc6uIwByRLHVeT7+yXQe2fQ=="
},
"monaco-editor-webpack-plugin": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.1.2.tgz",
"integrity": "sha512-snmHecygICKT0UlHhva+Cs2WaLPpxy3111xbvInhjjTr5m0xQTFHlmJ2QQDcB14Vzmm7f07uc1TtbvOpmL50BA==",
"dev": true,
"requires": {
"loader-utils": "^2.0.0"
},
"dependencies": {
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
}
}
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -46215,6 +46553,14 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"request-progress": {
@@ -46271,12 +46617,12 @@
"dev": true
},
"resolve": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
"integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"dev": true,
"requires": {
"is-core-module": "^2.8.0",
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
@@ -47311,14 +47657,6 @@
"faye-websocket": "^0.11.3",
"uuid": "^8.3.2",
"websocket-driver": "^0.7.4"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
}
}
},
"sockjs-client": {
@@ -49480,6 +49818,97 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
"unplugin": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-0.6.1.tgz",
"integrity": "sha512-cQqRCgQ2v/Q4fPIWNVZ6sNIDdl5v8JXOnlsUOsGzT4fblTONoPWaytiYSpu5qJ9lvSDZYAQN6BRVo3XQoZMfUQ==",
"dev": true,
"requires": {
"chokidar": "^3.5.3",
"webpack-sources": "^3.2.3",
"webpack-virtual-modules": "^0.4.3"
},
"dependencies": {
"webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true
}
}
},
"unplugin-auto-import": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-0.7.0.tgz",
"integrity": "sha512-40oC7bxzSyyvJI4mXmk8gDD7gKjR10gYkeWBu/UigK8TusXwUsJOjwvIr+REgzOMBGH/5pUiVMoBYEZrLejk3w==",
"dev": true,
"requires": {
"@antfu/utils": "^0.5.0",
"@rollup/pluginutils": "^4.2.0",
"local-pkg": "^0.4.1",
"magic-string": "^0.26.1",
"resolve": "^1.22.0",
"unplugin": "^0.6.1"
},
"dependencies": {
"magic-string": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
"integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
"dev": true,
"requires": {
"sourcemap-codec": "^1.4.8"
}
}
}
},
"unplugin-vue-components": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.19.2.tgz",
"integrity": "sha512-7DhQfTyHLyVIWR6VBQONLU6dDBOXtEYvZQYUpN9C+t11WOb5baIFoxfzDxkeFHTHGMhznyEOw6afHyV9JKWnig==",
"dev": true,
"requires": {
"@antfu/utils": "^0.5.0",
"@rollup/pluginutils": "^4.2.0",
"chokidar": "^3.5.3",
"debug": "^4.3.4",
"fast-glob": "^3.2.11",
"local-pkg": "^0.4.1",
"magic-string": "^0.26.1",
"minimatch": "^5.0.1",
"resolve": "^1.22.0",
"unplugin": "^0.6.1"
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
},
"magic-string": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
"integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
"dev": true,
"requires": {
"sourcemap-codec": "^1.4.8"
}
},
"minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
}
}
}
},
"unquote": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
@@ -49595,9 +50024,9 @@
}
},
"url-parse": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
@@ -49665,9 +50094,9 @@
"dev": true
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
},
"v8-compile-cache": {
@@ -50996,6 +51425,14 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"webpack-merge": {
@@ -51025,6 +51462,12 @@
}
}
},
"webpack-virtual-modules": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz",
"integrity": "sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==",
"dev": true
},
"websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "skywalking-booster-ui",
"version": "0.1.0",
"version": "9.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@@ -12,10 +12,12 @@
"dependencies": {
"axios": "^0.24.0",
"d3": "^7.3.0",
"d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1",
"echarts": "^5.2.2",
"element-plus": "^2.0.2",
"lodash": "^4.17.21",
"monaco-editor": "^0.27.0",
"pinia": "^2.0.5",
"vue": "^3.0.0",
"vue-grid-layout": "^3.0.0-beta1",
@@ -51,6 +53,7 @@
"eslint-plugin-vue": "^7.0.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.3",
"monaco-editor-webpack-plugin": "^4.1.2",
"node-sass": "^6.0.1",
"postcss-html": "^1.3.0",
"postcss-scss": "^4.0.2",
@@ -63,6 +66,8 @@
"stylelint-order": "^5.0.0",
"svg-sprite-loader": "^6.0.11",
"typescript": "~4.4.4",
"unplugin-auto-import": "^0.7.0",
"unplugin-vue-components": "^0.19.2",
"vue-jest": "^5.0.0-0"
},
"eslintConfig": {
@@ -103,6 +108,9 @@
}
]
},
"eslintIgnore": [
"vue.config.js"
],
"browserslist": [
"> 1%",
"last 2 versions",
@@ -133,7 +141,7 @@
"package.json": [
"prettier --write"
],
"*.{scss,less,styl,html}": [
"*.{scss,less,styl}": [
"stylelint --fix",
"prettier --write"
],

View File

@@ -13,7 +13,7 @@ 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. -->
<!DOCTYPE html>
<html lang="">
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">

View File

@@ -13,11 +13,13 @@ 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. -->
<template>
<router-view :key="$route.fullPath" />
<router-view />
</template>
<style>
#app {
color: #2c3e50;
height: 100%;
overflow: auto;
min-width: 1024px;
}
</style>

16
src/assets/icons/arrow-down.svg Executable file
View File

@@ -0,0 +1,16 @@
<!-- 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
http://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. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path id="a" d="m10.472352 7.28232367c.3431062-.36783247.904419-.36783247 1.2592596-.00644059.3578153.36442148.3578153.95850784.0002156 1.28561559l-3.10532264 3.16826253c-.17025689.1734002-.39845625.2702388-.62654793.2702388-.24380864 0-.45151514-.0919745-.62697852-.2706782l-3.09835734-3.16693764c-.36405333-.352236-.36405333-.94614513-.01248284-1.28566765.34310619-.36783247.90441901-.36783247 1.25901327-.0066912l2.48658215 2.52737493z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,16 @@
<!-- 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
http://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. -->
<svg t="1654161407133" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1721" width="16" height="16"><path d="M804.224 86.144c0-19.264-15.616-34.944-34.944-34.944l-734.336 0c-19.264 0-34.944 15.68-34.944 34.944l0 82.048 804.224 0 0-82.048zM738.88 602.432c0 47.072-38.176 85.248-85.248 85.248s-85.248-38.176-85.248-85.248c0-47.072 38.176-85.248 85.248-85.248s85.248 38.176 85.248 85.248zM804.992 264.64l0-62.976-804.224 0 0 665.408c0 18.56 14.656 33.472 32.96 34.56l402.24 0c61.12 44.544 136.192 71.168 217.664 71.168 204.544 0 370.368-165.824 370.368-370.368 0-150.592-89.984-279.936-219.008-337.792zM412.096 298.24l30.528 0c-10.624 7.36-20.8 15.36-30.528 23.744l0-23.744zM63.04 298.24l153.024 0 0 153.024-153.024 0 0-153.024zM216.064 805.056l-153.024 0 0-153.024 153.024 0 0 153.024zM219.136 631.232l-153.024 0 0-153.024 153.024 0 0 153.024zM237.568 805.056l0-153.024 49.408 0c7.488 55.936 27.264 107.904 56.832 153.024l-106.24 0zM284.672 631.232l-44.032 0 0-153.024 64.384 0c-13.824 38.848-21.824 80.576-21.824 124.224 0 9.728 0.768 19.264 1.472 28.8zM390.592 341.76c-31.168 31.424-56.512 68.544-74.88 109.44l-78.144 0 0-152.96 153.024 0 0 43.52zM899.136 638.4l-63.36 12.864c-4.288 16.064-10.688 31.296-18.816 45.376l35.712 53.888-50.944 50.944-53.888-35.712c-14.08 8.128-29.312 14.528-45.376 18.816l-12.864 63.36-72 0-12.864-63.36c-16.064-4.288-31.296-10.688-45.376-18.816l-53.888 35.712-50.944-50.944 35.712-53.888c-8.128-14.08-14.528-29.312-18.816-45.376l-63.36-12.864 0-72 63.36-12.864c4.352-16.064 10.688-31.296 18.816-45.312l-35.712-53.952 50.944-50.944 53.888 35.776c14.08-8.128 29.312-14.464 45.376-18.816l12.864-63.36 72 0 12.864 63.36c16.064 4.288 31.296 10.688 45.376 18.816l53.888-35.712 50.944 50.944-35.712 53.824c8.128 14.08 14.528 29.312 18.816 45.376l63.36 12.864 0 72z" p-id="1722" fill="#707070"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,17 @@
<!-- 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
http://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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M17.016 17.016v-4.031h-2.016v4.031h2.016zM12.984 17.016v-10.031h-1.969v10.031h1.969zM9 17.016v-7.031h-2.016v7.031h2.016zM18.984 3q0.797 0 1.406 0.609t0.609 1.406v13.969q0 0.797-0.609 1.406t-1.406 0.609h-13.969q-0.797 0-1.406-0.609t-0.609-1.406v-13.969q0-0.797 0.609-1.406t1.406-0.609h13.969z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -13,6 +13,5 @@ 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>timeline</title>
<path d="M23.016 8.016q0 0.797-0.609 1.383t-1.406 0.586h-0.047q-0.328 0-0.469-0.047l-3.563 3.563q0.094 0.281 0.094 0.516 0 0.797-0.609 1.383t-1.406 0.586-1.406-0.586-0.609-1.383q0-0.234 0.094-0.516l-2.578-2.578q-0.281 0.094-0.516 0.094t-0.516-0.094l-4.547 4.547q0.094 0.281 0.094 0.516 0 0.797-0.609 1.406t-1.406 0.609-1.406-0.609-0.609-1.406 0.609-1.383 1.406-0.586q0.375 0 0.516 0.047l4.547-4.547q-0.047-0.141-0.047-0.516 0-0.797 0.586-1.406t1.383-0.609 1.406 0.609 0.609 1.406q0 0.375-0.047 0.516l2.531 2.531q0.141-0.047 0.516-0.047t0.516 0.047l3.563-3.516q-0.094-0.281-0.094-0.516 0-0.797 0.609-1.406t1.406-0.609 1.406 0.609 0.609 1.406z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

15
src/assets/icons/view.svg Normal file
View File

@@ -0,0 +1,15 @@
<!-- 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
http://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. -->
<svg t="1650287922642" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3055" width="48" height="48"><path d="M277.333333 325.333333m5.333334 0l325.333333 0q5.333333 0 5.333333 5.333334l0 64q0 5.333333-5.333333 5.333333l-325.333333 0q-5.333333 0-5.333334-5.333333l0-64q0-5.333333 5.333334-5.333334Z" p-id="3056" fill="#707070"></path><path d="M277.333333 474.666667m5.333334 0l325.333333 0q5.333333 0 5.333333 5.333333l0 64q0 5.333333-5.333333 5.333333l-325.333333 0q-5.333333 0-5.333334-5.333333l0-64q0-5.333333 5.333334-5.333333Z" p-id="3057" fill="#707070"></path><path d="M277.333333 624m5.333334 0l247.36 0q5.333333 0 5.333333 5.333333l0 64q0 5.333333-5.333333 5.333334l-247.36 0q-5.333333 0-5.333334-5.333334l0-64q0-5.333333 5.333334-5.333333Z" p-id="3058" fill="#707070"></path><path d="M565.333333 842.666667H186.666667a5.333333 5.333333 0 0 1-5.333334-5.333334V186.666667a5.333333 5.333333 0 0 1 5.333334-5.333334h522.666666v346.24a5.333333 5.333333 0 0 0 5.333334 5.333334h64a5.333333 5.333333 0 0 0 5.333333-5.333334V106.666667H112a5.333333 5.333333 0 0 0-5.333333 5.333333v800a5.333333 5.333333 0 0 0 5.333333 5.333333h453.333333a5.333333 5.333333 0 0 0 5.333334-5.333333v-64a5.333333 5.333333 0 0 0-5.333334-5.333333z" p-id="3059" fill="#707070"></path><path d="M868.426667 723.786667a144.64 144.64 0 1 0-144.64 144.64 144.64 144.64 0 0 0 144.64-144.64z m-144.64 69.973333a69.973333 69.973333 0 1 1 69.973333-69.973333 70.026667 70.026667 0 0 1-69.973333 69.973333z" p-id="3060" fill="#707070"></path><path d="M811.758007 864.533065m3.771237-3.771236l45.254834-45.254834q3.771236-3.771236 7.542472 0l45.254834 45.254834q3.771236 3.771236 0 7.542472l-45.254834 45.254834q-3.771236 3.771236-7.542472 0l-45.254834-45.254834q-3.771236-3.771236 0-7.542472Z" p-id="3061" fill="#707070"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -247,7 +247,7 @@ limitations under the License. -->
(state.showMinutes = state.showSeconds = false)
"
:class="{ on: state.showHours }"
>{{ state.hour || dd }}</a
>{{ dd(state.hour) }}</a
>
<span>:</span>
<a
@@ -257,7 +257,7 @@ limitations under the License. -->
(state.showHours = state.showSeconds = false)
"
:class="{ on: state.showMinutes }"
>{{ state.minute || dd }}</a
>{{ dd(state.minute) }}</a
>
<span v-show="state.m !== 'D'">
<span>:</span>
@@ -268,7 +268,7 @@ limitations under the License. -->
(state.showHours = state.showMinutes = false)
"
:class="{ on: state.showSeconds }"
>{{ state.second || dd }}</a
>{{ dd(state.second) }}</a
>
</span>
</div>
@@ -464,7 +464,6 @@ const status = (
const minutes = time.getMinutes();
const seconds = time.getSeconds();
const milliseconds = time.getMilliseconds();
const dd = (t: number) => `0${t}`.slice(-2);
const map: { [key: string]: string | number } = {
YYYY: year,
MM: dd(month + 1),

View File

@@ -53,9 +53,6 @@ const available = computed(
(Array.isArray(props.option.series.data) && props.option.series.data[0])
);
onMounted(async () => {
if (!available.value) {
return;
}
await setOptions(props.option);
chartRef.value && addResizeListener(unref(chartRef), resize);
setTimeout(() => {

View File

@@ -32,7 +32,7 @@ interface Option {
const emit = defineEmits(["change"]);
const props = defineProps({
options: {
type: Array as PropType<(Option & { disabled: boolean })[]>,
type: Array as PropType<Option[]>,
default: () => [],
},
value: {
@@ -44,7 +44,7 @@ const props = defineProps({
const selected = ref<string>(props.value);
function checked(opt: string) {
function checked(opt: unknown) {
emit("change", opt);
}
</script>

View File

@@ -45,7 +45,7 @@ import { Option } from "@/types/app";
const emit = defineEmits(["change"]);
const props = defineProps({
options: {
type: Array as PropType<(Option & { disabled: boolean })[]>,
type: Array as PropType<Option[]>,
default: () => [],
},
value: {

View File

@@ -58,7 +58,7 @@ const props = defineProps({
},
size: { type: null, default: "default" },
placeholder: {
type: [String, Number] as PropType<string | number>,
type: [String, undefined] as PropType<string>,
default: "Select a option",
},
borderRadius: { type: Number, default: 3 },

View File

@@ -14,13 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { App } from "vue";
import Icon from "./Icon.vue";
import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue";
import Graph from "./Graph.vue";
import Radio from "./Radio.vue";
import SelectSingle from "./SelectSingle.vue";
import type { App } from "vue";
import VueGridLayout from "vue-grid-layout";
const components: { [key: string]: any } = {

View File

@@ -23,6 +23,7 @@ export enum TimeType {
export const Languages = [
{ label: "English", value: "en" },
{ label: "Chinese", value: "zh" },
{ label: "Spanish", value: "es" },
];
export const RoutesMap: { [key: string]: string } = {

View File

@@ -45,6 +45,5 @@ export const Alarm = {
endTime
}
}
total
}`,
};

View File

@@ -0,0 +1,36 @@
/**
* 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
*
* http://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.
*/
export const queryContainers = {
variable: "$condition: OndemandContainergQueryCondition!",
query: `
containers: listContainers(condition: $condition) {
errorReason
containers
}`,
};
export const queryStreamingLogs = {
variable: "$condition: OndemandLogQueryCondition",
query: `
logs: ondemandPodLogs(condition: $condition) {
errorReason
logs {
content
}
}`,
};

View File

@@ -0,0 +1,92 @@
/**
* 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
*
* http://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.
*/
export const queryCreateTaskData = {
variable: "$serviceId: ID!",
query: `
createTaskData: queryPrepareCreateEBPFProfilingTaskData(serviceId: $serviceId) {
couldProfiling
processLabels
}`,
};
export const createEBPFTask = {
variable: "$request: EBPFProfilingTaskFixedTimeCreationRequest!",
query: `
createTaskData: createEBPFProfilingFixedTimeTask(request: $request) {
status
errorReason
id
}`,
};
export const queryEBPFTasks = {
variable: "$serviceId: ID!",
query: `
queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId) {
taskId
serviceName
serviceId
processLabels
taskStartTime
triggerType
fixedTriggerDuration
targetType
createTime
}`,
};
export const queryEBPFSchedules = {
variable: "$taskId: ID!",
query: `
eBPFSchedules: queryEBPFProfilingSchedules(taskId: $taskId) {
scheduleId
taskId
process {
id
name
serviceId
serviceName
instanceId
instanceName
agentId
detectType
attributes {
name
value
}
labels
}
startTime
endTime
}`,
};
export const analysisEBPFResult = {
variable:
"$scheduleIdList: [ID!]!, $timeRanges: [EBPFProfilingAnalyzeTimeRange!]!, $aggregateType: EBPFProfilingAnalyzeAggregateType",
query: `
analysisEBPFResult: analysisEBPFProfilingResult(scheduleIdList: $scheduleIdList, timeRanges: $timeRanges, aggregateType: $aggregateType) {
tip
trees {
elements {
id
parentId
symbol
stackType
dumpCount
}
}
}`,
};

View File

@@ -35,6 +35,5 @@ export const FetchEvents = {
startTime
endTime
}
total
}`,
};

View File

@@ -30,7 +30,6 @@ export const QueryBrowserErrorLogs = {
stack
grade
}
total
}`,
};
@@ -54,7 +53,6 @@ export const QueryServiceLogs = {
value
}
}
total
}`,
};
@@ -63,3 +61,15 @@ export const QueryLogsByKeywords = {
query: `
support: supportQueryLogsByKeywords`,
};
export const LogTagKeys = {
variable: "$duration: Duration!",
query: `
tagKeys: queryLogTagAutocompleteKeys(duration: $duration)`,
};
export const LogTagValues = {
variable: "$tagKey: String!, $duration: Duration!",
query: `
tagValues: queryLogTagAutocompleteValues(tagKey: $tagKey, duration: $duration)`,
};

View File

@@ -20,10 +20,10 @@ export const Services = {
services: listServices(layer: $layer) {
id
value: name
label: name
group
layers
normal
label: name
group
layers
normal
}
`,
};
@@ -36,17 +36,16 @@ export const Instances = {
variable: "$serviceId: ID!, $duration: Duration!",
query: `
pods: listInstances(duration: $duration, serviceId: $serviceId) {
id
value: name
label: name
language
instanceUUID
layer
attributes {
name
value
}
id
value: name
label: name
language
instanceUUID
attributes {
name
value
}
}
`,
};
export const Endpoints = {
@@ -66,10 +65,10 @@ export const getService = {
service: getService(serviceId: $serviceId) {
id
value: name
label: name
group
layers
normal
label: name
group
layers
normal
}
`,
};
@@ -79,16 +78,15 @@ export const getInstance = {
query: `
instance: getInstance(instanceId: $instanceId) {
id
value: name
label: name
language
instanceUUID
layer
attributes {
name
value
}
value: name
label: name
language
instanceUUID
attributes {
name
value
}
}
`,
};
@@ -97,10 +95,10 @@ export const getEndpoint = {
query: `
endpoint: getEndpointInfo(endpointId: $endpointId) {
id
value: name
label: name
serviceId
serviceName
value: name
label: name
serviceId
serviceName
}
`,
};

View File

@@ -27,7 +27,6 @@ export const Traces = {
isError
traceIds
}
total
}`,
};
@@ -74,3 +73,14 @@ export const TraceSpans = {
}
`,
};
export const TraceTagKeys = {
variable: "$duration: Duration!",
query: `
tagKeys: queryTraceTagAutocompleteKeys(duration: $duration)`,
};
export const TraceTagValues = {
variable: "$tagKey: String!, $duration: Duration!",
query: `
tagValues: queryTraceTagAutocompleteValues(tagKey: $tagKey, duration: $duration)`,
};

View File

@@ -25,6 +25,8 @@ import * as log from "./query/log";
import * as profile from "./query/profile";
import * as alarm from "./query/alarm";
import * as event from "./query/event";
import * as ebpf from "./query/ebpf";
import * as demandLog from "./query/demand-log";
const query: { [key: string]: string } = {
...app,
@@ -36,6 +38,8 @@ const query: { [key: string]: string } = {
...profile,
...alarm,
...event,
...ebpf,
...demandLog,
};
class Graphql {
private queryData = "";

View File

@@ -0,0 +1,22 @@
/**
* 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
*
* http://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 { queryContainers, queryStreamingLogs } from "../fragments/demand-log";
export const fetchContainers = `query listContainers(${queryContainers.variable}) {${queryContainers.query}}`;
export const fetchDemandPodLogs = `query ondemandPodLogs(${queryStreamingLogs.variable}) {${queryStreamingLogs.query}}`;

34
src/graphql/query/ebpf.ts Normal file
View File

@@ -0,0 +1,34 @@
/**
* 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
*
* http://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 {
queryCreateTaskData,
createEBPFTask,
queryEBPFTasks,
queryEBPFSchedules,
analysisEBPFResult,
} from "../fragments/ebpf";
export const getCreateTaskData = `query queryCreateTaskData(${queryCreateTaskData.variable}) {${queryCreateTaskData.query}}`;
export const saveEBPFTask = `mutation createEBPFTask(${createEBPFTask.variable}) {${createEBPFTask.query}}`;
export const getEBPFTasks = `query queryEBPFTasks(${queryEBPFTasks.variable}) {${queryEBPFTasks.query}}`;
export const getEBPFSchedules = `query queryEBPFSchedules(${queryEBPFSchedules.variable}) {${queryEBPFSchedules.query}}`;
export const getEBPFResult = `query analysisEBPFResult(${analysisEBPFResult.variable}) {${analysisEBPFResult.query}}`;

View File

@@ -19,9 +19,13 @@ import {
QueryBrowserErrorLogs,
QueryServiceLogs,
QueryLogsByKeywords,
LogTagValues,
LogTagKeys,
} from "../fragments/log";
export const queryBrowserErrorLogs = `query queryBrowserErrorLogs(${QueryBrowserErrorLogs.variable}) {
${QueryBrowserErrorLogs.query}}`;
export const queryServiceLogs = `query queryLogs(${QueryServiceLogs.variable}) {${QueryServiceLogs.query}}`;
export const queryLogsByKeywords = `query queryLogsByKeywords {${QueryLogsByKeywords.query}}`;
export const queryLogTagValues = `query queryTagValues(${LogTagValues.variable}) {${LogTagValues.query}}`;
export const queryLogTagKeys = `query queryTagKeys(${LogTagKeys.variable}) {${LogTagKeys.query}}`;

View File

@@ -15,8 +15,17 @@
* limitations under the License.
*/
import { Traces, TraceSpans } from "../fragments/trace";
import {
Traces,
TraceSpans,
TraceTagKeys,
TraceTagValues,
} from "../fragments/trace";
export const queryTraces = `query queryTraces(${Traces.variable}) {${Traces.query}}`;
export const queryTrace = `query queryTrace(${TraceSpans.variable}) {${TraceSpans.query}}`;
export const queryTraceTagKeys = `query queryTraceTagKeys(${TraceTagKeys.variable}) {${TraceTagKeys.query}}`;
export const queryTraceTagValues = `query queryTraceTagValues(${TraceTagValues.variable}) {${TraceTagValues.query}}`;

View File

@@ -33,6 +33,9 @@ export enum Calculations {
ConvertSeconds = "convertSeconds",
ConvertMilliseconds = "convertMilliseconds",
MsTos = "msTos",
Average = "average",
PercentageAvg = "percentageAvg",
ApdexAvg = "apdexAvg",
}
export enum sizeEnum {
XS = "XS",

View File

@@ -79,7 +79,11 @@ export function useECharts(
if (!el || !unref(el)) {
return;
}
const { width, height } = el.getBoundingClientRect();
if (!width || !height) {
return;
}
chartInstance = echarts.init(el, t);
const { removeEvent } = useEventListener({
el: window,

View File

@@ -0,0 +1,39 @@
/**
* 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
*
* http://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 { MetricQueryTypes, Calculations } from "./data";
export function useListConfig(config: any, index: string) {
const i = Number(index);
const types = [
Calculations.Average,
Calculations.ApdexAvg,
Calculations.PercentageAvg,
];
const calculation =
config.metricConfig &&
config.metricConfig[i] &&
config.metricConfig[i].calculation;
const line =
config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
!types.includes(calculation);
const isAvg =
config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
types.includes(calculation);
return {
isLinear: line,
isAvg,
};
}

View File

@@ -150,9 +150,7 @@ export function useSourceProcessor(
const c = (config.metricConfig && config.metricConfig[index]) || {};
if (type === MetricQueryTypes.ReadMetricsValues) {
source[m] = resp.data[keys[index]].values.values.map(
(d: { value: number }) => aggregation(d.value, c)
);
source[m] = calculateExp(resp.data[keys[index]].values.values, c);
}
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
const resVal = Object.values(resp.data)[0] || [];
@@ -166,7 +164,6 @@ export function useSourceProcessor(
const values = item.values.values.map((d: { value: number }) =>
aggregation(Number(d.value), c)
);
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
if (labels[indexNum] && indexNum > -1) {
source[labels[indexNum]] = values;
@@ -281,14 +278,24 @@ export function usePodsSource(
}
const data = pods.map((d: Instance | any, idx: number) => {
config.metrics.map((name: string, index: number) => {
const c = (config.metricConfig && config.metricConfig[index]) || {};
const c: any = (config.metricConfig && config.metricConfig[index]) || {};
const key = name + idx + index;
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
d[name] = aggregation(resp.data[key], c);
}
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
d[name] = resp.data[key].values.values.map((d: { value: number }) =>
aggregation(d.value, c)
d[name] = {};
if (
[
Calculations.Average,
Calculations.ApdexAvg,
Calculations.PercentageAvg,
].includes(c.calculation)
) {
d[name]["avg"] = calculateExp(resp.data[key].values.values, c);
}
d[name]["values"] = resp.data[key].values.values.map(
(val: { value: number }) => aggregation(val.value, c)
);
}
});
@@ -322,26 +329,59 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
return { queryStr, conditions };
}
function calculateExp(
arr: { value: number }[],
config: { calculation?: string }
): (number | string)[] {
const sum = arr
.map((d: { value: number }) => d.value)
.reduce((a, b) => a + b);
let data: (number | string)[] = [];
switch (config.calculation) {
case Calculations.Average:
data = [(sum / arr.length).toFixed(2)];
break;
case Calculations.PercentageAvg:
data = [(sum / arr.length / 100).toFixed(2)];
break;
case Calculations.ApdexAvg:
data = [(sum / arr.length / 10000).toFixed(2)];
break;
default:
data = arr.map((d) => aggregation(d.value, config));
break;
}
return data;
}
export function aggregation(val: number, config: any): number | string {
export function aggregation(
val: number,
config: { calculation?: string }
): number | string {
let data: number | string = Number(val);
switch (config.calculation) {
case Calculations.Percentage:
data = val / 100;
data = (val / 100).toFixed(2);
break;
case Calculations.PercentageAvg:
data = (val / 100).toFixed(2);
break;
case Calculations.ByteToKB:
data = val / 1024;
data = (val / 1024).toFixed(2);
break;
case Calculations.ByteToMB:
data = val / 1024 / 1024;
data = (val / 1024 / 1024).toFixed(2);
break;
case Calculations.ByteToGB:
data = val / 1024 / 1024 / 1024;
data = (val / 1024 / 1024 / 1024).toFixed(2);
break;
case Calculations.Apdex:
data = val / 10000;
break;
case Calculations.ApdexAvg:
data = val / 10000;
break;
case Calculations.ConvertSeconds:
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
break;
@@ -352,7 +392,7 @@ export function aggregation(val: number, config: any): number | string {
data = data.toFixed(2);
break;
case Calculations.MsTos:
data = val / 1000;
data = (val / 1000).toFixed(2);
break;
default:
data;

View File

@@ -21,19 +21,6 @@ limitations under the License. -->
</router-view>
</section>
</template>
<script lang="ts" setup>
import { ElMessage } from "element-plus";
import { useAppStoreWithOut } from "@/store/modules/app";
const appStore = useAppStoreWithOut();
if (!appStore.utc) {
const res = appStore.queryOAPTimeInfo();
if (res.errors) {
ElMessage.error(res.errors);
}
}
</script>
<style lang="scss" scoped>
.app-main {
height: calc(100% - 40px);

View File

@@ -94,9 +94,6 @@ watch(
}
);
async function getVersion() {
if (appStore.version) {
return;
}
const res = await appStore.fetchVersion();
if (res.errors) {
ElMessage.error(res.errors);

View File

@@ -41,7 +41,7 @@ limitations under the License. -->
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
<Icon size="lg" :iconName="menu.meta.icon" />
</el-icon>
<span :class="isCollapse ? 'collapse' : ''">
<span class="title" :class="isCollapse ? 'collapse' : ''">
{{ t(menu.meta.title) }}
</span>
</router-link>
@@ -57,7 +57,7 @@ limitations under the License. -->
:to="m.path"
:exact="m.meta.exact || false"
>
<span>{{ t(m.meta.title) }}</span>
<span class="title">{{ t(m.meta.title) }}</span>
</router-link>
</el-menu-item>
</el-menu-item-group>
@@ -82,7 +82,7 @@ limitations under the License. -->
:to="menu.children[0].path"
:exact="menu.meta.exact"
>
<span>{{ t(menu.meta.title) }}</span>
<span class="title">{{ t(menu.meta.title) }}</span>
</router-link>
</template>
</el-menu-item>
@@ -109,14 +109,21 @@ import { ref } from "vue";
import { useRouter, RouteRecordRaw } from "vue-router";
import { useI18n } from "vue-i18n";
import Icon from "@/components/Icon.vue";
import { useAppStoreWithOut } from "@/store/modules/app";
const appStore = useAppStoreWithOut();
const { t } = useI18n();
const name = ref<any>(String(useRouter().currentRoute.value.name));
const name = ref<string>(String(useRouter().currentRoute.value.name));
const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "")
? ref("light")
: ref("black");
const routes = ref<any>(useRouter().options.routes);
const isCollapse = ref(false);
const routes = ref<RouteRecordRaw[] | any>(useRouter().options.routes);
if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
appStore.setIsMobile(true);
} else {
appStore.setIsMobile(false);
}
const isCollapse = ref(appStore.isMobile ? true : false);
const controlMenu = () => {
isCollapse.value = !isCollapse.value;
};
@@ -201,4 +208,11 @@ span.collapse {
width: 100%;
height: 60px;
}
.title {
display: inline-block;
max-width: 110px;
text-overflow: ellipsis;
overflow: hidden;
}
</style>

View File

@@ -17,10 +17,12 @@
import { createI18n } from "vue-i18n";
import zh from "./lang/zh";
import en from "./lang/en";
import es from "./lang/es";
const messages = {
en,
zh,
es,
};
const savedLanguage = window.localStorage.getItem("language");

View File

@@ -111,8 +111,8 @@ const msg = {
noRoot: "Please set a root dashboard for",
noWidget: "Please add widgets.",
rename: "Rename",
deleteTitle: "Are you sure to delete this?",
rootTitle: "Are you sure to set this?",
deleteTitle: "Are you sure you want to delete this?",
rootTitle: "Are you sure you want to set this?",
selfObservability: "Self Observability",
satellite: "Satellite",
skyWalkingServer: "SkyWalking Server",
@@ -131,6 +131,18 @@ const msg = {
metricLabel: "Metric Label",
showUnit: "Show Unit",
noGraph: "No Graph",
taskId: "Task ID",
triggerType: "Trigger Type",
targetType: "Target Type",
ebpfTip: "Don't have a process for profiling",
processSelect: "Click to select processes",
container: "Container",
limit: "Limit",
page: "Page",
interval: "Refresh Interval",
pause: "Pause",
begin: "Start",
seconds: "Seconds",
hourTip: "Select Hour",
minuteTip: "Select Minute",
secondTip: "Select Second",
@@ -138,7 +150,7 @@ const msg = {
yearSuffix: "Year",
monthsHead: "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec",
months: "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec",
weeks: "Mon_Tue_Wed_Thu_Fir_Sat_Sun",
weeks: "Mon_Tue_Wed_Thu_Fri_Sat_Sun",
hello: "Hello",
helloMessage: "Welcome Back, Apache SkyWalking APM System !",
username: "Username",
@@ -237,6 +249,10 @@ const msg = {
defaultDepth: "Default Depth",
traceTagsTip: `Only tags defined in the core/default/searchableTracesTags are searchable.
Check more details on the Configuration Vocabulary page`,
logTagsTip: `Only tags defined in the core/default/searchableLogsTags are searchable.
Check more details on the Configuration Vocabulary page`,
alarmTagsTip: `Only tags defined in the core/default/searchableAlarmTags are searchable.
Check more details on the Configuration Vocabulary page`,
tagsLink: "Configuration Vocabulary page",
addTag: "Please input a tag",
log: "Log",
@@ -317,7 +333,7 @@ const msg = {
addExcludingKeywordsOfContent: "Please input a keyword of excluding content",
noticeTag: "Please press Enter after inputting a tag(key=value).",
conditionNotice:
"Notice: Please press Enter after inputting a tag, key of content, exclude key of content(key=value).",
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
language: "Language",
};
export default msg;

342
src/locales/lang/es.ts Normal file
View File

@@ -0,0 +1,342 @@
/**
* 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
*
* http://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.
*/
const msg = {
general: "Servicio General",
services: "Servicios",
service: "Servicio",
traces: "Trazas",
metrics: "Métricas",
serviceMesh: "Malla de Servicios",
infrastructure: "Infraestructura",
virtualMachine: "Máquina Virtual",
dashboardNew: "Nuevo Panel",
dashboardList: "Listado Paneles",
logs: "Logs",
events: "Eventos",
alerts: "Alertas",
settings: "Ajustes",
dashboards: "Paneles",
profiles: "Perfiles",
database: "Base de Datos",
serviceName: "Nombre Servicio",
technologies: "Tecnologías",
generalServicePanel: "Panel Servicio General",
health: "Salud",
groupName: "Nombre Grupo",
topologies: "Topologías",
dataPanel: "Plano de Datos",
controlPanel: "Plano de Control",
eventList: "Listado Eventos",
newDashboard: "Crear panel nuevo",
dashboardEdit: "Editar el panel",
edit: "Editar",
delete: "Eliminar",
confirm: "Confirmar",
layer: "Capa",
endpoint: "Endpoint",
instance: "Instancia",
create: "Crear",
loading: "Cargando",
selectVisualization: "Visualiza tus métricas",
visualization: "Visualizaciones",
graphStyles: "Estilo de gráficas",
widgetOptions: "Opciones widget",
standardOptions: "Opciones estandar",
max: "Máx",
min: "Mín",
plus: "Más",
minus: "Menoss",
multiply: "Multiplcar",
divide: "Dividir",
convertToMilliseconds: "Convertir Unix Timestamp(milisegundos)",
convertToSeconds: "Convertir Unix Timestamp(segundos)",
smooth: "Suabe",
showSymbol: "Mostrar Símbolo",
step: "Paso",
showValues: "Mostrar Valores",
fontSize: "Tamaño Fuente",
showBackground: "Mostrar Fondo",
areaOpacity: "Opacidad Área",
editGraph: "Editar Opciones",
dashboardName: "Selecciona Nombre del Panel",
linkDashboard: "Nombre del panel relacionado con llamadas de la topología",
linkServerMetrics:
"Métricas de servidor relacionadas con llamadas de la topología",
linkClientMetrics:
"Métricas de cliente relacionadas con llamadas de la topología",
nodeDashboard: "Nombre del panel relacionado con nodos de la topología",
nodeMetrics: "Mêtricas relacionas con nodos de la topología",
instanceDashboard: "Nombre del panel relacionado con instancias de servicio",
endpointDashboard: "Nombre del panel relacionado con endpoints",
callSettings: "Ajustes Llamada",
nodeSettings: "Ajustes Nodo",
conditions: "Condiciones",
legendSettings: "Ajustes Leyenda",
setLegend: "Poner Leyenda",
backgroundColors: "Colores Fondo",
fontColors: "Colores Fuente",
iconTheme: "Tema Iconos",
default: "Por Defecto",
topSlow: "Top 5 lentos",
topChildren: "Top 5 hijos",
taskList: "Listado Tareas",
sampledTraces: "Trazas Muestreadas",
editTab: "Habilitar edición nombre pestanyas",
label: "Nombre Servicio",
id: "ID Servicio",
setRoot: "Ponerlo a raíz",
setNormal: "Ponerlo a normal",
export: "Exportar Plantilla Panel",
import: "Importar Plantilla Panel",
yes: "Sí",
no: "No",
tableHeaderCol1: "Nombre de la primera columna de la tabla",
tableHeaderCol2: "Nombre de la segunda columna de la tabla",
showXAxis: "Mostrar Eje X",
showYAxis: "Mostrar Eje Y",
nameError: "El nombre del panel no puede ser duplicado",
showGroup: "Mostrar Grupo",
noRoot: "Por favor ponga la raíz del panel",
noWidget: "Por favor añada widgets.",
rename: "Renombrar",
deleteTitle: "¿Está seguro que quiere eliminarlo?",
rootTitle: "¿Está seguro que quiere establecerlo?",
selfObservability: "Autoobservabilidad",
satellite: "Satéllite",
skyWalkingServer: "Servidor SkyWalking",
functions: "Funciones",
browser: "Navegador",
linux: "Linux",
editWarning: "Estás entrando en modo edición",
viewWarning: "Estás entrando en modo visualización",
virtualDatabase: "Base de Datos Virtual",
reloadDashboards: "Recargar Panel",
kubernetesService: "Servicio",
kubernetesCluster: "Cluster",
kubernetes: "Kubernetes",
textUrl: "Hipervínculo de Texto",
textAlign: "Alineación de Texto",
metricLabel: "Etiqueta de Métrica",
showUnit: "Mostrar Unidad",
noGraph: "Ningún Gráfico",
taskId: "ID Tarea",
triggerType: "Tipo de Disparador",
targetType: "Tipo de Objetivo",
ebpfTip: "Le falta el proceso para perfilar",
processSelect: "Click para seleccionar proceso",
page: "Página",
interval: "Intervalo de actualización",
pause: "Pausa",
begin: "Inicio",
seconds: "Segundos",
hourTip: "Seleccione Hora",
minuteTip: "Seleccione Minuto",
secondTip: "Seleccione Segundo",
second: "s",
yearSuffix: "Año",
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
months: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
weeks: "Lun_Mar_Mier_Jue_Vie_Sáb_Dom",
hello: "Hola",
helloMessage: "Bienvenido de vuelta, Apache SkyWalking APM System !",
username: "Usuario",
password: "Contraseña",
title: "Título",
width: "Ancho",
height: "Alto",
dashboard: "Panel",
topology: "Topología",
trace: "Traza",
alarm: "Alarmas",
auto: "Auto",
reload: "Recargar",
version: "Versión",
copy: "Copiar",
reset: "Resetear",
apply: "Aplicar",
template: "Plantilla",
cancel: "Cancelar",
createTab: "Crear Pestanya",
tabName: "Nombre de la Pestaña",
detectPoint: "Detectar Punto",
name: "Nombre",
types: "Tipos",
all: "Todo",
endpoints: "Endpoints",
cache: "Cache",
serviceinstance: "InstanciaServicio",
databaseaccess: "AccesoBaseDeDatos",
servicerelation: "RelaciónServicio",
serviceinstancerelation: "RelaciónInstanciaServicio",
endpointrelation: "RelaciónEndpoint",
status: "Estado",
endpointName: "Nombre Endpoint",
search: "Buscar",
clear: "Limpiar",
more: "Más",
traceID: "ID Traza",
range: "Rango",
timeRange: "Rango de Tiempo",
duration: "Duración",
startTime: "Hora Inicio",
start: "Incio",
spans: "Lapso",
spanInfo: "Info Lapso",
spanType: "Tipo de Lapso",
time: "Tiempo",
tags: "Etiquetas",
component: "Componente",
table: "Tabla",
list: "Lista",
tree: "Árbol",
filterScope: "Alcance de Filtro",
searchKeyword: "Palabra Clave",
quarterHourCutTip: "Últimos 15 mins",
halfHourCutTip: "Últimos 30 mins",
hourCutTip: "Última 1 hora",
dayCutTip: "Último 1 día",
weekCutTip: "Última 1 semana",
monthCutTip: "Última 1 mes",
serverZone: "Zona Horaria Servidor OAP",
exportImage: "Exportar imagen",
object: "Objecto",
profile: "Perfil",
newTask: "Nueva Tarea",
monitorTime: "Tiempo Monitorización",
monitorDuration: "Duración Monitorización",
minThreshold: "Mínn Umbral Duración",
dumpPeriod: "Volcar Periodo",
createTask: "Crear Tarea",
maxSamplingCount: "Máx Cantidad Mostreo",
analyze: "Analizar",
noData: "Ningún Dato",
taskInfo: "Información Tarea",
task: "Tarea",
operationType: "Tipo Operación",
operationTime: "Tiempo Operación",
taskView: "Ver Tarea",
includeChildren: "Incluir Hijos",
excludeChildren: "Excluir Hijos",
view: "Ver",
timeTips: "Intervalo de tiempo no puede excedir 60 dias",
entityType: "Tipo Entidad",
maxItemNum: "Máx número artículos",
unknownMetrics: "Métrica desconocida",
labels: "Etiquetas",
aggregation: "Cálculo",
unit: "Unidad",
labelsIndex: "Subíndice Etiqueta",
group: "Grupo Servicio",
browserView: "Navegador",
sortOrder: "Orden de clasificación",
chartType: "Tipo Gráfico",
currentDepth: "Profundidad actual",
showDepth: "Mostrar Selector Profundidad",
defaultDepth: "Profundidad Por Defecto",
traceTagsTip: `Solamente etiquetas definidas en core/default/searchableTracesTags pueden ser buscadas.
Más información en la página de Vocabulario de Configuración`,
logTagsTip: `Solamente etiquetas definidas en core/default/searchableLogsTags pueden ser buscadas.
Más información en la página de Vocabulario de Configuración`,
alarmTagsTip: `Solamente etiquetas definidas en core/default/searchableAlarmTags pueden ser buscadas.
Más información en la página de Vocabulario de Configuración`,
tagsLink: "Página de Vocabulario de Configuración",
addTag: "Por favor introduzca una etiqueta",
log: "Registro de Datos",
logCategory: "Categoría Registro de Datos",
errorCatalog: "Catálogo de Errores",
logDetail: "Detalle Registro de Datos",
timeReload: "Aviso: El intervalo de tiempo tiene que ser mayor que 0",
errorInfo: "Info Error",
stack: "Pila",
serviceVersion: "Versión Servicio",
errorPage: "Página de Error",
category: "Categoría",
grade: "Grado",
relatedTraceLogs: "Registro de Datos Relacionados",
setConditions: "Más Condiciones",
metricName: "Seleccionar Nombre Métrica",
keywordsOfContent: "Claves de Contenido",
excludingKeywordsOfContent: "Excluir Claves de Contenido",
return: "Volver",
isError: "Error",
contentType: "Tipo de Contenido",
content: "Contenido",
viewLogs: "Ver Registro de Datos",
logsTagsTip: `Solamente etiquetas definidas en core/default/searchableLogsTags pueden ser buscadas.
Más información en la página de Vocabulario de Configuración`,
keywordsOfContentLogTips:
"El almacenamiento actual del servidor SkyWalking OAP no lo soporta.",
setEvent: "Establecer Evento",
viewAttributes: "Ver",
serviceEvents: "Eventos Servico",
select: "Seleccionar",
eventID: "ID Evento",
eventName: "Nombre Evento",
endTime: "Hora Finalización",
instanceEvents: "Eventos Instancia",
endpointEvents: "Eventos Endpoint",
enableEvents: "Habilitar Eventos",
disableEvents: "Deshabilitar Eventos",
eventSeries: "Serie de Eventos",
eventsType: "Tipo de Evento",
eventsMessage: "Mensaje del Evento",
eventsParameters: "Parámetro del Evento",
eventDetail: "Detalle del Evento",
value: "Valor",
show: "Mostrar",
hide: "Oculatr",
statistics: "Estadísticas",
message: "Mensaje",
tooltipsContent: "Contenido de Información de Herramienta",
alarmDetail: "Detalle Alarma",
scope: "Alcance",
destService: "Servicio Destinación",
destServiceInstance: "Instancia Servicio Destinación",
destEndpoint: "Endpoint Destinación",
eventSource: "Fuente Envento",
modalTitle: "Inspección",
selectRedirectPage:
"Quiere inspeccionar las Trazas or Registros de datos del servicio %s?",
logAnalysis: "Lenguaje de Análisis de Registro de Datos",
logDataBody: "Contenido del Registro de Datos",
addType: "Por favor introduzca un tipo",
traceContext: "Registro de datos con contexto de traza",
traceSegmentId: "ID Segmento Traza",
spanId: "ID Lapso",
inputTraceSegmentId: "Por favor introduzca el ID del segmento de la traza",
inputSpanId: "Por favor introduzca el ID del lapso",
inputTraceId: "Por favor introduzca el ID de la traza",
dsl: "Entrada de guión para LAL",
logContentType: "Tipo del registro de datos",
logRespContent: "Contenido Registro de Datos",
analysis: "Análisis",
waitLoading: "Cargando",
dslEmpty: "Entrada de guión de LAL no puede estar vacio",
logContentEmpty: "El contenido del registro de datos no puede estar vacio.",
debug: "Debugar",
addTraceID: "Por favor introduzca el ID de la traza",
addTags: "Por favor introduzaca una etiqueta",
addKeywordsOfContent: "Por favor introduzca una clave de contenido",
addExcludingKeywordsOfContent:
"Por favor introduzca una clave excluyente de contenido",
noticeTag:
"Por favor presione Intro después de introducir una etiqueta(clave=valor).",
conditionNotice:
"Aviso: Por favor presione Intro después de introducir una clave de contenido, excluir clave de contenido(clave=valor).",
language: "Lenguaje",
};
export default msg;

View File

@@ -129,6 +129,18 @@ const msg = {
metricLabel: "指标标签",
showUnit: "显示单位",
noGraph: "无图表",
taskId: "任务ID",
triggerType: "触发类型",
targetType: "目标类型",
processSelect: "点击选择进程",
ebpfTip: "没有进程可以分析",
container: "容器",
limit: "范围",
page: "页面",
interval: "刷新间隔时间",
pause: "暂停",
begin: "开始",
seconds: "秒",
hourTip: "选择小时",
minuteTip: "选择分钟",
secondTip: "选择秒数",
@@ -238,6 +250,10 @@ const msg = {
defaultDepth: "默认深度",
traceTagsTip:
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
logTagsTip:
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
alarmTagsTip:
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
tagsLink: "配置词汇页",
addTag: "请添加标签",
logCategory: "日志类别",
@@ -318,7 +334,7 @@ const msg = {
addExcludingKeywordsOfContent: "请输入一个内容不包含的关键词",
noticeTag: "请输入一个标签(key=value)之后回车",
conditionNotice:
"请输入一个标签、内容关键词或者内容不包含的关键词(key=value)之后回车",
"请输入一个内容关键词或者内容不包含的关键词(key=value)之后回车",
language: "语言",
};
export default msg;

View File

@@ -20,14 +20,18 @@ import router from "./router";
import { store } from "./store";
import components from "@/components";
import i18n from "./locales";
import "element-plus/dist/index.css";
import "./styles/index.scss";
import ElementPlus from "element-plus";
import { useAppStoreWithOut } from "@/store/modules/app";
import "./styles/index.ts";
const app = createApp(App);
const appStore = useAppStoreWithOut();
app.use(ElementPlus, { size: "small", zIndex: 3000 });
app.use(components);
app.use(i18n);
app.use(store);
app.use(router).mount("#app");
mountApp();
async function mountApp() {
await appStore.queryOAPTimeInfo();
app.use(router).mount("#app");
}

View File

@@ -33,6 +33,7 @@ interface AppState {
autoRefresh: boolean;
pageTitle: string;
version: string;
isMobile: boolean;
}
export const appStore = defineStore({
@@ -51,6 +52,7 @@ export const appStore = defineStore({
autoRefresh: false,
pageTitle: "",
version: "",
isMobile: false,
}),
getters: {
duration(): Duration {
@@ -88,12 +90,9 @@ export const appStore = defineStore({
this.duration.start.getMonth());
break;
}
const utcArr = this.utc.split(":");
const utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
const utcMin = isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]);
const utcSpace =
(utcHour + new Date().getTimezoneOffset() / 60) * 3600000 +
utcMin * 60000;
(this.utcHour + new Date().getTimezoneOffset() / 60) * 3600000 +
this.utcMin * 60000;
const startUnix: number = this.duration.start.getTime();
const endUnix: number = this.duration.end.getTime();
const timeIntervals: string[] = [];
@@ -124,6 +123,9 @@ export const appStore = defineStore({
this.utcHour = utcHour;
this.utc = `${utcHour}:${utcMin}`;
},
setIsMobile(mode: boolean) {
this.isMobile = mode;
},
setEventStack(funcs: (() => void)[]): void {
this.eventStack = funcs;
},
@@ -155,6 +157,10 @@ export const appStore = defineStore({
}
this.utc = res.data.data.getTimeInfo.timezone / 100 + ":0";
const utcArr = this.utc.split(":");
this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
this.utcMin = isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]);
return res.data;
},
async fetchVersion(): Promise<void> {

View File

@@ -20,10 +20,8 @@ import { LayoutConfig } from "@/types/dashboard";
import graphql from "@/graphql";
import query from "@/graphql/fetch";
import { DashboardItem } from "@/types/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { NewControl, TextConfig } from "../data";
import { Duration } from "@/types/app";
import { AxiosResponse } from "axios";
import { ElMessage } from "element-plus";
import { useI18n } from "vue-i18n";
@@ -35,7 +33,6 @@ interface DashboardState {
entity: string;
layerId: string;
activedGridItem: string;
durationTime: Duration;
selectorStore: any;
showTopology: boolean;
currentTabItems: LayoutConfig[];
@@ -53,7 +50,6 @@ export const dashboardStore = defineStore({
entity: "",
layerId: "",
activedGridItem: "",
durationTime: useAppStoreWithOut().durationTime,
selectorStore: useSelectorStore(),
showTopology: false,
currentTabItems: [],
@@ -114,7 +110,13 @@ export const dashboardStore = defineStore({
: 3,
};
}
if (type === "Trace" || type === "Profile" || type === "Log") {
if (
type === "Trace" ||
type === "Profile" ||
type === "Log" ||
type === "Ebpf" ||
type === "DemandLog"
) {
newItem.h = 36;
}
if (type === "Text") {
@@ -169,7 +171,13 @@ export const dashboardStore = defineStore({
showDepth: true,
};
}
if (type === "Trace" || type === "Profile" || type === "Log") {
if (
type === "Trace" ||
type === "Profile" ||
type === "Log" ||
type === "DemandLog" ||
type === "Ebpf"
) {
newItem.h = 32;
}
if (type === "Text") {

View File

@@ -0,0 +1,126 @@
/**
* 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
*
* http://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 { defineStore } from "pinia";
import { Instance } from "@/types/selector";
import { store } from "@/store";
import graphql from "@/graphql";
import { AxiosResponse } from "axios";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { Conditions, Log } from "@/types/demand-log";
interface DemandLogState {
containers: Instance[];
instances: Instance[];
conditions: Conditions;
selectorStore: any;
logs: Log[];
loadLogs: boolean;
message: string;
total: number;
}
export const demandLogStore = defineStore({
id: "demandLog",
state: (): DemandLogState => ({
containers: [{ label: "", value: "" }],
instances: [{ value: "", label: "" }],
conditions: {
container: "",
serviceInstanceId: "",
duration: useAppStoreWithOut().durationTime,
},
selectorStore: useSelectorStore(),
logs: [],
loadLogs: false,
message: "",
total: 0,
}),
actions: {
setLogCondition(data: Conditions) {
this.conditions = { ...this.conditions, ...data };
},
setLogs(logs: Log[], message?: string) {
this.logs = logs;
this.message = message || "";
},
async getInstances(id: string) {
const serviceId = this.selectorStore.currentService
? this.selectorStore.currentService.id
: id;
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
});
if (res.data.errors) {
return res.data;
}
this.instances = res.data.data.pods || [];
return res.data;
},
async getContainers(serviceInstanceId: string) {
if (!serviceInstanceId) {
return new Promise((resolve) =>
resolve({ errors: "No service instance" })
);
}
const condition = {
serviceInstanceId,
};
const res: AxiosResponse = await graphql
.query("fetchContainers")
.params({ condition });
if (res.data.errors) {
return res.data;
}
if (res.data.data.containers.errorReason) {
this.containers = [{ label: "", value: "" }];
return res.data;
}
this.containers = res.data.data.containers.containers.map((d: string) => {
return { label: d, value: d };
});
return res.data;
},
async getDemandLogs() {
this.loadLogs = true;
const res: AxiosResponse = await graphql
.query("fetchDemandPodLogs")
.params({ condition: this.conditions });
this.loadLogs = false;
if (res.data.errors) {
return res.data;
}
if (res.data.data.logs.errorReason) {
this.setLogs("", res.data.data.logs.errorReason);
return res.data;
}
this.total = res.data.data.logs.logs.length;
const logs = res.data.data.logs.logs
.map((d: Log) => d.content)
.join("\n");
this.setLogs(logs);
return res.data;
},
},
});
export function useDemandLogStore(): any {
return demandLogStore(store);
}

169
src/store/modules/ebpf.ts Normal file
View File

@@ -0,0 +1,169 @@
/**
* 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
*
* http://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 { defineStore } from "pinia";
import { Option } from "@/types/app";
import {
EBPFTaskCreationRequest,
EBPFProfilingSchedule,
EBPFTaskList,
AnalyzationTrees,
} from "@/types/ebpf";
import { store } from "@/store";
import graphql from "@/graphql";
import { AxiosResponse } from "axios";
interface EbpfStore {
taskList: EBPFTaskList[];
eBPFSchedules: EBPFProfilingSchedule[];
currentSchedule: EBPFProfilingSchedule | Record<string, never>;
analyzeTrees: AnalyzationTrees[];
labels: Option[];
couldProfiling: boolean;
tip: string;
selectedTask: Recordable<EBPFTaskList>;
aggregateType: string;
}
export const ebpfStore = defineStore({
id: "eBPF",
state: (): EbpfStore => ({
taskList: [],
eBPFSchedules: [],
currentSchedule: {},
analyzeTrees: [],
labels: [{ value: "", label: "" }],
couldProfiling: false,
tip: "",
selectedTask: {},
aggregateType: "COUNT",
}),
actions: {
setSelectedTask(task: EBPFTaskList) {
this.selectedTask = task;
},
setCurrentSchedule(s: EBPFProfilingSchedule) {
this.currentSchedule = s;
},
setAnalyzeTrees(tree: AnalyzationTrees[]) {
this.analyzeTrees = tree;
},
async getCreateTaskData(serviceId: string) {
const res: AxiosResponse = await graphql
.query("getCreateTaskData")
.params({ serviceId });
if (res.data.errors) {
return res.data;
}
const json = res.data.data.createTaskData;
this.couldProfiling = json.couldProfiling || false;
this.labels = json.processLabels.map((d: string) => {
return { label: d, value: d };
});
return res.data;
},
async createTask(param: EBPFTaskCreationRequest) {
const res: AxiosResponse = await graphql
.query("saveEBPFTask")
.params({ request: param });
if (res.data.errors) {
return res.data;
}
this.getTaskList(param.serviceId);
return res.data;
},
async getTaskList(serviceId: string) {
if (!serviceId) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getEBPFTasks")
.params({ serviceId });
this.tip = "";
if (res.data.errors) {
return res.data;
}
this.taskList = res.data.data.queryEBPFTasks || [];
if (!this.taskList.length) {
return res.data;
}
this.getEBPFSchedules({ taskId: this.taskList[0].taskId });
return res.data;
},
async getEBPFSchedules(params: { taskId: string }) {
if (!params.taskId) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getEBPFSchedules")
.params({ ...params });
if (res.data.errors) {
this.eBPFSchedules = [];
return res.data;
}
this.tip = "";
const { eBPFSchedules } = res.data.data;
this.eBPFSchedules = eBPFSchedules;
if (!eBPFSchedules.length) {
this.eBPFSchedules = [];
this.analyzeTrees = [];
}
return res.data;
},
async getEBPFAnalyze(params: {
scheduleIdList: string[];
timeRanges: Array<{ start: number; end: number }>;
aggregateType: string;
}) {
this.aggregateType = params.aggregateType;
if (!params.scheduleIdList.length) {
return new Promise((resolve) => resolve({}));
}
if (!params.timeRanges.length) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getEBPFResult")
.params(params);
if (res.data.errors) {
this.analyzeTrees = [];
return res.data;
}
const { analysisEBPFResult } = res.data.data;
this.tip = analysisEBPFResult.tip;
if (!analysisEBPFResult) {
this.analyzeTrees = [];
return res.data;
}
if (analysisEBPFResult.tip) {
this.analyzeTrees = [];
return res.data;
}
this.analyzeTrees = analysisEBPFResult.trees;
return res.data;
},
},
});
export function useEbpfStore(): any {
return ebpfStore(store);
}

View File

@@ -25,7 +25,6 @@ import { useAppStoreWithOut } from "@/store/modules/app";
interface eventState {
loading: boolean;
events: Event[];
total: number;
services: Service[];
instances: Instance[];
endpoints: Endpoint[];
@@ -37,13 +36,11 @@ export const eventStore = defineStore({
state: (): eventState => ({
loading: false,
events: [],
total: 0,
services: [{ value: "", label: "All" }],
instances: [{ value: "", label: "All" }],
endpoints: [{ value: "", label: "All" }],
condition: {
time: useAppStoreWithOut().durationTime,
paging: { pageNum: 1, pageSize: 15, needTotal: true },
paging: { pageNum: 1, pageSize: 15 },
},
}),
actions: {
@@ -94,9 +91,12 @@ export const eventStore = defineStore({
},
async getEvents() {
this.loading = true;
const res: AxiosResponse = await graphql
.query("queryEvents")
.params({ condition: this.condition });
const res: AxiosResponse = await graphql.query("queryEvents").params({
condition: {
...this.condition,
time: useAppStoreWithOut().durationTime,
},
});
this.loading = false;
if (res.data.errors) {
return res.data;
@@ -115,7 +115,6 @@ export const eventStore = defineStore({
return item;
}
);
this.total = res.data.data.fetchEvents.total;
}
return res.data;
},

View File

@@ -15,7 +15,6 @@
* limitations under the License.
*/
import { defineStore } from "pinia";
import { Duration } from "@/types/app";
import { Instance, Endpoint, Service } from "@/types/selector";
import { store } from "@/store";
import graphql from "@/graphql";
@@ -29,11 +28,9 @@ interface LogState {
instances: Instance[];
endpoints: Endpoint[];
conditions: any;
durationTime: Duration;
selectorStore: any;
supportQueryLogsByKeywords: boolean;
logs: any[];
logsTotal: number;
loadLogs: boolean;
}
@@ -45,13 +42,11 @@ export const logStore = defineStore({
endpoints: [{ value: "0", label: "All" }],
conditions: {
queryDuration: useAppStoreWithOut().durationTime,
paging: { pageNum: 1, pageSize: 15, needTotal: true },
paging: { pageNum: 1, pageSize: 15 },
},
supportQueryLogsByKeywords: true,
durationTime: useAppStoreWithOut().durationTime,
selectorStore: useSelectorStore(),
logs: [],
logsTotal: 0,
loadLogs: false,
}),
actions: {
@@ -74,7 +69,7 @@ export const logStore = defineStore({
: id;
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
});
if (res.data.errors) {
@@ -92,7 +87,7 @@ export const logStore = defineStore({
: id;
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
});
if (res.data.errors) {
@@ -134,7 +129,6 @@ export const logStore = defineStore({
}
this.logs = res.data.data.queryLogs.logs;
this.logsTotal = res.data.data.queryLogs.total;
return res.data;
},
async getBrowserLogs() {
@@ -148,7 +142,20 @@ export const logStore = defineStore({
return res.data;
}
this.logs = res.data.data.queryBrowserErrorLogs.logs;
this.logsTotal = res.data.data.queryBrowserErrorLogs.total;
return res.data;
},
async getLogTagKeys() {
const res: AxiosResponse = await graphql
.query("queryLogTagKeys")
.params({ duration: useAppStoreWithOut().durationTime });
return res.data;
},
async getLogTagValues(tagKey: string) {
const res: AxiosResponse = await graphql
.query("queryLogTagValues")
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
return res.data;
},
},

View File

@@ -15,8 +15,7 @@
* limitations under the License.
*/
import { defineStore } from "pinia";
import { Duration } from "@/types/app";
import { Service } from "@/types/selector";
import { Endpoint } from "@/types/selector";
import {
TaskListItem,
SegmentSpan,
@@ -31,8 +30,8 @@ import { AxiosResponse } from "axios";
import { useAppStoreWithOut } from "@/store/modules/app";
interface ProfileState {
services: Service[];
durationTime: Duration;
endpoints: Endpoint[];
taskEndpoints: Endpoint[];
condition: { serviceId: string; endpointName: string };
taskList: TaskListItem[];
segmentList: Trace[];
@@ -47,8 +46,8 @@ interface ProfileState {
export const profileStore = defineStore({
id: "profile",
state: (): ProfileState => ({
services: [{ value: "0", label: "All" }],
durationTime: useAppStoreWithOut().durationTime,
endpoints: [{ value: "", label: "All" }],
taskEndpoints: [{ value: "", label: "All" }],
condition: { serviceId: "", endpointName: "" },
taskList: [],
segmentList: [],
@@ -75,14 +74,28 @@ export const profileStore = defineStore({
setHighlightTop() {
this.highlightTop = !this.highlightTop;
},
async getServices(layer: string) {
const res: AxiosResponse = await graphql.query("queryServices").params({
layer,
async getEndpoints(serviceId: string, keyword?: string) {
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
});
if (res.data.errors) {
return res.data;
}
this.services = res.data.data.services;
this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTaskEndpoints(serviceId: string, keyword?: string) {
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
});
if (res.data.errors) {
return res.data;
}
this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTaskList() {
@@ -106,6 +119,9 @@ export const profileStore = defineStore({
return res.data;
},
async getSegmentList(params: { taskID: string }) {
if (!params.taskID) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getProfileTaskSegmentList")
.params(params);
@@ -132,6 +148,9 @@ export const profileStore = defineStore({
return res.data;
},
async getSegmentSpans(params: { segmentId: string }) {
if (!params.segmentId) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("queryProfileSegment")
.params(params);
@@ -145,7 +164,13 @@ export const profileStore = defineStore({
this.analyzeTrees = [];
return res.data;
}
this.segmentSpans = segment.spans;
this.segmentSpans = segment.spans.map((d: SegmentSpan) => {
return {
...d,
segmentId: this.currentSegment.segmentId,
traceId: this.currentSegment.traceIds[0],
};
});
if (!(segment.spans && segment.spans.length)) {
this.analyzeTrees = [];
return res.data;
@@ -158,6 +183,12 @@ export const profileStore = defineStore({
segmentId: string;
timeRanges: Array<{ start: number; end: number }>;
}) {
if (!params.segmentId) {
return new Promise((resolve) => resolve({}));
}
if (!params.timeRanges.length) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getProfileAnalyze")
.params(params);

View File

@@ -15,13 +15,11 @@
* limitations under the License.
*/
import { defineStore } from "pinia";
import { Duration } from "@/types/app";
import { Service, Instance, Endpoint } from "@/types/selector";
import { store } from "@/store";
import graphql from "@/graphql";
import { AxiosResponse } from "axios";
import { useAppStoreWithOut } from "@/store/modules/app";
interface SelectorState {
services: Service[];
destServices: Service[];
@@ -31,7 +29,6 @@ interface SelectorState {
currentDestService: Nullable<Service>;
currentDestPod: Nullable<Instance | Endpoint>;
destPods: Array<Instance | Endpoint>;
durationTime: Duration;
}
export const selectorStore = defineStore({
@@ -45,7 +42,6 @@ export const selectorStore = defineStore({
currentPod: null,
currentDestService: null,
currentDestPod: null,
durationTime: useAppStoreWithOut().durationTime,
}),
actions: {
setCurrentService(service: Nullable<Service>) {
@@ -86,7 +82,7 @@ export const selectorStore = defineStore({
}
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
});
if (!res.data.errors) {
if (param && param.isRelation) {
@@ -112,7 +108,7 @@ export const selectorStore = defineStore({
}
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
keyword: params.keyword || "",
limit: params.limit,
});

View File

@@ -75,7 +75,7 @@ export const topologyStore = defineStore({
setTopology(data: { nodes: Node[]; calls: Call[] }) {
const obj = {} as any;
const services = useSelectorStore().services;
const nodes = data.nodes.reduce((prev: Node[], next: Node) => {
const nodes = (data.nodes || []).reduce((prev: Node[], next: Node) => {
if (!obj[next.id]) {
obj[next.id] = true;
const s = services.filter((d: Service) => d.id === next.id)[0] || {};
@@ -84,7 +84,7 @@ export const topologyStore = defineStore({
}
return prev;
}, []);
const calls = data.calls.reduce((prev: Call[], next: Call) => {
const calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
@@ -117,7 +117,7 @@ export const topologyStore = defineStore({
async getDepthServiceTopology(serviceIds: string[], depth: number) {
const res = await this.getServicesTopology(serviceIds);
if (depth > 1) {
const ids = res.nodes
const ids = (res.nodes || [])
.map((item: Node) => item.id)
.filter((d: string) => !serviceIds.includes(d));
if (!ids.length) {
@@ -410,6 +410,9 @@ export const topologyStore = defineStore({
const idsC = this.calls
.filter((i: Call) => i.detectPoints.includes("CLIENT"))
.map((b: Call) => b.id);
if (!idsC.length) {
return;
}
const param = await useQueryTopologyMetrics(linkClientMetrics, idsC);
const res = await this.getCallClientMetrics(param);
@@ -425,6 +428,9 @@ export const topologyStore = defineStore({
const idsS = this.calls
.filter((i: Call) => i.detectPoints.includes("SERVER"))
.map((b: Call) => b.id);
if (!idsS.length) {
return;
}
const param = await useQueryTopologyMetrics(linkServerMetrics, idsS);
const res = await this.getCallServerMetrics(param);
@@ -438,6 +444,9 @@ export const topologyStore = defineStore({
return;
}
const ids = this.nodes.map((d: Node) => d.id);
if (!ids.length) {
return;
}
const param = await useQueryTopologyMetrics(nodeMetrics, ids);
const res = await this.getNodeMetricValue(param);

View File

@@ -15,7 +15,6 @@
* limitations under the License.
*/
import { defineStore } from "pinia";
import { Duration } from "@/types/app";
import { Instance, Endpoint, Service } from "@/types/selector";
import { Trace, Span } from "@/types/trace";
import { store } from "@/store";
@@ -29,16 +28,10 @@ interface TraceState {
instances: Instance[];
endpoints: Endpoint[];
traceList: Trace[];
traceTotal: number;
traceSpans: Span[];
currentTrace: Trace | any;
conditions: any;
traceSpanLogs: any[];
traceSpanLogsTotal: number;
// traceListErrors: string;
// traceSpanErrors: string;
// traceSpanLogErrors: string;
durationTime: Duration;
selectorStore: any;
}
@@ -50,22 +43,19 @@ export const traceStore = defineStore({
endpoints: [{ value: "0", label: "All" }],
traceList: [],
traceSpans: [],
traceTotal: 0,
currentTrace: {},
conditions: {
queryDuration: useAppStoreWithOut().durationTime,
traceState: "ALL",
queryOrder: "BY_START_TIME",
paging: { pageNum: 1, pageSize: 15, needTotal: true },
paging: { pageNum: 1, pageSize: 20 },
},
traceSpanLogs: [],
traceSpanLogsTotal: 0,
durationTime: useAppStoreWithOut().durationTime,
selectorStore: useSelectorStore(),
}),
actions: {
setTraceCondition(data: any) {
this.condition = { ...this.condition, ...data };
this.conditions = { ...this.conditions, ...data };
},
setCurrentTrace(trace: Trace) {
this.currentTrace = trace;
@@ -89,16 +79,13 @@ export const traceStore = defineStore({
: id;
const res: AxiosResponse = await graphql.query("queryInstances").params({
serviceId: serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
});
if (res.data.errors) {
return res.data;
}
this.instances = [
{ value: "0", label: "All" },
...res.data.data.pods,
] || [{ value: " 0", label: "All" }];
this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getEndpoints(id: string, keyword?: string) {
@@ -107,27 +94,23 @@ export const traceStore = defineStore({
: id;
const res: AxiosResponse = await graphql.query("queryEndpoints").params({
serviceId,
duration: this.durationTime,
duration: useAppStoreWithOut().durationTime,
keyword: keyword || "",
});
if (res.data.errors) {
return res.data;
}
this.endpoints = [
{ value: "0", label: "All" },
...res.data.data.pods,
] || [{ value: "0", label: "All" }];
this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTraces() {
const res: AxiosResponse = await graphql
.query("queryTraces")
.params({ condition: this.condition });
.params({ condition: this.conditions });
if (res.data.errors) {
return res.data;
}
if (!res.data.data.data.traces.length) {
this.traceTotal = 0;
this.traceList = [];
this.setCurrentTrace({});
this.setTraceSpans([]);
@@ -140,7 +123,6 @@ export const traceStore = defineStore({
});
return d;
});
this.traceTotal = res.data.data.data.total;
this.setCurrentTrace(res.data.data.data.traces[0] || {});
return res.data;
},
@@ -160,11 +142,23 @@ export const traceStore = defineStore({
.params(params);
if (res.data.errors) {
this.traceSpanLogs = [];
this.traceSpanLogsTotal = 0;
return res.data;
}
this.traceSpanLogs = res.data.data.queryLogs.logs || [];
this.traceSpanLogsTotal = res.data.data.queryLogs.total;
return res.data;
},
async getTagKeys() {
const res: AxiosResponse = await graphql
.query("queryTraceTagKeys")
.params({ duration: useAppStoreWithOut().durationTime });
return res.data;
},
async getTagValues(tagKey: string) {
const res: AxiosResponse = await graphql
.query("queryTraceTagValues")
.params({ tagKey, duration: useAppStoreWithOut().durationTime });
return res.data;
},
},

View File

@@ -15,10 +15,7 @@
* limitations under the License.
*/
.show-xs,
.show-sm,
.show-md,
.show-lg {
.show-xs {
display: none !important;
}
@media (max-width: 767px) {
@@ -30,145 +27,6 @@
display: none !important;
}
}
@media (min-width: 768px) and (max-width: 1023px) {
.show-sm {
display: block !important;
}
.hide-sm {
display: none !important;
}
}
@media (min-width: 1024px) and (max-width: 1279px) {
.show-md {
display: block !important;
}
.hide-md {
display: none !important;
}
}
@media (min-width: 1280px) {
.show-lg {
display: block !important;
}
.hide-lg {
display: none !important;
}
}
.g-xs-1,
.g-xs-2,
.g-xs-3,
.g-xs-4,
.g-xs-5,
.g-xs-6,
.g-xs-7,
.g-xs-8,
.g-xs-9,
.g-xs-10,
.g-xs-11,
.g-xs-12 {
float: left;
min-height: 1px;
}
.g-xs-12 {
width: 100%;
}
.g-xs-11 {
width: 91.666%;
}
.g-xs-10 {
width: 83.333%;
}
.g-xs-9 {
width: 75%;
}
.g-xs-8 {
width: 66.666%;
}
.g-xs-7 {
width: 58.333%;
}
.g-xs-6 {
width: 50%;
}
.g-xs-5 {
width: 41.666%;
}
.g-xs-4 {
width: 33.333%;
}
.g-xs-3 {
width: 25%;
}
.g-xs-2 {
width: 16.666%;
}
.g-xs-1 {
width: 8.333%;
}
.g-xs-space-12 {
margin-left: 100%;
}
.g-xs-space-11 {
margin-left: 91.666%;
}
.g-xs-space-10 {
margin-left: 83.333%;
}
.g-xs-space-9 {
margin-left: 75%;
}
.g-xs-space-8 {
margin-left: 66.666%;
}
.g-xs-space-7 {
margin-left: 58.333%;
}
.g-xs-space-6 {
margin-left: 50%;
}
.g-xs-space-5 {
margin-left: 41.666%;
}
.g-xs-space-4 {
margin-left: 33.333%;
}
.g-xs-space-3 {
margin-left: 25%;
}
.g-xs-space-2 {
margin-left: 16.666%;
}
.g-xs-space-1 {
margin-left: 8.333%;
}
@media (min-width: 768px) {
.g-sm-1,
.g-sm-2,
@@ -233,278 +91,4 @@
.g-sm-1 {
width: 8.333%;
}
.g-sm-space-12 {
margin-left: 100%;
}
.g-sm-space-11 {
margin-left: 91.666%;
}
.g-sm-space-10 {
margin-left: 83.333%;
}
.g-sm-space-9 {
margin-left: 75%;
}
.g-sm-space-8 {
margin-left: 66.666%;
}
.g-sm-space-7 {
margin-left: 58.333%;
}
.g-sm-space-6 {
margin-left: 50%;
}
.g-sm-space-5 {
margin-left: 41.666%;
}
.g-sm-space-4 {
margin-left: 33.333%;
}
.g-sm-space-3 {
margin-left: 25%;
}
.g-sm-space-2 {
margin-left: 16.666%;
}
.g-sm-space-1 {
margin-left: 8.333%;
}
}
@media (min-width: 1024px) {
.g-md-1,
.g-md-2,
.g-md-3,
.g-md-4,
.g-md-5,
.g-md-6,
.g-md-7,
.g-md-8,
.g-md-9,
.g-md-10,
.g-md-11,
.g-md-12 {
float: left;
min-height: 1px;
}
.g-md-12 {
width: 100%;
}
.g-md-11 {
width: 91.666%;
}
.g-md-10 {
width: 83.333%;
}
.g-md-9 {
width: 75%;
}
.g-md-8 {
width: 66.666%;
}
.g-md-7 {
width: 58.333%;
}
.g-md-6 {
width: 50%;
}
.g-md-5 {
width: 41.666%;
}
.g-md-4 {
width: 33.333%;
}
.g-md-3 {
width: 25%;
}
.g-md-2 {
width: 16.666%;
}
.g-md-1 {
width: 8.333%;
}
.g-md-space-12 {
margin-left: 100%;
}
.g-md-space-11 {
margin-left: 91.666%;
}
.g-md-space-10 {
margin-left: 83.333%;
}
.g-md-space-9 {
margin-left: 75%;
}
.g-md-space-8 {
margin-left: 66.666%;
}
.g-md-space-7 {
margin-left: 58.333%;
}
.g-md-space-6 {
margin-left: 50%;
}
.g-md-space-5 {
margin-left: 41.666%;
}
.g-md-space-4 {
margin-left: 33.333%;
}
.g-md-space-3 {
margin-left: 25%;
}
.g-md-space-2 {
margin-left: 16.666%;
}
.g-md-space-1 {
margin-left: 8.333%;
}
}
@media (min-width: 1280px) {
.g-lg-1,
.g-lg-2,
.g-lg-3,
.g-lg-4,
.g-lg-5,
.g-lg-6,
.g-lg-7,
.g-lg-8,
.g-lg-9,
.g-lg-10,
.g-lg-11,
.g-lg-12 {
float: left;
min-height: 1px;
}
.g-lg-12 {
width: 100%;
}
.g-lg-11 {
width: 91.666%;
}
.g-lg-10 {
width: 83.333%;
}
.g-lg-9 {
width: 75%;
}
.g-lg-8 {
width: 66.666%;
}
.g-lg-7 {
width: 58.333%;
}
.g-lg-6 {
width: 50%;
}
.g-lg-5 {
width: 41.666%;
}
.g-lg-4 {
width: 33.333%;
}
.g-lg-3 {
width: 25%;
}
.g-lg-2 {
width: 16.666%;
}
.g-lg-1 {
width: 8.333%;
}
.g-lg-space-12 {
margin-left: 100%;
}
.g-lg-space-11 {
margin-left: 91.666%;
}
.g-lg-space-10 {
margin-left: 83.333%;
}
.g-lg-space-9 {
margin-left: 75%;
}
.g-lg-space-8 {
margin-left: 66.666%;
}
.g-lg-space-7 {
margin-left: 58.333%;
}
.g-lg-space-6 {
margin-left: 50%;
}
.g-lg-space-5 {
margin-left: 41.666%;
}
.g-lg-space-4 {
margin-left: 33.333%;
}
.g-lg-space-3 {
margin-left: 25%;
}
.g-lg-space-2 {
margin-left: 16.666%;
}
.g-lg-space-1 {
margin-left: 8.333%;
}
}

22
src/styles/index.ts Normal file
View File

@@ -0,0 +1,22 @@
/**
* 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
*
* http://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 "element-plus/es/components/message/style/css";
import "element-plus/es/components/message-box/style/css";
import "element-plus/es/components/notification/style/css";
import "./grid.scss";
import "./lib.scss";
import "./reset.scss";

View File

@@ -171,3 +171,21 @@
color: #ddd;
}
}
.scroll_bar_style::-webkit-scrollbar {
width: 9px;
height: 4px;
background-color: #eee;
}
.scroll_bar_style::-webkit-scrollbar-track {
background-color: #eee;
border-radius: 3px;
box-shadow: inset 0 0 6px #ccc;
}
.scroll_bar_style::-webkit-scrollbar-thumb {
border-radius: 3px;
box-shadow: inset 0 0 6px #ccc;
background-color: #aaa;
}

View File

@@ -29,6 +29,7 @@ body {
html,
body {
height: 100%;
overflow-y: hidden;
}
div,

4
src/types/app.d.ts vendored
View File

@@ -28,3 +28,7 @@ export interface DurationTime {
end: string;
step: string;
}
export type Paging = {
pageNum: number;
pageSize: number;
};

52
src/types/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,52 @@
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const EffectScope: typeof import('vue')['EffectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
}
export {}

47
src/types/components.d.ts vendored Normal file
View File

@@ -0,0 +1,47 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module '@vue/runtime-core' {
export interface GlobalComponents {
DateCalendar: typeof import('./../components/DateCalendar.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
Graph: typeof import('./../components/Graph.vue')['default']
Icon: typeof import('./../components/Icon.vue')['default']
Loading: typeof import('element-plus/es')['ElLoadingDirective']
Radio: typeof import('./../components/Radio.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Selector: typeof import('./../components/Selector.vue')['default']
SelectSingle: typeof import('./../components/SelectSingle.vue')['default']
TimePicker: typeof import('./../components/TimePicker.vue')['default']
}
}
export { }

View File

@@ -39,9 +39,9 @@ export interface LayoutConfig {
}
export type MetricConfigOpt = {
unit: string;
label: string;
calculation: string;
unit?: string;
label?: string;
calculation?: string;
labelsIndex: string;
sortOrder: string;
topN?: number;
@@ -73,6 +73,7 @@ export interface LineConfig extends AreaConfig {
showXAxis?: boolean;
showYAxis?: boolean;
smallTips?: boolean;
showlabels?: boolean;
}
export interface AreaConfig {

31
src/types/demand-log.ts Normal file
View File

@@ -0,0 +1,31 @@
/**
* 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
*
* http://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 { DurationTime } from "./app";
export interface Conditions {
container: string;
serviceInstanceId: string;
duration: DurationTime;
keywordsOfContent?: string[];
excludingKeywordsOfContent?: string;
}
export interface Log {
content: string;
timestamp: number;
contentType: string;
}

77
src/types/ebpf.d.ts vendored Normal file
View File

@@ -0,0 +1,77 @@
/**
* 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
*
* http://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.
*/
export interface EBPFTaskCreationRequest {
serviceId: string;
processLabels: string[];
startTime: number;
duration: number;
targetType: string;
}
export interface EBPFTaskList {
taskId: string;
serviceName: string;
serviceId: string;
processLabels: string[];
taskStartTime: number;
fixedTriggerDuration: number;
targetType: string;
createTime: number;
triggerType: string;
}
export interface EBPFProfilingSchedule {
scheduleId: string;
taskId: string;
process: Process;
endTime: number;
startTime: number;
}
export type Process = {
id: string;
name: string;
serviceId: string;
serviceName: string;
instanceId: string;
instanceName: string;
agentId: string;
detectType: string;
attributes: { name: string; value: string }[];
labels: string[];
};
export type StackElement = {
id: string;
originId: string;
name: string;
parentId: string;
symbol: string;
dumpCount: number;
stackType: string;
value: number;
children?: StackElement[];
rateOfRoot?: string;
rateOfParent: string;
};
export type AnalyzationTrees = {
id: string;
parentId: string;
symbol: string;
dumpCount: number;
stackType: string;
};

View File

@@ -14,6 +14,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import "./grid.scss";
@import "./lib.scss";
@import "./reset.scss";
declare module "monaco-editor";

View File

@@ -26,7 +26,6 @@ export type Service = {
export type Instance = {
value: string;
label: string;
layer?: string;
language?: string;
instanceUUID?: string;
attributes?: { name: string; value: string }[];

View File

@@ -46,8 +46,15 @@ export interface Span {
children?: Span[];
tags?: Array<Map<string, string>>;
logs?: log[];
parentSegmentId?: string;
refs?: Ref[];
}
export type Ref = {
type: string;
parentSegmentId: string;
parentSpanId: number;
traceId: string;
};
export interface log {
time: number;
data: Map<string, string>;

View File

@@ -77,16 +77,15 @@ import { Languages } from "@/constants/data";
import Selector from "@/components/Selector.vue";
const { t, locale } = useI18n();
const appStore = useAppStoreWithOut();
const state = reactive<{ timer: ReturnType<typeof setInterval> | null }>({
timer: null,
});
const lang = ref<string>(locale.value || "en");
const autoTime = ref<number>(6);
const auto = ref<boolean>(false);
const appStore = useAppStoreWithOut();
const utcArr = appStore.utc.split(":");
const utcHour = ref<number>(isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]));
const utcMin = ref<number>(isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]));
const auto = ref<boolean>(appStore.autoRefresh || false);
const utcHour = ref<number>(appStore.utcHour);
const utcMin = ref<number>(appStore.utcMin);
appStore.setPageTitle("Setting");
const handleReload = () => {
@@ -195,10 +194,11 @@ const setUTCMin = () => {
}
.label {
width: 160px;
width: 180px;
display: inline-block;
font-weight: 500;
color: #000;
line-height: 25px;
}
}
</style>

View File

@@ -29,6 +29,7 @@ limitations under the License. -->
<div class="mr-10 ml-10">
<span class="grey">{{ t("searchKeyword") }}: </span>
<el-input
size="small"
v-model="keyword"
class="alarm-tool-input"
@change="refreshAlarms({ pageNum: 1 })"
@@ -38,8 +39,8 @@ limitations under the License. -->
<el-pagination
v-model:currentPage="pageNum"
v-model:page-size="pageSize"
layout="prev, jumper, total, next"
:total="alarmStore.total"
layout="prev, pager, next"
:total="total"
@current-change="changePage"
:pager-count="5"
small
@@ -54,7 +55,7 @@ limitations under the License. -->
</nav>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { ref, computed } from "vue";
import { useI18n } from "vue-i18n";
import ConditionTags from "@/views/components/ConditionTags.vue";
import { AlarmOptions } from "./data";
@@ -69,6 +70,11 @@ const pageSize = 20;
const entity = ref<string>("");
const keyword = ref<string>("");
const pageNum = ref<number>(1);
const total = computed(() =>
alarmStore.alarms.length === pageSize
? pageSize * pageNum.value + 1
: pageSize * pageNum.value
);
refreshAlarms({ pageNum: 1 });
@@ -78,7 +84,6 @@ async function refreshAlarms(param: { pageNum: number; tagsMap?: any }) {
paging: {
pageNum: param.pageNum,
pageSize,
needTotal: true,
},
tags: param.tagsMap,
};

View File

@@ -13,54 +13,99 @@ 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. -->
<template>
<div class="flex-h" :class="{ light: theme === 'light' }">
<div class="mr-5">
<span class="sm grey" v-show="theme === 'dark'">{{ t("tags") }}: </span>
<span
class="trace-tags"
:style="type === 'LOG' ? `min-width: 122px;` : ''"
>
<span class="selected" v-for="(item, index) in tagsList" :key="index">
<span>{{ item }}</span>
<span class="remove-icon" @click="removeTags(index)">×</span>
</span>
<div>
<span class="grey">{{ t("tags") }}: </span>
<span
v-if="tagsList.length"
class="trace-tags"
:style="type === 'LOG' ? `min-width: 122px;` : ''"
>
<span class="selected" v-for="(item, index) in tagsList" :key="index">
<span>{{ item }}</span>
<span class="remove-icon" @click="removeTags(index)">×</span>
</span>
</span>
<el-input
v-if="type === 'ALARM'"
size="small"
v-model="tags"
class="trace-new-tag"
@change="addLabels"
:placeholder="t('addTags')"
/>
<span v-else>
<el-input
size="small"
v-model="tags"
class="trace-new-tag"
@change="addLabels"
:placeholder="t('addTags')"
@click="showClick"
/>
<span class="tags-tip">
<a
target="blank"
href="https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/configuration-vocabulary.md"
>
{{ t("tagsLink") }}
</a>
<el-tooltip :content="t('traceTagsTip')">
<span>
<Icon class="icon-help mr-5" iconName="help" size="middle" />
</span>
</el-tooltip>
<b v-if="type !== 'LOG'">{{ t("noticeTag") }}</b>
</span>
</div>
<el-dropdown
ref="dropdownTag"
trigger="contextmenu"
:hide-on-click="false"
style="margin: 20px 0 0 -130px"
v-if="tagArr.length"
>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="(item, index) in tagArr" :key="index">
<span @click="selectTag(item)" class="tag-item">
{{ item }}
</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
<span
class="tags-tip"
:class="type !== 'ALARM' && tagArr.length ? 'link-tips' : ''"
>
<a
target="blank"
href="https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/configuration-vocabulary.md"
>
{{ t("tagsLink") }}
</a>
<el-tooltip :content="t(tipsMap[type])">
<span>
<Icon class="icon-help mr-5" iconName="help" size="middle" />
</span>
</el-tooltip>
<b v-if="type === 'AL'">{{ t("noticeTag") }}</b>
</span>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useTraceStore } from "@/store/modules/trace";
import { useLogStore } from "@/store/modules/log";
import { ElMessage } from "element-plus";
import { useAppStoreWithOut } from "@/store/modules/app";
/*global defineEmits, defineProps */
/*global Nullable, defineEmits, defineProps */
const emit = defineEmits(["update"]);
defineProps({
const props = defineProps({
type: { type: String, default: "TRACE" },
});
const traceStore = useTraceStore();
const logStore = useLogStore();
const appStore = useAppStoreWithOut();
const { t } = useI18n();
const theme = ref<string>("dark");
const tags = ref<string>("");
const tagsList = ref<string[]>([]);
const tagArr = ref<string[]>([]);
const tagKeys = ref<string[]>([]);
const tipsMap = {
LOG: "logTagsTip",
TRACE: "traceTagsTip",
ALARM: "alarmTagsTip",
};
const dropdownTag = ref<Nullable<any>>(null);
fetchTagKeys();
function removeTags(index: number) {
tagsList.value.splice(index, 1);
@@ -84,6 +129,61 @@ function updateTags() {
});
emit("update", { tagsMap, tagsList: tagsList.value });
}
async function fetchTagKeys() {
let resp: any = {};
if (props.type === "TRACE") {
resp = await traceStore.getTagKeys();
} else {
resp = await logStore.getLogTagKeys();
}
if (resp.errors) {
ElMessage.error(resp.errors);
return;
}
tagArr.value = resp.data.tagKeys;
tagKeys.value = resp.data.tagKeys;
}
async function fetchTagValues() {
const param = tags.value.split("=")[0];
let resp: any = {};
if (props.type === "TRACE") {
resp = await traceStore.getTagValues(param);
} else {
resp = await logStore.getLogTagValues(param);
}
if (resp.errors) {
ElMessage.error(resp.errors);
return;
}
tagArr.value = resp.data.tagValues;
}
function selectTag(item: string) {
if (tags.value.includes("=")) {
tags.value += item;
addLabels();
tagArr.value = tagKeys.value;
dropdownTag.value.handleClose();
return;
}
tags.value = item + "=";
fetchTagValues();
}
function showClick() {
if (dropdownTag.value) {
dropdownTag.value.handleOpen();
}
}
watch(
() => appStore.durationTime,
() => {
fetchTagKeys();
}
);
</script>
<style lang="scss" scoped>
.trace-tags {
@@ -110,7 +210,6 @@ function updateTags() {
padding: 2px 5px;
border-radius: 3px;
width: 250px;
margin-right: 3px;
}
.remove-icon {
@@ -119,10 +218,20 @@ function updateTags() {
cursor: pointer;
}
.tag-item {
display: inline-block;
min-width: 210px;
}
.tags-tip {
color: #a7aebb;
}
.link-tips {
display: inline-block;
margin-left: 130px;
}
.light {
color: #3d444f;

View File

@@ -28,7 +28,7 @@ limitations under the License. -->
</el-button>
</template>
</el-input>
<el-button class="ml-10" size="small" @click="reloadTemplates">
<el-button class="ml-10 reload-btn" size="small" @click="reloadTemplates">
<Icon size="sm" iconName="retry" class="reload" />
{{ t("reloadDashboards") }}
</el-button>
@@ -44,9 +44,10 @@ limitations under the License. -->
:style="{ fontSize: '13px', width: '100%' }"
v-loading="loading"
ref="multipleTableRef"
:default-sort="{ prop: 'name' }"
:default-sort="{ prop: 'name', order: 'ascending' }"
@selection-change="handleSelectionChange"
height="637px"
size="small"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="Name">
@@ -145,9 +146,10 @@ import type { ElTable } from "element-plus";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useDashboardStore } from "@/store/modules/dashboard";
import router from "@/router";
import { DashboardItem } from "@/types/dashboard";
import { DashboardItem, LayoutConfig } from "@/types/dashboard";
import { saveFile, readFile } from "@/utils/file";
import { EntityType } from "./data";
import { isEmptyObject } from "@/utils/is";
/*global Nullable*/
const { t } = useI18n();
@@ -220,12 +222,77 @@ function exportTemplates() {
const layout = JSON.parse(sessionStorage.getItem(key) || "{}");
return layout;
});
for (const item of templates) {
optimizeTemplate(item.configuration.children);
}
const name = `dashboards.json`;
saveFile(templates, name);
setTimeout(() => {
multipleTableRef.value!.clearSelection();
}, 2000);
}
function optimizeTemplate(
children: (LayoutConfig & { moved?: boolean; standard?: unknown })[]
) {
for (const child of children || []) {
delete child.moved;
delete child.activedTabIndex;
delete child.standard;
if (isEmptyObject(child.graph)) {
delete child.graph;
}
if (child.widget) {
if (child.widget.title === "") {
delete child.widget.title;
}
if (child.widget.tips === "") {
delete child.widget.tips;
}
}
if (isEmptyObject(child.widget)) {
delete child.widget;
}
if (!(child.metrics && child.metrics.length && child.metrics[0])) {
delete child.metrics;
}
if (
!(child.metricTypes && child.metricTypes.length && child.metricTypes[0])
) {
delete child.metricTypes;
}
if (child.metricConfig && child.metricConfig.length) {
child.metricConfig.forEach((c, index) => {
if (!c.calculation) {
delete c.calculation;
}
if (!c.unit) {
delete c.unit;
}
if (!c.label) {
delete c.label;
}
if (isEmptyObject(c)) {
(child.metricConfig || []).splice(index, 1);
}
});
}
if (!(child.metricConfig && child.metricConfig.length)) {
delete child.metricConfig;
}
if (child.type === "Tab") {
for (const item of child.children || []) {
optimizeTemplate(item.children);
}
}
if (
["Trace", "Topology", "Tab", "Profile", "Ebpf", "Log"].includes(
child.type
)
) {
delete child.widget;
}
}
}
function handleEdit(row: DashboardItem) {
dashboardStore.setMode(true);
dashboardStore.setEntity(row.entity);
@@ -457,4 +524,9 @@ function changePage(pageIndex: number) {
.reload {
margin-right: 3px;
}
.reload-btn {
display: inline-block;
margin-left: 10px;
}
</style>

View File

@@ -41,7 +41,6 @@ import { useDashboardStore } from "@/store/modules/dashboard";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const { selectedGrid } = dashboardStore;
const widget = dashboardStore.selectedGrid.widget || {};
const title = ref<string>(widget.title || "");
const tips = ref<string>(widget.tips || "");
@@ -51,6 +50,7 @@ function updateWidgetConfig(param: { [key: string]: string }) {
if (!key) {
return;
}
const { selectedGrid } = dashboardStore;
const widget = {
...dashboardStore.selectedGrid.widget,
[key]: decodeURIComponent(param[key]),

View File

@@ -203,16 +203,13 @@ async function setMetricType(chart?: any) {
}
);
const metrics: any = states.metricList.filter(
(d: { value: string; type: string }) => {
const index = states.metrics.findIndex((m: string) => m === d.value);
if (index > -1) {
return d;
}
}
(d: { value: string; type: string }) => states.metrics.includes(d.value)
);
if (metrics.length) {
states.metrics = metrics.map((d: { value: string }) => d.value);
// keep states.metrics index
const m = metrics.map((d: { value: string }) => d.value);
states.metrics = states.metrics.filter((d) => m.includes(d));
} else {
states.metrics = [""];
states.metricTypes = [""];
@@ -466,7 +463,7 @@ function setMetricConfig(index: number) {
.chart-types {
span {
display: inline-block;
padding: 5px 10px;
padding: 2px 10px;
border: 1px solid #ccc;
background-color: #fff;
border-right: 0;

View File

@@ -0,0 +1,93 @@
<!-- 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
http://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. -->
<template>
<div class="log-wrapper flex-v">
<el-popover
placement="bottom"
trigger="click"
:width="100"
v-if="dashboardStore.editMode"
>
<template #reference>
<span class="delete cp">
<Icon iconName="ellipsis_v" size="middle" class="operation" />
</span>
</template>
<div class="tools" @click="removeWidget">
<span>{{ t("delete") }}</span>
</div>
</el-popover>
<div class="header">
<Header />
</div>
<Content />
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import Header from "../related/demand-log/Header.vue";
import Content from "../related/demand-log/Content.vue";
/*global defineProps */
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
activeIndex: { type: String, default: "" },
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
function removeWidget() {
dashboardStore.removeControls(props.data);
}
</script>
<style lang="scss" scoped>
.log-wrapper {
width: 100%;
height: 100%;
font-size: 12px;
position: relative;
overflow: auto;
}
.delete {
position: absolute;
top: 5px;
right: 3px;
}
.header {
padding: 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
min-width: 1024px;
}
.tools {
padding: 5px 0;
color: #999;
cursor: pointer;
position: relative;
text-align: center;
&:hover {
color: #409eff;
background-color: #eee;
}
}
</style>

View File

@@ -0,0 +1,95 @@
<!-- 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
http://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. -->
<template>
<div class="profile-wrapper flex-v">
<el-popover
placement="bottom"
trigger="click"
:width="100"
v-if="dashboardStore.editMode"
>
<template #reference>
<span class="delete cp">
<Icon iconName="ellipsis_v" size="middle" class="operation" />
</span>
</template>
<div class="tools" @click="removeWidget">
<span>{{ t("delete") }}</span>
</div>
</el-popover>
<Header />
<Content />
</div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import Header from "../related/ebpf/Header.vue";
import Content from "../related/ebpf/Content.vue";
/*global defineProps */
const props = defineProps({
data: {
type: Object as PropType<any>,
default: () => ({ graph: {} }),
},
activeIndex: { type: String, default: "" },
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
function removeWidget() {
dashboardStore.removeControls(props.data);
}
</script>
<style lang="scss" scoped>
.profile-wrapper {
width: 100%;
height: 100%;
font-size: 12px;
position: relative;
}
.delete {
position: absolute;
top: 5px;
right: 3px;
}
.header {
padding: 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
}
.tools {
padding: 5px 0;
color: #999;
cursor: pointer;
position: relative;
text-align: center;
&:hover {
color: #409eff;
background-color: #eee;
}
}
.trace {
width: 100%;
overflow: auto;
}
</style>

View File

@@ -64,6 +64,7 @@ function removeWidget() {
height: 100%;
font-size: 12px;
position: relative;
overflow: auto;
}
.delete {
@@ -76,6 +77,7 @@ function removeWidget() {
padding: 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
min-width: 1024px;
}
.tools {
@@ -93,6 +95,5 @@ function removeWidget() {
.log {
width: 100%;
overflow: auto;
}
</style>

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="flex-h tab-header">
<div class="tabs">
<div class="tabs scroll_bar_style" @click="handleClick">
<span
v-for="(child, idx) in data.children || []"
:key="idx"
@@ -26,15 +26,15 @@ limitations under the License. -->
v-model="child.name"
placeholder="Please input"
class="tab-name"
:readonly="isNaN(editTabIndex)"
:class="{ view: isNaN(editTabIndex) }"
:readonly="isNaN(editTabIndex) && !canEditTabName"
:class="{ view: !canEditTabName }"
/>
<Icon
v-show="activeTabIndex === idx"
size="sm"
iconName="cancel"
@click="deleteTabItem($event, idx)"
v-if="dashboardStore.editMode"
v-if="dashboardStore.editMode && canEditTabName"
/>
</span>
<span class="tab-icons" v-if="dashboardStore.editMode">
@@ -46,35 +46,21 @@ limitations under the License. -->
</span>
</div>
<div class="operations" v-if="dashboardStore.editMode">
<el-popover
placement="bottom"
trigger="click"
:width="200"
v-model:visible="showTools"
>
<template #reference>
<span>
<Icon
iconName="ellipsis_v"
size="middle"
class="operation"
@click="showTools = true"
/>
</span>
<el-dropdown placement="bottom" trigger="click" :width="200">
<span class="icon-operation">
<Icon iconName="ellipsis_v" size="middle" />
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="canEditTabName = true">
<span class="edit-tab">{{ t("editTab") }}</span>
</el-dropdown-item>
<el-dropdown-item @click="removeTab">
<span>{{ t("delete") }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
<div
class="tools"
@click="
canEditTabName = true;
showTools = false;
"
>
<span class="edit-tab">{{ t("editTab") }}</span>
</div>
<div class="tools" @click="removeTab">
<span>{{ t("delete") }}</span>
</div>
</el-popover>
</el-dropdown>
</div>
</div>
<div class="tab-layout" @click="handleClick">
@@ -97,7 +83,7 @@ limitations under the License. -->
:key="item.i"
@click="clickTabGrid($event, item)"
:class="{ active: activeTabWidget === item.i }"
drag-ignore-from="svg.d3-trace-tree, .dragger, .micro-topo-chart"
:drag-ignore-from="dragIgnoreFrom"
>
<component
:is="item.type"
@@ -122,6 +108,9 @@ import Trace from "./Trace.vue";
import Profile from "./Profile.vue";
import Log from "./Log.vue";
import Text from "./Text.vue";
import Ebpf from "./Ebpf.vue";
import { dragIgnoreFrom } from "../data";
import DemandLog from "./DemandLog.vue";
const props = {
data: {
@@ -132,7 +121,7 @@ const props = {
};
export default defineComponent({
name: "Tab",
components: { Topology, Widget, Trace, Profile, Log, Text },
components: { Topology, Widget, Trace, Profile, Log, Text, Ebpf, DemandLog },
props,
setup(props) {
const { t } = useI18n();
@@ -142,7 +131,6 @@ export default defineComponent({
const editTabIndex = ref<number>(NaN); // edit tab item name
const canEditTabName = ref<boolean>(false);
const needQuery = ref<boolean>(false);
const showTools = ref<boolean>(false);
const l = dashboardStore.layout.findIndex(
(d: LayoutConfig) => d.i === props.data.i
);
@@ -246,8 +234,8 @@ export default defineComponent({
editTabIndex,
needQuery,
canEditTabName,
showTools,
t,
dragIgnoreFrom,
};
},
});
@@ -256,24 +244,30 @@ export default defineComponent({
.tabs {
height: 40px;
color: #ccc;
width: 100%;
overflow-x: auto;
white-space: nowrap;
overflow-y: hidden;
span {
display: inline-block;
padding: 0 10px;
margin: 0 10px;
height: 40px;
line-height: 40px;
cursor: pointer;
text-align: center;
}
.tab-name {
max-width: 130px;
max-width: 110px;
height: 20px;
line-height: 20px;
outline: none;
color: #333;
font-style: normal;
margin-right: 5px;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
.tab-icons {
@@ -294,7 +288,10 @@ export default defineComponent({
span.active {
border-bottom: 1px solid #409eff;
color: #409eff;
.tab-name {
color: #409eff;
}
}
}
@@ -306,6 +303,11 @@ export default defineComponent({
padding-right: 10px;
}
.icon-operation {
display: inline-block;
margin-top: 8px;
}
.tab-header {
justify-content: space-between;
width: 100%;
@@ -339,17 +341,4 @@ export default defineComponent({
padding-top: 30px;
color: #888;
}
.tools {
padding: 5px 0;
color: #999;
cursor: pointer;
position: relative;
text-align: center;
&:hover {
color: #409eff;
background-color: #eee;
}
}
</style>

View File

@@ -105,6 +105,7 @@ function editConfig() {
cursor: pointer;
display: flex;
align-items: center;
overflow: auto;
}
.tools {

View File

@@ -66,6 +66,7 @@ function removeWidget() {
height: 100%;
font-size: 12px;
position: relative;
overflow: auto;
}
.delete {
@@ -78,6 +79,7 @@ function removeWidget() {
padding: 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
min-width: 1200px;
}
.tools {
@@ -96,5 +98,6 @@ function removeWidget() {
.trace {
width: 100%;
overflow: auto;
min-width: 1200px;
}
</style>

View File

@@ -21,5 +21,17 @@ import Trace from "./Trace.vue";
import Profile from "./Profile.vue";
import Log from "./Log.vue";
import Text from "./Text.vue";
import Ebpf from "./Ebpf.vue";
import DemandLog from "./DemandLog.vue";
export default { Tab, Widget, Trace, Topology, Profile, Log, Text };
export default {
Tab,
Widget,
Trace,
Topology,
Profile,
Log,
Text,
Ebpf,
DemandLog,
};

View File

@@ -14,6 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const dragIgnoreFrom =
"svg.d3-trace-tree, .dragger, .micro-topo-chart, .schedules";
export const PodsChartTypes = ["EndpointList", "InstanceList"];
@@ -181,8 +183,10 @@ export const ServiceTools = [
{ name: "library_books", content: "Text", id: "addText" },
{ name: "device_hub", content: "Topology", id: "addTopology" },
{ name: "merge", content: "Trace", id: "addTrace" },
{ name: "timeline", content: "Profile", id: "addProfile" },
{ name: "timeline", content: "Trace Profiling", id: "addProfile" },
{ name: "insert_chart", content: "eBPF Profiling", id: "addEbpf" },
{ name: "assignment", content: "Log", id: "addLog" },
{ name: "demand", content: "Add On Demand Log", id: "addDemandLog" },
];
export const InstanceTools = [
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
@@ -190,6 +194,7 @@ export const InstanceTools = [
{ name: "library_books", content: "Add Text", id: "addText" },
{ name: "merge", content: "Add Trace", id: "addTrace" },
{ name: "assignment", content: "Add Log", id: "addLog" },
{ name: "demand", content: "Add On Demand Log", id: "addDemandLog" },
];
export const EndpointTools = [
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
@@ -263,14 +268,17 @@ export const TextColors: { [key: string]: string } = {
export const CalculationOpts = [
{ label: "Percentage", value: "percentage" },
{ label: "Apdex", value: "apdex" },
{ label: "Avg-preview", value: "average" },
{ label: "Percentage + Avg-preview", value: "percentageAvg" },
{ label: "Apdex + Avg-preview", value: "apdexAvg" },
{ label: "Byte to KB", value: "byteToKB" },
{ label: "Byte to MB", value: "byteToMB" },
{ label: "Byte to GB", value: "byteToGB" },
{
label: "Convert milliseconds to YYYY-MM-DD HH:mm:ss",
label: "Milliseconds to YYYY-MM-DD HH:mm:ss",
value: "convertMilliseconds",
},
{ label: "Convert seconds to YYYY-MM-DD HH:mm:ss", value: "convertSeconds" },
{ label: "Seconds to YYYY-MM-DD HH:mm:ss", value: "convertSeconds" },
{ label: "Precision is 2", value: "precision" },
{ label: "Milliseconds to seconds", value: "msTos" },
];

View File

@@ -17,11 +17,13 @@ limitations under the License. -->
<div
v-if="!isNaN(singleVal)"
class="chart-card"
:class="{ center: config.textAlign === 'center' }"
:style="{ fontSize: `${config.fontSize}px`, textAlign: config.textAlign }"
:style="{
fontSize: `${config.fontSize}px`,
justifyContent: config.textAlign || 'center',
}"
>
{{ singleVal.toFixed(2) }}
<span v-show="config.showUnit">
<span class="unit" v-show="config.showUnit && unit">
{{ decodeURIComponent(unit) }}
</span>
</div>
@@ -53,25 +55,27 @@ const metricConfig = computed(() => props.config.metricConfig || []);
const key = computed(() => Object.keys(props.data)[0]);
const singleVal = computed(() => Number(props.data[key.value]));
const unit = computed(
() => metricConfig.value[0] && encodeURIComponent(metricConfig.value[0].unit)
() =>
metricConfig.value[0] &&
encodeURIComponent(metricConfig.value[0].unit || "")
);
</script>
<style lang="scss" scoped>
.chart-card {
color: #333;
width: 100%;
height: 100%;
}
.center {
box-sizing: border-box;
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: center;
display: flex;
align-items: center;
}
.no-data {
height: 100%;
color: #666;
}
.unit {
display: inline-block;
margin-left: 2px;
}
</style>

View File

@@ -42,34 +42,11 @@ limitations under the License. -->
</span>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in colMetrics"
:label="`${decodeURIComponent(
getLabel(metric, index)
)} ${decodeURIComponent(getUnit(index))}`"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{
showXAxis: false,
showYAxis: false,
smallTips: true,
showSymbol: true,
}"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
<ColumnGraph
:intervalTime="intervalTime"
:colMetrics="colMetrics"
:config="config"
/>
</el-table>
</div>
</div>
@@ -83,12 +60,11 @@ import { EndpointListConfig } from "@/types/dashboard";
import { Endpoint } from "@/types/selector";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
import Line from "./Line.vue";
import Card from "./Card.vue";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
import { MetricConfigOpt } from "@/types/dashboard";
import ColumnGraph from "./components/ColumnGraph.vue";
/*global defineProps */
const props = defineProps({
@@ -184,26 +160,6 @@ function clickEndpoint(scope: any) {
async function searchList() {
await queryEndpoints();
}
function getUnit(index: number) {
const u =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].unit;
if (u) {
return `(${encodeURIComponent(u)})`;
}
return encodeURIComponent("");
}
function getLabel(metric: string, index: number) {
const label =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].label;
if (label) {
return encodeURIComponent(label);
}
return encodeURIComponent(metric);
}
watch(
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
(data, old) => {

View File

@@ -42,35 +42,11 @@ limitations under the License. -->
</span>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in colMetrics"
:label="`${decodeURIComponent(
getLabel(metric, index)
)} ${decodeURIComponent(getUnit(index))}`"
:key="metric + index"
min-width="120"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="metric ? { [metric]: scope.row[metric] } : {}"
:intervalTime="intervalTime"
:config="{
showXAxis: false,
showYAxis: false,
smallTips: true,
showSymbol: true,
}"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
<ColumnGraph
:intervalTime="intervalTime"
:colMetrics="colMetrics"
:config="config"
/>
<el-table-column label="Attributes">
<template #default="scope">
<el-popover placement="left" :width="400" trigger="click">
@@ -110,8 +86,6 @@ import { ref, watch, computed } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage } from "element-plus";
import type { PropType } from "vue";
import Line from "./Line.vue";
import Card from "./Card.vue";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { InstanceListConfig } from "@/types/dashboard";
@@ -121,6 +95,7 @@ import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
import { MetricConfigOpt } from "@/types/dashboard";
import ColumnGraph from "./components/ColumnGraph.vue";
/*global defineProps */
const props = defineProps({
@@ -236,28 +211,6 @@ function searchList() {
queryInstanceMetrics(instances.value);
}
function getUnit(index: number) {
const u =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].unit;
if (u) {
return `(${encodeURIComponent(u)})`;
}
return encodeURIComponent("");
}
function getLabel(metric: string, index: number) {
const label =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].label;
if (label) {
return encodeURIComponent(label);
}
return encodeURIComponent(metric);
}
watch(
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
(data, old) => {

View File

@@ -40,6 +40,7 @@ const props = defineProps({
showXAxis: true,
showYAxis: true,
smallTips: false,
showlabels: true,
}),
},
});
@@ -185,7 +186,8 @@ function getOption() {
left: 0,
right: 10,
bottom: 5,
containLabel: true,
containLabel:
props.config.showlabels === undefined ? true : props.config.showlabels,
},
xAxis: {
type: "category",
@@ -199,6 +201,7 @@ function getOption() {
axisLabel: { color: "#9da5b2", fontSize: "11" },
},
yAxis: {
show: props.config.showYAxis,
type: "value",
axisLine: { show: false },
axisTick: { show: false },

View File

@@ -54,34 +54,11 @@ limitations under the License. -->
</span>
</template>
</el-table-column>
<el-table-column
v-for="(metric, index) in colMetrics"
:label="`${decodeURIComponent(
getLabel(metric, index)
)} ${decodeURIComponent(getUnit(index))}`"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="config.metricTypes[index] === 'readMetricsValues'"
:data="{ [metric]: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{
showXAxis: false,
showYAxis: false,
smallTips: true,
showSymbol: true,
}"
/>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
<ColumnGraph
:intervalTime="intervalTime"
:colMetrics="colMetrics"
:config="config"
/>
</el-table>
</div>
<el-pagination
@@ -102,8 +79,6 @@ import { watch, ref, computed } from "vue";
import { ElMessage } from "element-plus";
import type { PropType } from "vue";
import { ServiceListConfig } from "@/types/dashboard";
import Line from "./Line.vue";
import Card from "./Card.vue";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { Service } from "@/types/selector";
@@ -112,6 +87,7 @@ import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
import { MetricConfigOpt } from "@/types/dashboard";
import ColumnGraph from "./components/ColumnGraph.vue";
/*global defineProps */
const props = defineProps({
@@ -240,6 +216,7 @@ async function queryServiceMetrics(currentServices: Service[]) {
});
return;
}
services.value = currentServices;
}
function objectSpanMethod(param: any): any {
@@ -275,26 +252,6 @@ function searchList() {
);
setServices(services);
}
function getUnit(index: number) {
const u =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].unit;
if (u) {
return `(${encodeURIComponent(u)})`;
}
return encodeURIComponent("");
}
function getLabel(metric: string, index: number) {
const label =
props.config.metricConfig &&
props.config.metricConfig[index] &&
props.config.metricConfig[index].label;
if (label) {
return encodeURIComponent(label);
}
return encodeURIComponent(metric);
}
watch(
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
@@ -318,10 +275,6 @@ watch(
<style lang="scss" scoped>
@import "./style.scss";
.chart {
height: 60px;
}
.inputs {
width: 300px;
}

View File

@@ -17,13 +17,13 @@ limitations under the License. -->
<div class="top-list" v-if="available">
<div class="chart-slow-i" v-for="(i, index) in data[key]" :key="index">
<div class="ell tools flex-h">
<div>
<div class="desc">
<span class="calls mr-10">{{ i.value }}</span>
<span class="cp mr-20">
{{ i.name }}
</span>
</div>
<div>
<div class="copy">
<Icon
iconName="review-list"
size="middle"
@@ -103,6 +103,16 @@ function handleClick(i: string) {
height: 100%;
}
.desc {
flex-grow: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.copy {
width: 30px;
}
.calls {
font-size: 12px;
padding: 0 5px;

View File

@@ -0,0 +1,161 @@
<!-- 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
http://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. -->
<template>
<el-table-column
v-for="(metric, index) in colMetrics"
:label="`${decodeURIComponent(
getLabel(metric, index)
)} ${decodeURIComponent(getUnit(index))}`"
:key="metric + index"
>
<template #default="scope">
<div class="chart">
<Line
v-if="useListConfig(config, index).isLinear"
:data="{
[metric]: scope.row[metric] && scope.row[metric].values,
}"
:intervalTime="intervalTime"
:config="{
showXAxis: false,
showYAxis: false,
smallTips: true,
showlabels: false,
}"
/>
<span
class="item flex-h"
v-else-if="useListConfig(config, index).isAvg"
>
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<span class="trend">
<Icon
iconName="timeline"
size="middle"
style="color: #409eff"
/>
</span>
</template>
<div class="view-line">
<Line
:data="{
[metric]: scope.row[metric] && scope.row[metric].values,
}"
:intervalTime="intervalTime"
:config="{
showXAxis: true,
showYAxis: true,
smallTips: false,
showlabels: true,
}"
/>
</div>
</el-popover>
<span class="value">
<Card
:data="{
[metric]: scope.row[metric] && scope.row[metric].avg,
}"
:config="{ textAlign: 'left' }"
/>
</span>
</span>
<Card
v-else
:data="{ [metric]: scope.row[metric] }"
:config="{ textAlign: 'left' }"
/>
</div>
</template>
</el-table-column>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { MetricConfigOpt } from "@/types/dashboard";
import { useListConfig } from "@/hooks/useListConfig";
import Line from "../Line.vue";
import Card from "../Card.vue";
/*global defineProps */
const props = defineProps({
colMetrics: { type: Object },
config: {
type: Object as PropType<
{
i: string;
metrics: string[];
metricTypes: string[];
} & { metricConfig: MetricConfigOpt[] }
>,
default: () => ({}),
},
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
});
function getUnit(index: string) {
const i = Number(index);
const u =
props.config.metricConfig &&
props.config.metricConfig[i] &&
props.config.metricConfig[i].unit;
if (u) {
return `(${encodeURIComponent(u)})`;
}
return encodeURIComponent("");
}
function getLabel(metric: string, index: string) {
const i = Number(index);
const label =
props.config.metricConfig &&
props.config.metricConfig[i] &&
props.config.metricConfig[i].label;
if (label) {
return encodeURIComponent(label);
}
return encodeURIComponent(metric);
}
</script>
<style lang="scss" scoped>
.chart {
height: 40px;
}
.view-line {
width: 380px;
height: 200px;
}
.item {
display: inline-block;
width: 100%;
height: 100%;
}
.trend {
width: 30px;
display: inline-block;
height: 100%;
cursor: pointer;
}
.value {
display: inline-block;
flex-grow: 2;
height: 100%;
}
</style>

View File

@@ -29,9 +29,9 @@ limitations under the License. -->
:h="item.h"
:i="item.i"
:key="item.i"
@click="clickGrid(item)"
@click="clickGrid(item, $event)"
:class="{ active: dashboardStore.activedGridItem === item.i }"
drag-ignore-from="svg.d3-trace-tree, .dragger, .micro-topo-chart"
:drag-ignore-from="dragIgnoreFrom"
>
<component :is="item.type" :data="item" />
</grid-item>
@@ -45,6 +45,7 @@ import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { LayoutConfig } from "@/types/dashboard";
import controls from "../controls/index";
import { dragIgnoreFrom } from "../data";
export default defineComponent({
name: "Layout",
@@ -53,13 +54,14 @@ export default defineComponent({
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const selectorStore = useSelectorStore();
// function layoutUpdatedEvent(newLayout: LayoutConfig[]) {
// dashboardStore.setLayout(newLayout);
// }
function clickGrid(item: LayoutConfig) {
function clickGrid(item: LayoutConfig, event: Event) {
dashboardStore.activeGridItem(item.i);
dashboardStore.selectWidget(item);
if (item.type === "Tab") {
if (
item.type === "Tab" &&
(event.target as HTMLDivElement)?.className !== "tab-layout"
) {
dashboardStore.setActiveTabIndex(0);
}
}
@@ -68,11 +70,13 @@ export default defineComponent({
selectorStore.setCurrentService(null);
selectorStore.setCurrentPod(null);
dashboardStore.setEntity("");
dashboardStore.setConfigPanel(false);
});
return {
dashboardStore,
clickGrid,
t,
dragIgnoreFrom,
};
},
});

View File

@@ -78,9 +78,9 @@ limitations under the License. -->
/>
</div>
</div>
<div class="flex-h tools" v-loading="loading">
<div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile">
<div class="tool-icons flex-h" v-if="dashboardStore.editMode">
<el-dropdown content="Controls" placement="bottom">
<el-dropdown content="Controls" placement="bottom" :persistent="false">
<i>
<Icon class="icon-btn" size="sm" iconName="control" />
</i>
@@ -217,12 +217,23 @@ async function setSelector() {
return;
}
let currentService, currentDestService;
for (const d of selectorStore.services) {
if (d.id === String(params.serviceId)) {
currentService = d;
if (states.currentService) {
for (const d of selectorStore.services) {
if (d.value === states.currentService) {
currentService = d;
}
if (d.value === states.currentDestService) {
currentDestService = d;
}
}
if (d.id === String(params.destServiceId)) {
currentDestService = d;
} else {
for (const d of selectorStore.services) {
if (d.id === String(params.serviceId)) {
currentService = d;
}
if (d.id === String(params.destServiceId)) {
currentDestService = d;
}
}
}
selectorStore.setCurrentService(currentService);
@@ -244,9 +255,14 @@ async function setSourceSelector() {
return;
}
const pod = params.podId || selectorStore.pods[0].id;
const currentPod = selectorStore.pods.find(
(d: { id: string }) => d.id === pod
);
let currentPod;
if (states.currentPod) {
currentPod = selectorStore.pods.find(
(d: { label: string }) => d.label === states.currentPod
);
} else {
currentPod = selectorStore.pods.find((d: { id: string }) => d.id === pod);
}
if (currentPod) {
selectorStore.setCurrentPod(currentPod);
states.currentPod = currentPod.label;
@@ -267,9 +283,16 @@ async function setDestSelector() {
return;
}
const destPod = params.destPodId || selectorStore.destPods[0].id;
const currentDestPod = selectorStore.destPods.find(
(d: { id: string }) => d.id === destPod
);
let currentDestPod = { label: "" };
if (states.currentDestPod) {
currentDestPod = selectorStore.pods.find(
(d: { label: string }) => d.label === states.currentDestPod
);
} else {
currentDestPod = selectorStore.destPods.find(
(d: { id: string }) => d.id === destPod
);
}
if (currentDestPod) {
selectorStore.setCurrentDestPod(currentDestPod);
states.currentDestPod = currentDestPod.label;
@@ -291,19 +314,40 @@ async function getServices() {
ElMessage.error(json.errors);
return;
}
selectorStore.setCurrentService(
selectorStore.services.length ? selectorStore.services[0] : null
);
selectorStore.setCurrentDestService(
selectorStore.services.length ? selectorStore.services[1] : null
);
let s;
if (states.currentService) {
s = (selectorStore.services || []).find(
(d: { label: string }) => d.label === states.currentService
);
} else {
s = (selectorStore.services || []).find(
(d: unknown, index: number) => index === 0
);
}
selectorStore.setCurrentService(s || null);
let d;
if (states.currentService) {
d = (selectorStore.services || []).find(
(d: { label: string }) => d.label === states.currentDestService
);
} else {
d = (selectorStore.services || []).find(
(d: unknown, index: number) => index === 1
);
}
selectorStore.setCurrentDestService(d || null);
if (!selectorStore.currentService) {
return;
}
states.currentService = selectorStore.currentService.value;
const e = dashboardStore.entity.split("Relation")[0];
if (
[EntityType[2].value, EntityType[3].value].includes(dashboardStore.entity)
[
EntityType[2].value,
EntityType[3].value,
EntityType[5].value,
EntityType[6].value,
].includes(dashboardStore.entity)
) {
fetchPods(e, selectorStore.currentService.id, true);
}
@@ -322,7 +366,10 @@ async function changeService(service: any) {
if (service[0]) {
states.currentService = service[0].value;
selectorStore.setCurrentService(service[0]);
fetchPods(dashboardStore.entity, selectorStore.currentService.id, true);
const e = dashboardStore.entity.split("Relation")[0];
selectorStore.setCurrentPod(null);
states.currentPod = "";
fetchPods(e, selectorStore.currentService.id, true);
} else {
selectorStore.setCurrentService(null);
}
@@ -332,6 +379,9 @@ function changeDestService(service: any) {
if (service[0]) {
states.currentDestService = service[0].value;
selectorStore.setCurrentDestService(service[0]);
selectorStore.setCurrentDestPod(null);
states.currentDestPod = "";
fetchPods(dashboardStore.entity, selectorStore.currentDestService.id, true);
} else {
selectorStore.setCurrentDestService(null);
}
@@ -396,12 +446,18 @@ function setTabControls(id: string) {
case "addProfile":
dashboardStore.addTabControls("Profile");
break;
case "addEbpf":
dashboardStore.addTabControls("Ebpf");
break;
case "addTopology":
dashboardStore.addTabControls("Topology");
break;
case "addText":
dashboardStore.addTabControls("Text");
break;
case "addDemandLog":
dashboardStore.addTabControls("DemandLog");
break;
default:
ElMessage.info("Don't support this control");
break;
@@ -422,6 +478,9 @@ function setControls(id: string) {
case "addProfile":
dashboardStore.addControl("Profile");
break;
case "addEbpf":
dashboardStore.addControl("Ebpf");
break;
case "addLog":
dashboardStore.addControl("Log");
break;
@@ -431,6 +490,9 @@ function setControls(id: string) {
case "addText":
dashboardStore.addControl("Text");
break;
case "addDemandLog":
dashboardStore.addControl("DemandLog");
break;
default:
dashboardStore.addControl("Widget");
}
@@ -447,18 +509,34 @@ async function fetchPods(
case EntityType[2].value:
resp = await selectorStore.getEndpoints({ serviceId, ...param });
if (setPod) {
selectorStore.setCurrentPod(
selectorStore.pods.length ? selectorStore.pods[0] : null
);
let p;
if (states.currentPod) {
p = selectorStore.pods.find(
(d: { label: unknown }) => d.label === states.currentPod
);
} else {
p = selectorStore.pods.find(
(d: unknown, index: number) => index === 0
);
}
selectorStore.setCurrentPod(p || null);
states.currentPod = selectorStore.currentPod.label;
}
break;
case EntityType[3].value:
resp = await selectorStore.getServiceInstances({ serviceId });
if (setPod) {
selectorStore.setCurrentPod(
selectorStore.pods.length ? selectorStore.pods[0] : null
);
let p;
if (states.currentPod) {
p = selectorStore.pods.find(
(d: { label: string }) => d.label === states.currentPod
);
} else {
p = selectorStore.pods.find(
(d: { label: string }, index: number) => index === 0
);
}
selectorStore.setCurrentPod(p || null);
states.currentPod = selectorStore.currentPod.label;
}
break;
@@ -469,9 +547,17 @@ async function fetchPods(
...param,
});
if (setPod) {
selectorStore.setCurrentDestPod(
selectorStore.destPods.length ? selectorStore.destPods[0] : null
);
let p;
if (states.currentDestPod) {
p = selectorStore.destPods.find(
(d: { label: string }) => d.label === states.currentDestPod
);
} else {
p = selectorStore.destPods.find(
(d: { label: string }, index: number) => index === 0
);
}
selectorStore.setCurrentDestPod(p || null);
states.currentDestPod = selectorStore.currentDestPod.label;
}
break;
@@ -481,9 +567,17 @@ async function fetchPods(
isRelation: true,
});
if (setPod) {
selectorStore.setCurrentDestPod(
selectorStore.destPods.length ? selectorStore.destPods[0] : null
);
let p;
if (states.currentDestPod) {
p = selectorStore.destPods.find(
(d: { label: string }) => d.label === states.currentDestPod
);
} else {
p = selectorStore.destPods.find(
(d: { label: string }, index: number) => index === 0
);
}
selectorStore.setCurrentDestPod(p || null);
states.currentDestPod = selectorStore.currentDestPod.label;
}
break;

View File

@@ -93,6 +93,8 @@ function setCurrentLog(log: any) {
.log {
font-size: 12px;
height: 100%;
border-bottom: 1px solid #eee;
width: 100%;
overflow: auto;
}

View File

@@ -22,12 +22,12 @@ limitations under the License. -->
<span v-else-if="item.label === 'tags'">
{{ tags }}
</span>
<router-link
<!-- <router-link
v-else-if="item.label === 'traceId' && !noLink"
:to="{ name: 'trace', query: { traceid: data[item.label] } }"
>
<span :class="noLink ? '' : 'blue'">{{ data[item.label] }}</span>
</router-link>
</router-link> -->
<span v-else>{{ data[item.label] }}</span>
</div>
</div>

View File

@@ -0,0 +1,99 @@
<!-- 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
http://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. -->
<template>
<span v-if="demandLogStore.message">{{ demandLogStore.message }}</span>
<div
v-else
v-loading="demandLogStore.loadLogs"
class="log-content"
ref="logContent"
style="width: calc(100% - 10px); height: calc(100% - 140px)"
></div>
</template>
<script lang="ts" setup>
import { onMounted, ref, onUnmounted, watch, toRaw } from "vue";
import { useDemandLogStore } from "@/store/modules/demand-log";
/*global Nullable */
const demandLogStore = useDemandLogStore();
const monacoInstance = ref();
const logContent = ref<Nullable<HTMLDivElement>>(null);
onMounted(() => {
init();
});
async function init() {
const monaco = await import("monaco-editor");
setTimeout(() => {
monacoInstanceGen(monaco);
}, 500);
window.addEventListener("resize", () => {
editorLayout();
});
}
function monacoInstanceGen(monaco: any) {
monacoInstance.value = monaco.editor.create(logContent.value, {
value: "",
language: "text",
wordWrap: true,
minimap: { enabled: false },
readonly: true,
});
toRaw(monacoInstance.value).updateOptions({ readOnly: true });
editorLayout();
}
function editorLayout() {
if (!logContent.value) {
return;
}
const { width, height } = logContent.value.getBoundingClientRect();
toRaw(monacoInstance.value).layout({
height: height,
width: width,
});
}
onUnmounted(() => {
if (!toRaw(monacoInstance.value)) {
return;
}
toRaw(monacoInstance.value).dispose();
monacoInstance.value = null;
demandLogStore.setLogs("");
});
watch(
() => demandLogStore.logs,
() => {
if (!toRaw(monacoInstance.value)) {
return;
}
toRaw(monacoInstance.value).setValue(demandLogStore.logs);
if (!demandLogStore.logs) {
return;
}
setTimeout(() => {
toRaw(monacoInstance.value).revealPosition({
column: 1,
lineNumber: demandLogStore.total,
});
}, 1000);
}
);
</script>
<style lang="scss" scoped>
.log-content {
min-width: 600px;
min-height: 400px;
}
</style>

View File

@@ -0,0 +1,433 @@
<!-- 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
http://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. -->
<template>
<div class="flex-h row">
<div class="mr-5 mb-5" v-if="dashboardStore.entity !== EntityType[3].value">
<span class="grey mr-5"> {{ t("instance") }}: </span>
<Selector
size="small"
:value="state.instance.value"
:options="demandLogStore.instances"
placeholder="Select a instance"
@change="changeField('instance', $event)"
class="selectors"
/>
</div>
<div class="mr-5 mb-5" v-if="state.container">
<span class="grey mr-5">{{ t("container") }}:</span>
<Selector
size="small"
:value="state.container.value"
:options="demandLogStore.containers"
placeholder="Select a container"
@change="changeField('container', $event)"
class="selectors"
/>
</div>
<!-- <div class="mr-5">
<span class="grey mr-5">{{ t("limit") }}:</span>
<el-input-number
v-model="limit"
:min="1"
:max="1000"
size="small"
controls-position="right"
@change="changeField('limit', $event)"
/>
</div> -->
<div class="mr-5">
<span class="grey mr-5">{{ t("duration") }}:</span>
<Selector
size="small"
:value="state.duration.value"
:options="TimeRanges"
placeholder="Select a time range"
@change="changeField('duration', $event)"
class="duration-range"
/>
</div>
<div class="mr-5">
<span class="grey mr-5">{{ t("interval") }}:</span>
<Selector
size="small"
:value="state.interval.value"
:options="IntervalOpts"
@change="changeField('interval', $event)"
/>
</div>
</div>
<div class="flex-h row">
<div class="mr-5">
<span class="mr-5 grey">{{ t("keywordsOfContent") }}:</span>
<span class="log-tags">
<span
class="selected"
v-for="(item, index) in keywordsOfContent"
:key="`keywordsOfContent${index}`"
>
<span>{{ item }}</span>
<span class="remove-icon" @click="removeContent(index)">×</span>
</span>
</span>
<el-input
size="small"
class="inputs-max"
:placeholder="t('addKeywordsOfContent')"
v-model="contentStr"
@change="addLabels('keywordsOfContent')"
/>
</div>
<div class="mr-5">
<span class="grey mr-5"> {{ t("excludingKeywordsOfContent") }}: </span>
<span class="log-tags">
<span
class="selected"
v-for="(item, index) in excludingKeywordsOfContent"
:key="`excludingKeywordsOfContent${index}`"
>
<span>{{ item }}</span>
<span class="remove-icon" @click="removeExcludeContent(index)">
×
</span>
</span>
</span>
<el-input
class="inputs-max"
size="small"
:placeholder="t('addExcludingKeywordsOfContent')"
v-model="excludingContentStr"
@change="addLabels('excludingKeywordsOfContent')"
/>
</div>
</div>
<div class="flex-h row btn-row">
<el-button
class="search-btn mt-10"
size="small"
type="primary"
@click="runInterval"
:disabled="disabled"
>
<Icon
size="middle"
iconName="retry"
:loading="!!intervalFn"
class="mr-5"
/>
{{ intervalFn ? t("pause") : t("start") }}
</el-button>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onMounted, onUnmounted } from "vue";
import { useI18n } from "vue-i18n";
import { useDemandLogStore } from "@/store/modules/demand-log";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus";
import { EntityType } from "../../data";
import { TimeRanges, IntervalOpts } from "./data";
import getLocalTime from "@/utils/localtime";
import dateFormatStep from "@/utils/dateFormat";
const { t } = useI18n();
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const demandLogStore = useDemandLogStore();
const keywordsOfContent = ref<string[]>([]);
const excludingKeywordsOfContent = ref<string[]>([]);
const contentStr = ref<string>("");
const excludingContentStr = ref<string>("");
// const limit = ref<number>(20);
const state = reactive<any>({
instance: { value: "", label: "" },
container: { value: "", label: "" },
duration: { label: "From 30 minutes ago", value: 1800 },
interval: { label: "30 seconds", value: 30 },
});
const disabled = ref<boolean>(true);
/*global Nullable */
const intervalFn = ref<Nullable<any>>(null);
onMounted(() => {
fetchSelectors();
});
async function fetchSelectors() {
if (dashboardStore.entity !== EntityType[3].value) {
await getInstances();
}
getContainers();
if (intervalFn.value) {
clearTimer();
}
}
async function getContainers() {
if (
!(
state.instance.id ||
(selectorStore.currentPod && selectorStore.currentPod.id)
)
) {
return;
}
const resp = await demandLogStore.getContainers(
state.instance.id || selectorStore.currentPod.id
);
if (resp.errors) {
disabled.value = true;
ElMessage.error(resp.errors);
return;
}
if (resp.data.containers.errorReason) {
disabled.value = true;
ElMessage.warning(resp.data.containers.errorReason);
return;
}
if (demandLogStore.containers.length) {
state.container = demandLogStore.containers[0];
disabled.value = false;
}
}
async function getInstances() {
const resp = await demandLogStore.getInstances();
if (resp.errors) {
ElMessage.error(resp.errors);
return;
}
state.instance = demandLogStore.instances[0];
}
function runInterval() {
if (intervalFn.value) {
clearTimer();
return;
}
searchLogs();
if (state.interval.value === 0) {
return;
}
intervalFn.value = setInterval(searchLogs, state.interval.value * 1000);
setTimeout(() => {
clearTimer();
}, state.duration.value * 1000);
}
function searchLogs() {
let instance = "";
if (dashboardStore.entity === EntityType[3].value) {
instance = selectorStore.currentPod.id;
}
const serviceInstanceId =
instance || (state.instance && state.instance.id) || "";
demandLogStore.setLogCondition({
serviceInstanceId,
container: state.container.value,
duration: rangeTime(),
keywordsOfContent: keywordsOfContent.value.length
? keywordsOfContent.value
: undefined,
excludingKeywordsOfContent: excludingKeywordsOfContent.value.length
? excludingKeywordsOfContent.value
: undefined,
});
if (!serviceInstanceId) {
return;
}
queryLogs();
}
function rangeTime() {
{
const times = {
start: getLocalTime(
appStore.utc,
new Date(new Date().getTime() - state.duration.value * 1000)
),
end: getLocalTime(appStore.utc, new Date()),
step: "SECOND",
};
return {
start: dateFormatStep(times.start, times.step, false),
end: dateFormatStep(times.end, times.step, false),
step: times.step,
};
}
}
async function queryLogs() {
const res = await demandLogStore.getDemandLogs();
if (res && res.errors) {
ElMessage.error(res.errors);
}
}
function changeField(type: string, opt: any) {
clearTimer();
// if (["limit"].includes(type)) {
// state[type] = opt;
// return;
// }
state[type] = opt[0];
if (type === "instance") {
getContainers();
}
}
function removeContent(index: number) {
const keywordsOfContentList = keywordsOfContent.value || [];
keywordsOfContentList.splice(index, 1);
demandLogStore.setLogCondition({
keywordsOfContent: keywordsOfContentList,
});
contentStr.value = "";
clearTimer();
}
function addLabels(type: string) {
if (type === "keywordsOfContent" && !contentStr.value) {
return;
}
if (type === "excludingKeywordsOfContent" && !excludingContentStr.value) {
return;
}
if (type === "keywordsOfContent") {
keywordsOfContent.value.push(contentStr.value);
demandLogStore.setLogCondition({
[type]: keywordsOfContent.value,
});
contentStr.value = "";
} else if (type === "excludingKeywordsOfContent") {
excludingKeywordsOfContent.value.push(excludingContentStr.value);
demandLogStore.setLogCondition({
[type]: excludingKeywordsOfContent.value,
});
excludingContentStr.value = "";
}
clearTimer();
}
function removeExcludeContent(index: number) {
excludingKeywordsOfContent.value.splice(index, 1);
demandLogStore.setLogCondition({
excludingKeywordsOfContent: excludingKeywordsOfContent.value,
});
excludingContentStr.value = "";
clearTimer();
}
function clearTimer() {
if (!intervalFn.value) {
return;
}
clearInterval(intervalFn.value);
intervalFn.value = null;
}
onUnmounted(() => {
clearTimer();
});
watch(
() => selectorStore.currentService,
() => {
if (dashboardStore.entity === EntityType[0].value) {
fetchSelectors();
demandLogStore.setLogs("");
}
}
);
watch(
() => [selectorStore.currentPod],
() => {
if (dashboardStore.entity === EntityType[3].value) {
fetchSelectors();
demandLogStore.setLogs("");
}
}
);
</script>
<style lang="scss" scoped>
.inputs {
width: 120px;
}
.row {
margin-bottom: 5px;
position: relative;
flex-wrap: wrap;
}
.inputs-max {
width: 270px;
}
.traceId {
margin-top: 2px;
}
.search-btn {
cursor: pointer;
width: 120px;
}
.tips {
color: #888;
}
.log-tag {
width: 30%;
border-style: unset;
outline: 0;
border: 1px solid #ccc;
height: 30px;
padding: 0 5px;
}
.log-tags {
padding: 1px 5px 0 0;
border-radius: 3px;
height: 24px;
display: inline-block;
vertical-align: top;
}
.selected {
display: inline-block;
padding: 0 3px;
border-radius: 3px;
overflow: hidden;
color: #3d444f;
border: 1px dashed #aaa;
font-size: 12px;
margin: 0 2px;
}
.remove-icon {
display: inline-block;
margin-left: 3px;
cursor: pointer;
}
.selectors {
width: 250px;
}
.duration-range {
width: 210px;
}
.btn-row {
justify-content: flex-end;
}
.help {
color: #999;
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,37 @@
/**
* 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
*
* http://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.
*/
export const TimeRanges = [
{ label: "From 5 seconds ago -- Now", value: 5 },
{ label: "From 10 seconds ago -- Now", value: 10 },
{ label: "From 15 seconds ago -- Now", value: 15 },
{ label: "From 30 seconds ago -- Now", value: 30 },
{ label: "From 45 seconds ago -- Now", value: 45 },
{ label: "From 1 minute ago -- Now", value: 60 },
{ label: "From 5 minutes ago -- Now", value: 300 },
{ label: "From 15 minutes ago -- Now", value: 900 },
{ label: "From 30 minutes ago -- Now", value: 1800 },
];
export const IntervalOpts = [
{ label: "None", value: 0 },
{ label: "5 seconds", value: 5 },
{ label: "10 seconds", value: 10 },
{ label: "15 seconds", value: 15 },
{ label: "30 seconds", value: 30 },
{ label: "45 seconds", value: 45 },
{ label: "1 minute", value: 60 },
];

View File

@@ -0,0 +1,58 @@
<!-- 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
http://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. -->
<template>
<div class="flex-h content">
<TaskList />
<div class="vis-graph ml-5">
<div class="schedules">
<EBPFSchedules />
</div>
<div class="item">
<EBPFStack />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import TaskList from "./components/TaskList.vue";
import EBPFSchedules from "./components/EBPFSchedules.vue";
import EBPFStack from "./components/EBPFStack.vue";
</script>
<style lang="scss" scoped>
.content {
height: calc(100% - 30px);
width: 100%;
}
.vis-graph {
height: 100%;
flex-grow: 2;
min-width: 700px;
overflow: auto;
}
.item {
width: 100%;
overflow: auto;
height: calc(100% - 100px);
padding-bottom: 10px;
}
.schedules {
height: 90px;
border-bottom: 1px solid #ccc;
padding-right: 10px;
}
</style>

View File

@@ -0,0 +1,104 @@
<!-- 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
http://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. -->
<template>
<div class="flex-h header">
<div class="title">eBPF Profiling</div>
<el-button type="primary" size="small" @click="createTask">
{{ t("newTask") }}
</el-button>
</div>
<el-dialog
v-model="newTask"
:destroy-on-close="true"
fullscreen
@closed="newTask = false"
>
<NewTask @close="newTask = false" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus";
import NewTask from "./components/NewTask.vue";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { EntityType } from "../../data";
const ebpfStore = useEbpfStore();
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const { t } = useI18n();
const newTask = ref<boolean>(false);
searchTasks();
async function searchTasks() {
const serviceId =
(selectorStore.currentService && selectorStore.currentService.id) || "";
const res = await ebpfStore.getTaskList(serviceId);
if (res.errors) {
ElMessage.error(res.errors);
}
}
async function createTask() {
if (!selectorStore.currentService) {
return;
}
newTask.value = true;
ebpfStore.getCreateTaskData(selectorStore.currentService.id);
}
watch(
() => selectorStore.currentService,
() => {
searchTasks();
}
);
watch(
() => appStore.durationTime,
() => {
if (dashboardStore.entity === EntityType[1].value) {
searchTasks();
}
}
);
</script>
<style lang="scss" scoped>
.header {
padding: 5px 20px 5px 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
justify-content: space-between;
}
.name {
width: 270px;
}
.new-btn {
float: right;
}
.title {
font-weight: bold;
line-height: 24px;
}
</style>

View File

@@ -0,0 +1,280 @@
<!-- 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
http://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. -->
<template>
<div class="filters">
<div class="mb-10 flex-h">
<Selector
:value="selectedLabels"
:options="labels"
size="small"
placeholder="Please select labels"
@change="changeLabels"
class="inputs mr-10"
:multiple="true"
/>
<div class="mr-5 duration" v-if="duration.length">
<span>{{ duration[0] }}</span>
<span> ~ </span>
<span>{{ duration[1] }}</span>
</div>
</div>
<div class="flex-h">
<Selector
v-if="ebpfStore.selectedTask.targetType === 'OFF_CPU'"
:value="aggregateType"
:options="AggregateTypes"
size="small"
placeholder="Please select a type"
@change="changeAggregateType"
class="selector mr-10"
/>
<el-popover
placement="bottom"
:width="680"
trigger="click"
:persistent="false"
>
<template #reference>
<el-button type="primary" size="small">
{{ t("processSelect") }}
</el-button>
</template>
<el-input
v-model="searchText"
placeholder="Please input name"
class="input-with-search"
size="small"
@change="searchProcesses(0)"
>
<template #append>
<el-button size="small">
<Icon size="sm" iconName="search" />
</el-button>
</template>
</el-input>
<el-table
:data="currentProcesses"
ref="multipleTableRef"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column
v-for="(h, index) of TableHeader"
:property="h.property"
:label="h.label"
:key="index"
width="150"
/>
<el-table-column width="300" label="Attributes">
<template #default="scope">
{{ scope.row.attributes.map((d: {name: string, value: string}) => `${d.name}=${d.value}`).join("; ") }}
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
background
small
layout="prev, pager, next"
:page-size="pageSize"
:total="processes.length"
@current-change="changePage"
@prev-click="changePage"
@next-click="changePage"
/>
</el-popover>
<el-button type="primary" size="small" @click="analyzeEBPF">
{{ t("analyze") }}
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
import { Option } from "@/types/app";
import { TableHeader, AggregateTypes } from "./data";
import { useEbpfStore } from "@/store/modules/ebpf";
import { EBPFProfilingSchedule, Process } from "@/types/ebpf";
import { ElMessage, ElTable } from "element-plus";
const { t } = useI18n();
const ebpfStore = useEbpfStore();
const pageSize = 5;
const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const selectedProcesses = ref<string[]>([]);
const labels = ref<Option[]>([{ label: "All", value: "0" }]);
const processes = ref<Process[]>([]);
const currentProcesses = ref<Process[]>([]);
const selectedLabels = ref<string[]>(["0"]);
const searchText = ref<string>("");
const aggregateType = ref<string>(AggregateTypes[0].value);
const duration = ref<string[]>([]);
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
function changeLabels(opt: any[]) {
const arr = opt.map((d) => d.value);
selectedLabels.value = arr;
}
function changeAggregateType(opt: any[]) {
aggregateType.value = opt[0].value;
ebpfStore.setAnalyzeTrees([]);
}
const handleSelectionChange = (arr: Process[]) => {
selectedProcesses.value = arr.map((d: Process) => d.id);
};
async function analyzeEBPF() {
let arr: string[] = selectedLabels.value;
if (selectedLabels.value.includes("0")) {
arr = labels.value.map((d: Option) => d.value);
}
const ranges: { start: number; end: number }[] = [];
const scheduleIdList = ebpfStore.eBPFSchedules.flatMap(
(d: EBPFProfilingSchedule) => {
const l = d.process.labels.find((d: string) => arr.includes(d));
const i = selectedProcesses.value.includes(d.process.id);
if (l || i) {
ranges.push({
start: d.startTime,
end: d.endTime,
});
return d.scheduleId;
}
}
);
let timeRanges: { start: number; end: number }[] = [];
for (const r of ranges) {
if (timeRanges.length) {
for (const t of timeRanges) {
if (r.start > t.start && r.start < t.end) {
if (r.end > t.end) {
t.end = r.end;
}
}
}
} else {
timeRanges.push(r);
}
}
const res = await ebpfStore.getEBPFAnalyze({
scheduleIdList,
timeRanges,
aggregateType: aggregateType.value,
});
if (res.data && res.data.errors) {
ElMessage.error(res.data.errors);
return;
}
}
function getSchedules() {
labels.value = [{ label: "All", value: "0" }];
selectedLabels.value = ["0"];
processes.value = [];
const ranges = ebpfStore.eBPFSchedules.map((d: EBPFProfilingSchedule) => {
for (const l of d.process.labels) {
labels.value.push({ label: l, value: l });
}
processes.value.push(d.process);
return [d.startTime / 10000, d.endTime / 10000];
});
if (ranges.length) {
const arr = ranges.flat(1);
const min = Math.min(...arr);
const max = Math.max(...arr);
duration.value = [dateFormat(min * 10000), dateFormat(max * 10000)];
} else {
duration.value = [];
}
searchProcesses(0);
analyzeEBPF();
}
function changePage(pageIndex: number) {
searchProcesses(pageIndex);
}
function searchProcesses(pageIndex: number) {
const arr = processes.value.filter(
(d: {
name: string;
instanceName: string;
attributes: { name: string; value: string }[];
}) =>
d.name.includes(searchText.value) ||
d.instanceName.includes(searchText.value) ||
searchAttribute(d.attributes, searchText.value)
);
currentProcesses.value = arr.filter(
(d, index: number) =>
(pageIndex - 1 || 0) * pageSize <= index &&
pageSize * (pageIndex || 1) > index
);
}
function searchAttribute(
attributes: { name: string; value: string }[],
text: string
) {
const item = attributes.find(
(d: { name: string; value: string }) => d.name === "command_line"
);
if (!item) {
return false;
}
return item.value.includes(text);
}
watch(
() => ebpfStore.eBPFSchedules,
() => {
getSchedules();
}
);
</script>
<style lang="scss" scoped>
.filters {
margin: 5px 0;
width: 100%;
min-width: 560px;
}
.inputs {
width: 400px;
}
.input-with-search {
width: 650px;
margin-bottom: 5px;
}
.pagination {
margin-top: 10px;
}
.selector {
width: 120px;
}
.duration {
line-height: 30px;
}
</style>

View File

@@ -0,0 +1,212 @@
<!-- 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
http://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. -->
<template>
<div id="graph-stack" ref="graph">
<span class="tip" v-show="ebpfStore.tip">{{ ebpfStore.tip }}</span>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import * as d3 from "d3";
import d3tip from "d3-tip";
import { flamegraph } from "d3-flame-graph";
import { useEbpfStore } from "@/store/modules/ebpf";
import { StackElement } from "@/types/ebpf";
import { AggregateTypes } from "./data";
import "d3-flame-graph/dist/d3-flamegraph.css";
/*global Nullable*/
const ebpfStore = useEbpfStore();
const stackTree = ref<Nullable<StackElement>>(null);
const selectStack = ref<Nullable<StackElement>>(null);
const graph = ref<Nullable<HTMLDivElement>>(null);
const flameChart = ref<any>(null);
const min = ref<number>(1);
const max = ref<number>(1);
function drawGraph() {
if (flameChart.value) {
flameChart.value.destroy();
}
if (!ebpfStore.analyzeTrees.length) {
return (stackTree.value = null);
}
const root: StackElement = {
parentId: "0",
originId: "1",
name: "Virtual Root",
children: [],
value: 0,
id: "1",
symbol: "Virtual Root",
dumpCount: 0,
stackType: "",
rateOfRoot: "",
rateOfParent: "",
};
countRange();
for (const tree of ebpfStore.analyzeTrees) {
const ele = processTree(tree.elements);
root.children && root.children.push(ele);
}
const param = (root.children || []).reduce(
(prev: number[], curr: StackElement) => {
prev[0] += curr.value;
prev[1] += curr.dumpCount;
return prev;
},
[0, 0]
);
root.value = param[0];
root.dumpCount = param[1];
stackTree.value = root;
const width = (graph.value && graph.value.getBoundingClientRect().width) || 0;
const w = width < 800 ? 802 : width;
flameChart.value = flamegraph()
.width(w - 15)
.cellHeight(18)
.transitionDuration(750)
.minFrameSize(1)
.transitionEase(d3.easeCubic as any)
.sort(true)
.title("")
.selfValue(false)
.inverted(true)
.onClick((d: { data: StackElement }) => {
selectStack.value = d.data;
})
.setColorMapper((d, originalColor) =>
d.highlight ? "#6aff8f" : originalColor
);
const tip = (d3tip as any)()
.attr("class", "d3-tip")
.direction("w")
.html((d: { data: StackElement } & { parent: { data: StackElement } }) => {
const name = d.data.name.replace("<", "&lt;").replace(">", "&gt;");
const valStr =
ebpfStore.aggregateType === AggregateTypes[0].value
? `<div class="mb-5">Dump Count: ${d.data.dumpCount}</div>`
: `<div class="mb-5">Duration: ${d.data.dumpCount} ns</div>`;
const rateOfParent =
(d.parent &&
`<div class="mb-5">Percentage Of Selected: ${
(
(d.data.dumpCount /
((selectStack.value && selectStack.value.dumpCount) ||
root.dumpCount)) *
100
).toFixed(3) + "%"
}</div>`) ||
"";
const rateOfRoot = `<div class="mb-5">Percentage Of Root: ${
((d.data.dumpCount / root.dumpCount) * 100).toFixed(3) + "%"
}</div>`;
return `<div class="mb-5 name">Symbol: ${name}</div>${valStr}${rateOfParent}${rateOfRoot}`;
})
.style("max-width", "500px");
flameChart.value.tooltip(tip);
d3.select("#graph-stack").datum(stackTree.value).call(flameChart.value);
}
function countRange() {
const list = [];
for (const tree of ebpfStore.analyzeTrees) {
for (const ele of tree.elements) {
list.push(ele.dumpCount);
}
}
max.value = Math.max(...list);
min.value = Math.min(...list);
}
function processTree(arr: StackElement[]) {
const copyArr = (window as any).structuredClone(arr);
const obj: any = {};
let res = null;
for (const item of copyArr) {
item.parentId = String(Number(item.parentId) + 1);
item.originId = String(Number(item.id) + 1);
item.name = item.symbol;
delete item.id;
obj[item.originId] = item;
}
const scale = d3.scaleLinear().domain([min.value, max.value]).range([1, 200]);
for (const item of copyArr) {
if (item.parentId === "1") {
const val = Number(scale(item.dumpCount).toFixed(4));
res = item;
res.value = val;
}
for (const key in obj) {
if (item.originId === obj[key].parentId) {
const val = Number(scale(obj[key].dumpCount).toFixed(4));
obj[key].value = val;
if (item.children) {
item.children.push(obj[key]);
} else {
item.children = [obj[key]];
}
}
}
}
treeForeach([res], (node: StackElement) => {
if (node.children) {
let val = 0;
for (const child of node.children) {
val = child.value + val;
}
node.value = node.value < val ? val : node.value;
}
});
return res;
}
function treeForeach(tree: StackElement[], func: (node: StackElement) => void) {
for (const data of tree) {
data.children && treeForeach(data.children, func);
func(data);
}
return tree;
}
watch(
() => ebpfStore.analyzeTrees,
() => {
drawGraph();
}
);
</script>
<style>
#graph-stack {
width: 100%;
height: 100%;
cursor: pointer;
}
.tip {
display: inline-block;
width: 100%;
text-align: center;
color: red;
margin-top: 20px;
}
.name {
word-wrap: break-word;
}
</style>

View File

@@ -0,0 +1,168 @@
<!-- 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
http://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. -->
<template>
<div class="ebpf-task" v-if="eBPFStore.couldProfiling">
<div>
<div class="label">{{ t("labels") }}</div>
<Selector
class="profile-input"
size="small"
:value="labels"
:options="eBPFStore.labels"
placeholder="Select labels"
:multiple="true"
@change="changeLabel"
/>
</div>
<div>
<div class="label">{{ t("targetType") }}</div>
<Selector
class="profile-input"
size="small"
:value="type"
:options="TargetTypes"
placeholder="Select a type"
:isRemote="true"
@change="changeType"
/>
</div>
<div>
<div class="label">{{ t("monitorTime") }}</div>
<div>
<Radio
:value="monitorTime"
:options="InitTaskField.monitorTimeEn"
@change="changeMonitorTime"
/>
<span class="date">
<TimePicker
:value="time"
position="bottom"
format="YYYY-MM-DD HH:mm:ss"
@input="changeTimeRange"
/>
</span>
</div>
</div>
<div>
<div class="label">{{ t("monitorDuration") }}</div>
<el-input
class="profile-input"
v-model="monitorDuration"
size="small"
placeholder="none"
type="number"
:min="1"
:max="60"
/>
Min
</div>
<div>
<el-button @click="createTask" type="primary" class="create-task-btn">
{{ t("createTask") }}
</el-button>
</div>
</div>
<div v-else>{{ t("ebpfTip") }}</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { ElMessage } from "element-plus";
import { InitTaskField, TargetTypes } from "./data";
/* global defineEmits */
const emits = defineEmits(["close"]);
const eBPFStore = useEbpfStore();
const selectorStore = useSelectorStore();
const appStore = useAppStoreWithOut();
const { t } = useI18n();
const labels = ref<string[]>([]);
const type = ref<string>(TargetTypes[0].value);
const monitorTime = ref<string>(InitTaskField.monitorTimeEn[0].value);
const monitorDuration = ref<number>(10);
const time = ref<Date>(appStore.durationRow.start);
const disabled = ref<boolean>(false);
function changeMonitorTime(opt: string) {
monitorTime.value = opt;
}
function changeLabel(opt: any[]) {
labels.value = opt.map((d) => d.value);
}
function changeType(opt: any[]) {
type.value = opt[0].value;
}
async function createTask() {
if (disabled.value) {
return;
}
disabled.value = true;
const date = monitorTime.value === "0" ? new Date() : time.value;
const params = {
serviceId: selectorStore.currentService.id,
processLabels: labels.value,
startTime: date.getTime(),
duration: monitorDuration.value * 60,
targetType: type.value,
};
const res = await eBPFStore.createTask(params);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
disabled.value = false;
if (!res.data.createTaskData.status) {
ElMessage.error(res.data.createTaskData.errorReason);
return;
}
ElMessage.success("Task created successfully");
emits("close");
}
function changeTimeRange(val: Date) {
time.value = val;
}
</script>
<style lang="scss" scoped>
.ebpf-task {
margin: 0 auto;
width: 400px;
}
.date {
font-size: 12px;
}
.label {
margin-top: 10px;
font-size: 14px;
}
.profile-input {
width: 300px;
}
.create-task-btn {
width: 300px;
margin-top: 50px;
}
</style>

View File

@@ -0,0 +1,207 @@
<!-- 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
http://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. -->
<template>
<div class="profile-task-list flex-v">
<div class="profile-task-wrapper flex-v">
<div class="profile-t-tool flex-h">{{ t("taskList") }}</div>
<div class="profile-t-wrapper">
<div class="no-data" v-show="!ebpfStore.taskList.length">
{{ t("noData") }}
</div>
<table class="profile-t">
<tr
class="profile-tr cp"
v-for="(i, index) in ebpfStore.taskList"
@click="changeTask(i)"
:key="index"
>
<td
class="profile-td"
:class="{
selected: selectedTask.taskId === i.taskId,
}"
>
<div class="ell">
<span>
{{
i.targetType +
": " +
(i.processLabels.length
? i.processLabels.join(" ")
: `All Processes`)
}}
</span>
<a class="profile-btn r" @click="viewDetail = true">
<Icon iconName="view" size="middle" />
</a>
</div>
<div class="grey ell sm">
<span class="mr-10 sm">{{ dateFormat(i.taskStartTime) }}</span>
<span class="mr-10 sm">
{{
dateFormat(i.taskStartTime + i.fixedTriggerDuration * 1000)
}}
</span>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<el-dialog
v-model="viewDetail"
:destroy-on-close="true"
fullscreen
@closed="viewDetail = false"
>
<div class="profile-detail flex-v">
<div>
<h5 class="mb-10">{{ t("task") }}.</h5>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("taskId") }}:</span>
<span class="g-sm-8 wba">
{{ selectedTask.taskId }}
</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("service") }}:</span>
<span class="g-sm-8 wba">{{ selectedTask.serviceName }}</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("labels") }}:</span>
<span class="g-sm-8 wba">
{{ selectedTask.processLabels.join(";") }}
</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("monitorTime") }}:</span>
<span class="g-sm-8 wba">
{{ dateFormat(selectedTask.taskStartTime) }}
</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span>
<span class="g-sm-8 wba">
{{ selectedTask.fixedTriggerDuration / 60 }} min
</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("triggerType") }}:</span>
<span class="g-sm-8 wba">{{ selectedTask.triggerType }}</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("targetType") }}:</span>
<span class="g-sm-8 wba">{{ selectedTask.targetType }}</span>
</div>
</div>
</div>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { EBPFTaskList } from "@/types/ebpf";
import { ElMessage } from "element-plus";
const { t } = useI18n();
const ebpfStore = useEbpfStore();
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
const selectedTask = ref<EBPFTaskList | Record<string, never>>({});
const viewDetail = ref<boolean>(false);
async function changeTask(item: EBPFTaskList) {
selectedTask.value = item;
ebpfStore.setSelectedTask(item);
const res = await ebpfStore.getEBPFSchedules({
taskId: item.taskId,
});
if (res.errors) {
ElMessage.error(res.errors);
}
}
watch(
() => ebpfStore.taskList,
() => {
selectedTask.value = ebpfStore.taskList[0] || {};
ebpfStore.setSelectedTask(selectedTask.value);
}
);
</script>
<style lang="scss" scoped>
.profile-task-list {
width: 300px;
height: calc(100% - 10px);
overflow: auto;
border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.item span {
height: 21px;
}
.profile-td {
padding: 5px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
&.selected {
background-color: #ededed;
}
}
.no-data {
text-align: center;
margin-top: 10px;
}
.profile-t-wrapper {
overflow: auto;
flex-grow: 1;
}
.profile-t {
width: 100%;
border-spacing: 0;
table-layout: fixed;
flex-grow: 1;
position: relative;
border: none;
}
.profile-tr {
&:hover {
background-color: rgba(0, 0, 0, 0.04);
}
}
.profile-t-tool {
padding: 5px 10px;
font-weight: bold;
border-right: 1px solid rgba(0, 0, 0, 0.07);
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
background: #f3f4f9;
}
.profile-btn {
color: #3d444f;
padding: 1px 3px;
border-radius: 2px;
font-size: 12px;
float: right;
}
</style>

Some files were not shown because too many files have changed in this diff Show More