已关闭
实例监控codecheck #383
AtomGit-Bot创建于 2023年8月25日关闭于 2023年8月28日
实例监控codecheck #383
已关闭
AtomGit-Bot创建于 2023年8月25日关闭于 2023年8月28日
refs/pull/383/head合入到master
164 个文件变更+11936-9940
Mplugins/observability-instance/InstanceExporter/pom.xml+2-2
@@ -43,8 +43,8 @@
43 <!-- https://mvnrepository.com/artifact/org.apache.sshd/sshd-scp -->43 <!-- https://mvnrepository.com/artifact/org.apache.sshd/sshd-scp -->
44 <dependency>44 <dependency>
45 <groupId>org.apache.sshd</groupId>45 <groupId>org.apache.sshd</groupId>
46- <artifactId>sshd-sftp</artifactId>46+ <artifactId>sshd-core</artifactId>
47- <version>2.9.2</version>47+ <version>2.10.0</version>
48 </dependency>48 </dependency>
49 <dependency>49 <dependency>
50 <groupId>org.opengauss</groupId>50 <groupId>org.opengauss</groupId>
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/controller/Command.java+79-1
@@ -1,10 +1,14 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.controller;5package org.opengauss.plugin.agent.controller;
5 6 
6import java.io.FileNotFoundException;7import java.io.FileNotFoundException;
7import java.io.IOException;8import java.io.IOException;
9+import java.math.BigDecimal;
10+import java.math.RoundingMode;
11+import java.text.DecimalFormat;
8import java.util.ArrayList;12import java.util.ArrayList;
9import java.util.Arrays;13import java.util.Arrays;
10import java.util.Collections;14import java.util.Collections;
@@ -14,15 +18,18 @@ import java.util.HashSet;
14import java.util.List;18import java.util.List;
15import java.util.Map;19import java.util.Map;
16import java.util.Set;20import java.util.Set;
21+import java.util.stream.Collectors;
17 22 
18import org.opengauss.plugin.agent.config.DbConfig;23import org.opengauss.plugin.agent.config.DbConfig;
19import org.opengauss.plugin.agent.util.CmdUtil;24import org.opengauss.plugin.agent.util.CmdUtil;
25+import org.opengauss.plugin.agent.util.DbUtil;
20import org.opengauss.plugin.agent.util.StringUtil;26import org.opengauss.plugin.agent.util.StringUtil;
21import org.springframework.web.bind.annotation.GetMapping;27import org.springframework.web.bind.annotation.GetMapping;
22import org.springframework.web.bind.annotation.RequestMapping;28import org.springframework.web.bind.annotation.RequestMapping;
23import org.springframework.web.bind.annotation.RequestParam;29import org.springframework.web.bind.annotation.RequestParam;
24import org.springframework.web.bind.annotation.RestController;30import org.springframework.web.bind.annotation.RestController;
25 31 
32+import cn.hutool.core.util.ArrayUtil;
26import cn.hutool.core.util.StrUtil;33import cn.hutool.core.util.StrUtil;
27import lombok.AllArgsConstructor;34import lombok.AllArgsConstructor;
28 35 
@@ -30,9 +37,14 @@ import lombok.AllArgsConstructor;
30@RequestMapping("/cmd")37@RequestMapping("/cmd")
31@AllArgsConstructor38@AllArgsConstructor
32public class Command {39public class Command {
40+ private static final String PID = "PID";
33 private static final String TOP = "top -b -n 1";41 private static final String TOP = "top -b -n 1";
34 private static final String TOP_DB_PID = "netstat -nap|grep :'|grep LISTEN";42 private static final String TOP_DB_PID = "netstat -nap|grep :'|grep LISTEN";
35 private static final String TOP_DB_THREAD = "top -H -bn1 -p ";43 private static final String TOP_DB_THREAD = "top -H -bn1 -p ";
44+ private static final String PS = "ps -aux";
45+ private static final String NETSTAT = "netstat -tulnp|grep gauss";
46+ private static final String TOTAL_MEMORY = "grep MemTotal /proc/meminfo | awk '{print $2 * 1024}'";
47+ private final DbUtil dbUtil;
36 private final DbConfig dbConfig;48 private final DbConfig dbConfig;
37 49 
38 @GetMapping("/top")50 @GetMapping("/top")
@@ -53,6 +65,7 @@ public class Command {
53 }65 }
54 top.add(obj);66 top.add(obj);
55 });67 });
68+ // db thread pids
56 Set<String> pids = new HashSet<>();69 Set<String> pids = new HashSet<>();
57 CmdUtil.readFromCmd(TOP_DB_PID.replaceAll("'", dbConfig.getDbport().toString()), line -> {70 CmdUtil.readFromCmd(TOP_DB_PID.replaceAll("'", dbConfig.getDbport().toString()), line -> {
58 var part = StringUtil.splitByBlank(line);71 var part = StringUtil.splitByBlank(line);
@@ -60,12 +73,67 @@ public class Command {
60 pid = pid.split("/")[0];73 pid = pid.split("/")[0];
61 pids.add(pid);74 pids.add(pid);
62 });75 });
76+ // full command
77+ CmdUtil.readFromCmd(PS, line -> {
78+ var part = StringUtil.splitByBlank(line);
79+ for (Map<String, String> map : top) {
80+ if (map.get(PID).equals(part[1])) {
81+ map.put("FullCommand",
82+ StrUtil.join(StrUtil.SPACE, (Object[]) ArrayUtil.sub(part, 10, part.length - 1)));
83+ return;
84+ }
85+ }
86+ });
87+ // db port
88+ // eg: tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN 3825204/gaussdb
89+ Map<String, Set<String>> ports = new HashMap<>();
90+ CmdUtil.readFromCmd(NETSTAT, (i, line) -> {
91+ if (i < 2 && !line.startsWith("tcp")) {
92+ return;
93+ }
94+ var part = StringUtil.splitByBlank(line);
95+ if (part.length < 7) {
96+ return;
97+ }
98+ // 5432
99+ String[] ipPort = part[3].split(StrUtil.COLON);
100+ // 3825204, 5432
101+ String key = part[6].split(StrUtil.SLASH)[0];
102+ if (!ports.containsKey(key)) {
103+ ports.put(key, new HashSet<>());
104+ }
105+ ports.get(key).add(ipPort[ipPort.length - 1]);
106+ });
107+ top.forEach(map -> {
108+ String pid = map.get(PID);
109+ if ("gaussdb".equals(map.get("COMMAND")) && ports.containsKey(pid)) {
110+ map.put("port", String.valueOf(ports.get(pid).stream().mapToInt(Integer::parseInt).min().orElse(0)));
111+ }
112+ });
113+ 
114+ // thread session/query id
115+ List<Map<String, Object>> sessions = dbUtil.query("select s.lwtid , a.sessionid , a.query_id "
116+ + "from pg_stat_activity a, dbe_perf.thread_wait_status s where a.sessionid = s.sessionid");
117+ Map<String, Map<String, Object>> sessionMap = sessions.stream()
118+ .collect(Collectors.toMap(map -> map.get("lwtid").toString(), map -> map));
119+ // thread memory
120+ BigDecimal totalMemory = new BigDecimal(CmdUtil.readFromCmd(TOTAL_MEMORY)).divide(new BigDecimal(100));
121+ List<Map<String, Object>> memList = dbUtil.query("select s.lwtid , sum(usedsize) used "
122+ + "from GS_SESSION_MEMORY_DETAIL d, dbe_perf.thread_wait_status s "
123+ + "where d.sessid like '%.' || s.tid group by s.lwtid,d.sessid");
124+ DecimalFormat decimalFormat = new DecimalFormat("#0.0");
125+ Map<String, Object> memMap = memList.stream()
126+ .collect(Collectors.toMap(map -> map.get("lwtid").toString(), map -> decimalFormat.format(
127+ new BigDecimal(map.get("used").toString()).divide(totalMemory, 2, RoundingMode.HALF_UP))));
128+ // thread
129+ // e.g. 3825204 omm 20 0 7461264 490508 170264 S 0.0 3.1 2:32.81 gaussdb
63 List<Map<String, String>> db_top = new ArrayList<>();130 List<Map<String, String>> db_top = new ArrayList<>();
64 if (pids.size() != 0) {131 if (pids.size() != 0) {
65 var pid = pids.iterator().next();132 var pid = pids.iterator().next();
66 CmdUtil.readFromCmd(TOP_DB_THREAD + pid, (i, line) -> {133 CmdUtil.readFromCmd(TOP_DB_THREAD + pid, (i, line) -> {
67- if (i < 6)134+ if (i < 6) {
68 return;135 return;
136+ }
69 String[] part = StringUtil.splitByBlank(line);137 String[] part = StringUtil.splitByBlank(line);
70 if (i == 6) {138 if (i == 6) {
71 header.clear();139 header.clear();
@@ -76,9 +144,19 @@ public class Command {
76 for (int j = 0; j < header.size(); j++) {144 for (int j = 0; j < header.size(); j++) {
77 obj.put(header.get(j), part[j]);145 obj.put(header.get(j), part[j]);
78 }146 }
147+ if (obj.containsKey(PID)) {
148+ var dbTid = obj.get(PID).toString();
149+ if (sessionMap.containsKey(dbTid)) {
150+ sessionMap.get(dbTid).forEach((key, value) -> {
151+ obj.put(key, value.toString());
152+ });
153+ }
154+ obj.put("%MEM", memMap.getOrDefault(dbTid, "0").toString());
155+ }
79 db_top.add(obj);156 db_top.add(obj);
80 });157 });
81 }158 }
159+ 
82 if (StrUtil.isNotBlank(sort)) {160 if (StrUtil.isNotBlank(sort)) {
83 var comparator = new Comparator<Map<String, String>>() {161 var comparator = new Comparator<Map<String, String>>() {
84 @Override162 @Override
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/controller/Config.java+2-1
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.controller;5package org.opengauss.plugin.agent.controller;
5 6 
6import java.io.File;7import java.io.File;
@@ -36,7 +37,7 @@ public class Config {
36 if (map == null)37 if (map == null)
37 map = new HashMap<>();38 map = new HashMap<>();
38 map.put("conf", Map.of("hostId", hostId, "node",39 map.put("conf", Map.of("hostId", hostId, "node",
39- Map.of("nodeId", nodeId, "dbport", dbport, "username", username, "password", password)));40+ Map.of("nodeId", nodeId, "dbport", dbport, "dbUsername", username, "dbPassword", password)));
40 // refresh curr config41 // refresh curr config
41 dbConfig.setHostId(hostId).setNodeId(nodeId).setDbport(dbport).setDbUsername(username)42 dbConfig.setHostId(hostId).setNodeId(nodeId).setDbport(dbport).setDbUsername(username)
42 .setDbPassword(password);43 .setDbPassword(password);
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/exception/ExporterException.java+30-0
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.agent.exception;
6+ 
7+/**
8+ * ExporterException.java
9+ *
10+ * @since 2023-08-25
11+ */
12+public class ExporterException extends RuntimeException {
13+ private static final long serialVersionUID = 1L;
14+ 
15+ public ExporterException() {
16+ super();
17+ }
18+ 
19+ public ExporterException(String message) {
20+ super(message);
21+ }
22+ 
23+ public ExporterException(String message, Throwable cause) {
24+ super(message, cause);
25+ }
26+ 
27+ public ExporterException(Throwable cause) {
28+ super(cause);
29+ }
30+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/opengauss/DBStatus.java+48-0
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.agent.metric.opengauss;
6+ 
7+import java.io.FileNotFoundException;
8+import java.io.IOException;
9+import java.util.Collections;
10+import java.util.HashMap;
11+import java.util.List;
12+import java.util.Map;
13+ 
14+import org.opengauss.plugin.agent.metric.DBmetric;
15+import org.opengauss.plugin.agent.metric.Metric;
16+import org.opengauss.plugin.agent.util.DbUtil;
17+import org.springframework.stereotype.Service;
18+ 
19+import io.prometheus.client.Collector;
20+import lombok.RequiredArgsConstructor;
21+ 
22+/**
23+ * DBStatus
24+ *
25+ * @since 2023/8/7 11:46
26+ */
27+@Service
28+@RequiredArgsConstructor
29+public class DBStatus implements DBmetric {
30+ private static final String SQL = "select 1 as count";
31+ 
32+ private final DbUtil dbUtil;
33+ 
34+ @Override
35+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
36+ if (dbPort == null) {
37+ return Collections.emptyMap();
38+ }
39+ Map<String, Metric> map = new HashMap<>();
40+ List<Map<String, Object>> query = dbUtil.query(SQL);
41+ Integer count = 0;
42+ if (query.get(0).get("count") instanceof Integer) {
43+ count = (Integer) query.get(0).get("count");
44+ }
45+ map.put("pg_db_status", new Metric(Collector.Type.GAUGE, null).addValue(null, count));
46+ return map;
47+ }
48+}
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/opengauss/OpengaussExporter.java+14-1
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.InitializingBean;
25import org.springframework.core.io.ClassPathResource;25import org.springframework.core.io.ClassPathResource;
26import org.springframework.scheduling.annotation.Scheduled;26import org.springframework.scheduling.annotation.Scheduled;
27import org.springframework.stereotype.Service;27import org.springframework.stereotype.Service;
28+import org.springframework.util.StringUtils;
28import org.yaml.snakeyaml.Yaml;29import org.yaml.snakeyaml.Yaml;
29 30 
30import cn.hutool.core.bean.BeanUtil;31import cn.hutool.core.bean.BeanUtil;
@@ -70,7 +71,7 @@ public class OpengaussExporter implements DBmetric, InitializingBean {
70 return metricData;71 return metricData;
71 }72 }
72 73 
73- @Scheduled(fixedRate = 15000, initialDelay = 1000)74+ @Scheduled(fixedDelay = 15000, initialDelay = 1000)
74 public void catchMetric() {75 public void catchMetric() {
75 if (CONFIG.size() == 0) {76 if (CONFIG.size() == 0) {
76 return;77 return;
@@ -97,6 +98,9 @@ public class OpengaussExporter implements DBmetric, InitializingBean {
97 private Map<String, Metric> metric(Entry<String, queryInstance> conf) {98 private Map<String, Metric> metric(Entry<String, queryInstance> conf) {
98 String sql = null;99 String sql = null;
99 for (query q : conf.getValue().getQuery()) {100 for (query q : conf.getValue().getQuery()) {
101+ if (!isRoleMatched(q)) {
102+ continue;
103+ }
100 if (q.getStatus() == state.enable) {104 if (q.getStatus() == state.enable) {
101 sql = q.getSql();105 sql = q.getSql();
102 break;106 break;
@@ -143,6 +147,15 @@ public class OpengaussExporter implements DBmetric, InitializingBean {
143 return result;147 return result;
144 }148 }
145 149 
150+ private boolean isRoleMatched(query q) {
151+ String dbRole = q.getDbRole();
152+ if (!StringUtils.hasLength(dbRole)) {
153+ return true;
154+ }
155+ List<Map<String, Object>> query = dbUtil.query("SELECT pg_is_in_recovery();");
156+ return ((boolean) query.get(0).get("pg_is_in_recovery")) != "primary".equalsIgnoreCase(dbRole);
157+ }
158+ 
146 @Data159 @Data
147 public static class queryInstance {160 public static class queryInstance {
148 private String name;161 private String name;
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/VmStat.java+4-2
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.metric.system;5package org.opengauss.plugin.agent.metric.system;
5 6 
6import java.io.FileNotFoundException;7import java.io.FileNotFoundException;
@@ -38,12 +39,13 @@ public class VmStat implements OSmetric {
38 "us",39 "us",
39 "sy",40 "sy",
40 "wa",41 "wa",
41- "st" };42+ "st"
43+ };
42 44 
43 @Override45 @Override
44 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {46 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
45 Map<String, Metric> map = new HashMap<>();47 Map<String, Metric> map = new HashMap<>();
46- CmdUtil.readFromCmd(CmdUtil.cmd("vmstat"), (index, line) -> {48+ CmdUtil.readFromCmd("vmstat", (index, line) -> {
47 if (index < 2)49 if (index < 2)
48 return;50 return;
49 var part = StringUtil.splitByBlank(line);51 var part = StringUtil.splitByBlank(line);
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/memory/Free.java+12-4
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.metric.system.memory;5package org.opengauss.plugin.agent.metric.system.memory;
5 6 
6import java.io.FileNotFoundException;7import java.io.FileNotFoundException;
@@ -9,22 +10,29 @@ import java.util.HashMap;
9import java.util.Map;10import java.util.Map;
10 11 
11import org.opengauss.plugin.agent.metric.Metric;12import org.opengauss.plugin.agent.metric.Metric;
13+import org.opengauss.plugin.agent.metric.OSmetric;
12import org.opengauss.plugin.agent.util.CmdUtil;14import org.opengauss.plugin.agent.util.CmdUtil;
13import org.opengauss.plugin.agent.util.StringUtil;15import org.opengauss.plugin.agent.util.StringUtil;
14import org.springframework.stereotype.Service;16import org.springframework.stereotype.Service;
15 17 
16-import org.opengauss.plugin.agent.metric.OSmetric;
17- 
18import io.prometheus.client.Collector.Type;18import io.prometheus.client.Collector.Type;
19 19 
20@Service("free_")20@Service("free_")
21public class Free implements OSmetric {21public class Free implements OSmetric {
22- private static final String[] KEYS = { "type", "total", "used", "free", "shared", "cache", "available" };22+ private static final String[] KEYS = {
23+ "type",
24+ "total",
25+ "used",
26+ "free",
27+ "shared",
28+ "cache",
29+ "available"
30+ };
23 31 
24 @Override32 @Override
25 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {33 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
26 Map<String, Metric> map = new HashMap<>();34 Map<String, Metric> map = new HashMap<>();
27- CmdUtil.readFromCmd(CmdUtil.cmd("free"), (index, line) -> {35+ CmdUtil.readFromCmd("free", (index, line) -> {
28 if (index == 0) {36 if (index == 0) {
29 // total used free shared buff/cache available37 // total used free shared buff/cache available
30 return;38 return;
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/network/Socket.java+3-10
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.metric.system.network;5package org.opengauss.plugin.agent.metric.system.network;
5 6 
6import java.io.FileNotFoundException;7import java.io.FileNotFoundException;
@@ -10,12 +11,11 @@ import java.util.HashMap;
10import java.util.Map;11import java.util.Map;
11 12 
12import org.opengauss.plugin.agent.metric.Metric;13import org.opengauss.plugin.agent.metric.Metric;
14+import org.opengauss.plugin.agent.metric.OSmetric;
13import org.opengauss.plugin.agent.util.CmdUtil;15import org.opengauss.plugin.agent.util.CmdUtil;
14import org.opengauss.plugin.agent.util.StringUtil;16import org.opengauss.plugin.agent.util.StringUtil;
15import org.springframework.stereotype.Service;17import org.springframework.stereotype.Service;
16 18 
17-import org.opengauss.plugin.agent.metric.OSmetric;
18- 
19import io.prometheus.client.Collector.Type;19import io.prometheus.client.Collector.Type;
20 20 
21@Service("network_socket")21@Service("network_socket")
@@ -24,14 +24,8 @@ public class Socket implements OSmetric {
24 24 
25 @Override25 @Override
26 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {26 public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
27- // TODO
28-// try {
29-// CmdUtil.readFromCmd(CmdUtil.cmd("ss -e"), line -> {
30-//
31-// });
32-// } catch (IOException e) {
33 Map<String, Integer> counter = new HashMap<>();27 Map<String, Integer> counter = new HashMap<>();
34- CmdUtil.readFromCmd(CmdUtil.cmd("netstat"), (index, line) -> {28+ CmdUtil.readFromCmd("netstat", (index, line) -> {
35 if (index < 2)29 if (index < 2)
36 return;30 return;
37 var part = StringUtil.splitByBlank(line);31 var part = StringUtil.splitByBlank(line);
@@ -43,7 +37,6 @@ public class Socket implements OSmetric {
43 counter.put(key, counter.getOrDefault(key, 0) + 1);37 counter.put(key, counter.getOrDefault(key, 0) + 1);
44 }38 }
45 });39 });
46-// }
47 var metric = new Metric(Type.GAUGE, Arrays.asList("proto", "state"));40 var metric = new Metric(Type.GAUGE, Arrays.asList("proto", "state"));
48 counter.forEach((key, value) -> {41 counter.forEach((key, value) -> {
49 var part = key.split(SPLIT);42 var part = key.split(SPLIT);
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/server/HostMetric.java+2-1
@@ -83,7 +83,7 @@ public class HostMetric implements ApplicationRunner {
83 return StringUtil.replaceParenthesis((collector instanceof DBmetric) ? k : "agent_" + collectorName + k);83 return StringUtil.replaceParenthesis((collector instanceof DBmetric) ? k : "agent_" + collectorName + k);
84 }84 }
85 85 
86- @Scheduled(fixedRate = 1000)86+ @Scheduled(fixedDelay = 5000)
87 public void cache() {87 public void cache() {
88 try {88 try {
89 collectors.forEach((collectorName, collector) -> {89 collectors.forEach((collectorName, collector) -> {
@@ -128,6 +128,7 @@ public class HostMetric implements ApplicationRunner {
128 }128 }
129 CACHE.put(collectorName, list);129 CACHE.put(collectorName, list);
130 });130 });
131+ log.info("refresh metric cache!");
131 } catch (Exception e) {132 } catch (Exception e) {
132 log.error("metric cache err", e);133 log.error("metric cache err", e);
133 }134 }
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/CmdUtil.java+47-24
@@ -1,60 +1,83 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.util;5package org.opengauss.plugin.agent.util;
5 6 
6import java.io.BufferedReader;7import java.io.BufferedReader;
7import java.io.FileNotFoundException;8import java.io.FileNotFoundException;
8import java.io.IOException;9import java.io.IOException;
9import java.io.InputStreamReader;10import java.io.InputStreamReader;
10-import java.util.Arrays;11+import java.util.EnumSet;
11import java.util.function.BiConsumer;12import java.util.function.BiConsumer;
12import java.util.function.Consumer;13import java.util.function.Consumer;
13 14 
15+import org.apache.sshd.client.SshClient;
16+import org.apache.sshd.client.channel.ClientChannelEvent;
17+import org.apache.sshd.client.session.ClientSession;
18+import org.opengauss.plugin.agent.exception.ExporterException;
19+ 
20+import cn.hutool.core.thread.ThreadUtil;
21+import cn.hutool.core.util.StrUtil;
14import lombok.extern.log4j.Log4j2;22import lombok.extern.log4j.Log4j2;
15 23 
16@Log4j224@Log4j2
17public class CmdUtil {25public class CmdUtil {
18- public static final String[] cmd(String... cmd) {26+ private static final int SESSION_TIMEOUT = 10000;
19- return cmd;27+ private static final int CHANNEL_TIMEOUT = 1000 * 60 * 5;
28+ 
29+ private static SshClient client;
30+ private static ClientSession session;
31+ 
32+ static {
33+ try {
34+ client = SshClient.setUpDefaultClient();
35+ client.start();
36+ session = client.connect("root", "127.0.0.1", 22).verify().getSession();
37+ session.addPasswordIdentity("Ncti@001122");
38+ session.auth().verify(SESSION_TIMEOUT);
39+ log.info("server init success");
40+ } catch (Exception e) {
41+ e.printStackTrace();
42+ }
20 }43 }
21 44 
22 public static final void readFromCmd(String cmd, Consumer<String> consumer)45 public static final void readFromCmd(String cmd, Consumer<String> consumer)
23 throws FileNotFoundException, IOException {46 throws FileNotFoundException, IOException {
24- readFromCmd(Arrays.asList("sh", "-c", cmd).toArray(new String[3]), consumer);47+ if (consumer != null) {
48+ readFromCmd(cmd, (i, line) -> consumer.accept(line));
49+ }
25 }50 }
26 51 
27 public static final void readFromCmd(String cmd, BiConsumer<Integer, String> consumer)52 public static final void readFromCmd(String cmd, BiConsumer<Integer, String> consumer)
28 throws FileNotFoundException, IOException {53 throws FileNotFoundException, IOException {
29- readFromCmd(Arrays.asList("sh", "-c", cmd).toArray(new String[3]), consumer);54+ log.debug("exec:" + cmd);
30- }55+ var channel = session.createExecChannel(cmd);
56+ channel.setPtyType("ansi");
57+ channel.setPtyColumns(300);
58+ channel.setPtyWidth(300);
59+ channel.open();
31 60 
32- public static final void readFromCmd(String[] cmd, Consumer<String> consumer)61+ channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), CHANNEL_TIMEOUT);
33- throws FileNotFoundException, IOException {62+ for (int i = 0; i < 100 && channel.getExitStatus() == null; i++)
34- if (consumer != null)63+ ThreadUtil.sleep(100L);
35- readFromCmd(cmd, (i, line) -> consumer.accept(line));64+ if (channel.getExitStatus() != null && channel.getExitStatus() != 0) {
36- }65+ throw new ExporterException(cmd + StrUtil.SPACE + StrUtil.LF + StrUtil.SPACE + channel.getInvertedErr());
37- 
38- public static final void readFromCmd(String[] cmd, BiConsumer<Integer, String> consumer)
39- throws FileNotFoundException, IOException {
40- var process = new ProcessBuilder().command(cmd).start();
41- try {
42- process.waitFor();
43- } catch (InterruptedException e) {
44- log.error("", e);
45- return;
46 }66 }
47- try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()));) {67+ 
68+ try (var reader = new BufferedReader(new InputStreamReader(channel.getInvertedOut()));) {
48 int i = -1;69 int i = -1;
49 while (reader.ready()) {70 while (reader.ready()) {
50 i++;71 i++;
51 String line = reader.readLine();72 String line = reader.readLine();
52- if (line.isBlank())73+ if (line.isBlank()) {
53 continue;74 continue;
54- if (consumer != null)75+ }
76+ if (consumer != null) {
55 consumer.accept(i, line);77 consumer.accept(i, line);
78+ }
56 }79 }
57- } catch (Exception e) {80+ } catch (IOException e) {
58 log.error("cmd '{}' format error", cmd, e);81 log.error("cmd '{}' format error", cmd, e);
59 throw e;82 throw e;
60 }83 }
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/DbUtil.java+5-6
@@ -32,14 +32,13 @@ public class DbUtil {
32 synchronized (this) {32 synchronized (this) {
33 try {33 try {
34 if (conn == null) {34 if (conn == null) {
35- conn = DriverManager35+ conn = DriverManager.getConnection(
36- .getConnection(36+ "jdbc:opengauss://" + "localhost" + ":" + dbConfig.getDbport() + "/" + "postgres"
37- "jdbc:opengauss://" + "localhost" + ":" + dbConfig.getDbport() + "/"37+ + "?TimeZone=UTC&ApplicationName=DataKit Instance Monitoring Agent",
38- + "postgres"38+ dbConfig.getDbUsername(), dbConfig.getDbPassword());
39- + "?TimeZone=UTC&ApplicationName=DataKit Instance Monitoring Agent",
40- dbConfig.getDbUsername(), dbConfig.getDbPassword());
41 }39 }
42 } catch (SQLException e) {40 } catch (SQLException e) {
41+ log.error("db connection fail", e);
43 conn = null;42 conn = null;
44 }43 }
45 }44 }
Mplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/FileUtil.java+2-1
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package org.opengauss.plugin.agent.util;5package org.opengauss.plugin.agent.util;
5 6 
6import java.io.FileNotFoundException;7import java.io.FileNotFoundException;
@@ -23,6 +24,6 @@ public class FileUtil {
23 */24 */
24 public static final void readFileLine(String name, BiConsumer<Integer, String> consumer)25 public static final void readFileLine(String name, BiConsumer<Integer, String> consumer)
25 throws FileNotFoundException, IOException {26 throws FileNotFoundException, IOException {
26- CmdUtil.readFromCmd(CmdUtil.cmd("cat", name), consumer);27+ CmdUtil.readFromCmd("cat " + name, consumer);
27 }28 }
28}29}
Mplugins/observability-instance/InstanceExporter/src/main/resources/og_exporter.yml+185-9
@@ -1547,7 +1547,102 @@ gauss_thread_wait_status:
1547 desc: openGauss database thread metrics1547 desc: openGauss database thread metrics
1548 query:1548 query:
1549 - name: gauss_thread_wait_status1549 - name: gauss_thread_wait_status
1550- sql: select wait_status, count(*) count from pg_thread_wait_status group by wait_status;1550+ sql: |-
1551+ select
1552+ *
1553+ from
1554+ (
1555+ select
1556+ WAIT_STATUS,
1557+ COUNT(*) COUNT
1558+ from
1559+ PG_THREAD_WAIT_STATUS
1560+ where
1561+ WAIT_STATUS not like 'SET CMD%'
1562+ and WAIT_STATUS not like 'WAIT NODE%'
1563+ and WAIT_STATUS not like 'POOLER CREATE CONN%'
1564+ and WAIT_STATUS not like 'FLUSH DATA%'
1565+ and WAIT_STATUS not like 'STREAM GET CONN%'
1566+ and WAIT_STATUS not like 'WAIT PRODUCER READY%'
1567+ and WAIT_STATUS not like 'analyze:%'
1568+ and wait_status not like 'vacuum:%'
1569+ and wait_status not like 'vacuum full:%'
1570+ group by
1571+ WAIT_STATUS
1572+ union all
1573+ select
1574+ 'SET CMD',
1575+ COUNT(*) COUNT
1576+ from
1577+ PG_THREAD_WAIT_STATUS
1578+ where
1579+ WAIT_STATUS like 'SET CMD%'
1580+ union all
1581+ select
1582+ 'WAIT NODE',
1583+ COUNT(*) COUNT
1584+ from
1585+ PG_THREAD_WAIT_STATUS
1586+ where
1587+ WAIT_STATUS like 'WAIT NODE%'
1588+ union all
1589+ select
1590+ 'POOLER CREATE CONN',
1591+ COUNT(*) COUNT
1592+ from
1593+ PG_THREAD_WAIT_STATUS
1594+ where
1595+ WAIT_STATUS like 'POOLER CREATE CONN%'
1596+ union all
1597+ select
1598+ 'FLUSH DATA',
1599+ COUNT(*) COUNT
1600+ from
1601+ PG_THREAD_WAIT_STATUS
1602+ where
1603+ WAIT_STATUS like 'FLUSH DATA%'
1604+ union all
1605+ select
1606+ 'STREAM GET CONN',
1607+ COUNT(*) COUNT
1608+ from
1609+ PG_THREAD_WAIT_STATUS
1610+ where
1611+ WAIT_STATUS like 'STREAM GET CONN%'
1612+ union all
1613+ select
1614+ 'analyze',
1615+ COUNT(*) COUNT
1616+ from
1617+ PG_THREAD_WAIT_STATUS
1618+ where
1619+ WAIT_STATUS like 'analyze%'
1620+ union all
1621+ select
1622+ 'WAIT PRODUCER READY',
1623+ COUNT(*) COUNT
1624+ from
1625+ PG_THREAD_WAIT_STATUS
1626+ where
1627+ WAIT_STATUS like 'WAIT PRODUCER READY%'
1628+ union all
1629+ select
1630+ 'vacuum',
1631+ COUNT(*) COUNT
1632+ from
1633+ PG_THREAD_WAIT_STATUS
1634+ where
1635+ WAIT_STATUS like 'vacuum:%'
1636+ union all
1637+ select
1638+ 'vacuum full',
1639+ COUNT(*) COUNT
1640+ from
1641+ PG_THREAD_WAIT_STATUS
1642+ where
1643+ WAIT_STATUS like 'vacuum full:%')
1644+ where
1645+ count != 0;
1551 version: '>=0.0.0'1646 version: '>=0.0.0'
1552 timeout: 11647 timeout: 1
1553 ttl: 601648 ttl: 60
@@ -2536,13 +2631,13 @@ pg_stat_activity_slow:
2536 query:2631 query:
2537 - name: count2632 - name: count
2538 sql: |-2633 sql: |-
2539- select count(1) count2634+ select count(1) count
2540- from pg_stat_activity2635+ from pg_stat_activity
2541- where2636+ where
2542- pid <> pg_backend_pid()2637+ pid <> pg_backend_pid()
2543- and xact_start is not null2638+ and xact_start is not null
2544- and state = 'active'2639+ and state = 'active'
2545- and extract(epoch from now() - xact_start)>32640+ and extract(epoch from now() - xact_start)>3
2546 version: '>=0.0.0'2641 version: '>=0.0.0'
2547 timeout: 102642 timeout: 10
2548 ttl: 36002643 ttl: 3600
@@ -2551,4 +2646,85 @@ pg_stat_activity_slow:
2551 metrics:2646 metrics:
2552 - name: count2647 - name: count
2553 description: index scans initiated on this index2648 description: index scans initiated on this index
2554- usage: GAUGE2649+ usage: GAUGE
2650+ 
2651+pg_wal_write_total:
2652+ name: count
2653+ query:
2654+ - name: count
2655+ sql: select round(pg_xlog_location_diff(pg_current_xlog_location(), '0/0')/1024) as count
2656+ version: '>=0.0.0'
2657+ timeout: 10
2658+ ttl: 3600
2659+ dbRole: "primary"
2660+ status: enable
2661+ metrics:
2662+ - name: count
2663+ description: index scans initiated on this index
2664+ usage: GAUGE
2665+ 
2666+pg_wal_send_pressure:
2667+ name: count
2668+ query:
2669+ - name: count
2670+ sql: |-
2671+ select round(sum(pg_xlog_location_diff(sender_write_location, sender_sent_location))/1024) as count
2672+ from pg_stat_get_wal_senders();
2673+ version: '>=0.0.0'
2674+ timeout: 10
2675+ ttl: 3600
2676+ dbRole: "primary"
2677+ status: enable
2678+ metrics:
2679+ - name: count
2680+ description: index scans initiated on this index
2681+ usage: GAUGE
2682+ 
2683+pg_wal_delay:
2684+ name: count
2685+ query:
2686+ - name: count
2687+ sql: |-
2688+ select round(pg_xlog_location_diff (sender_sent_location,receiver_received_location)/1024) received,
2689+ round(pg_xlog_location_diff (sender_write_location,receiver_write_location)/1024) as write,
2690+ round(pg_xlog_location_diff(sender_replay_location,receiver_replay_location)/1024) replay
2691+ from pg_stat_get_wal_receiver()
2692+ version: '>=0.0.0'
2693+ timeout: 10
2694+ ttl: 3600
2695+ dbRole: "standby"
2696+ status: enable
2697+ metrics:
2698+ - name: received
2699+ description: index scans initiated on this index
2700+ usage: GAUGE
2701+ - name: write
2702+ description: index scans initiated on this index
2703+ usage: GAUGE
2704+ - name: replay
2705+ description: index scans initiated on this index
2706+ usage: GAUGE usage: GAUGE
2707+
2708+pg_tablespace:
2709+ name: pg_tablespace
2710+ desc: openGauss tablespace
2711+ query:
2712+ - name: pg_tablespace
2713+ sql: |-
2714+ SELECT spcname as name, pg_tablespace_size(spcname)/1024/1024 AS size FROM pg_tablespace;
2715+ version: '>=0.0.0'
2716+ timeout: 0.1
2717+ ttl: 10
2718+ status: enable
2719+ dbRole: ""
2720+ metrics:
2721+ - name: name
2722+ description: tablespace name
2723+ usage: LABEL
2724+ - name: size
2725+ description: tablespace size
2726+ usage: GAUGE
2727+ status: enable
2728+ ttl: 60
2729+ timeout: 0.1
2730+ public: true
Mplugins/observability-instance/pom.xml+4-1
@@ -11,6 +11,7 @@
11 <plugin-id>observability-instance</plugin-id>11 <plugin-id>observability-instance</plugin-id>
12 <npm.run.script>buildNoTest</npm.run.script>12 <npm.run.script>buildNoTest</npm.run.script>
13 <web.build.skip>false</web.build.skip>13 <web.build.skip>false</web.build.skip>
14+ <build.frontend.skip>false</build.frontend.skip>
14 <!--project-->15 <!--project-->
15 <java.version>11</java.version>16 <java.version>11</java.version>
16 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>17 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -22,7 +23,6 @@
22 <fastjson.version>1.2.78</fastjson.version>23 <fastjson.version>1.2.78</fastjson.version>
23 <lombok.version>1.18.24</lombok.version>24 <lombok.version>1.18.24</lombok.version>
24 <hutool.version>5.0.3</hutool.version>25 <hutool.version>5.0.3</hutool.version>
25- <build.frontend.skip>false</build.frontend.skip>
26 </properties>26 </properties>
27 <dependencies>27 <dependencies>
28 <!-- same as framework -->28 <!-- same as framework -->
@@ -241,6 +241,9 @@
241 <source>${java.version}</source>241 <source>${java.version}</source>
242 <target>${java.version}</target>242 <target>${java.version}</target>
243 <encoding>${project.build.sourceEncoding}</encoding>243 <encoding>${project.build.sourceEncoding}</encoding>
244+ <compilerArgs>
245+ <arg>-parameters</arg>
246+ </compilerArgs>
244 </configuration>247 </configuration>
245 </plugin>248 </plugin>
246 <plugin>249 <plugin>
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/ObservabilityPluginApplication.java+6-1
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance;5package com.nctigba.observability.instance;
5 6 
6import org.springframework.boot.autoconfigure.SpringBootApplication;7import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -8,7 +9,9 @@ import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
8import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;9import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
9import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;10import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
10import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;11import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
12+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
11import org.springframework.cache.annotation.EnableCaching;13import org.springframework.cache.annotation.EnableCaching;
14+import org.springframework.context.annotation.EnableAspectJAutoProxy;
12 15 
13import com.gitee.starblues.bootstrap.SpringPluginBootstrap;16import com.gitee.starblues.bootstrap.SpringPluginBootstrap;
14 17 
@@ -17,8 +20,10 @@ import com.gitee.starblues.bootstrap.SpringPluginBootstrap;
17 HibernateJpaAutoConfiguration.class,20 HibernateJpaAutoConfiguration.class,
18 RedisAutoConfiguration.class,21 RedisAutoConfiguration.class,
19 RedisRepositoriesAutoConfiguration.class,22 RedisRepositoriesAutoConfiguration.class,
20- org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class })23+ SecurityAutoConfiguration.class
24+})
21@EnableCaching25@EnableCaching
26+@EnableAspectJAutoProxy
22public class ObservabilityPluginApplication extends SpringPluginBootstrap {27public class ObservabilityPluginApplication extends SpringPluginBootstrap {
23 28 
24 public static void main(String[] args) {29 public static void main(String[] args) {
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/aop/DatasourceAspect.java+82-0
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.aop;
6+ 
7+import java.util.Map;
8+ 
9+import org.aspectj.lang.ProceedingJoinPoint;
10+import org.aspectj.lang.Signature;
11+import org.aspectj.lang.annotation.Around;
12+import org.aspectj.lang.annotation.Aspect;
13+import org.aspectj.lang.reflect.MethodSignature;
14+import org.springframework.stereotype.Component;
15+ 
16+import com.nctigba.observability.instance.service.ClusterManager;
17+ 
18+import cn.hutool.core.bean.BeanUtil;
19+import cn.hutool.core.util.StrUtil;
20+import lombok.RequiredArgsConstructor;
21+ 
22+/**
23+ * aop for switch data source, using nodeId or clusterId
24+ *
25+ * @since 202381
26+ */
27+@Aspect
28+@Component
29+@RequiredArgsConstructor
30+public class DatasourceAspect {
31+ private final ClusterManager clusterManager;
32+ 
33+ /**
34+ * {@code @Ds @interface} used by this join point
35+ *
36+ * @param joinPoint point cut object
37+ * @return target method result
38+ * @throws Throwable target method Throwable
39+ */
40+ @Around("@annotation(com.nctigba.observability.instance.aop.Ds)")
41+ public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
42+ var args = joinPoint.getArgs();
43+ Signature signature = joinPoint.getSignature();
44+ Class<?> declaringType = signature.getDeclaringType();
45+ 
46+ Ds annotation = declaringType.getMethod(signature.getName(), getParameterTypes(joinPoint))
47+ .getAnnotation(Ds.class);
48+ int index = annotation.index();
49+ if (args.length <= index) {
50+ index = args.length - 1;
51+ }
52+ var obj = args[index];
53+ String path = annotation.value();
54+ Object nodeId;
55+ if (StrUtil.isNotBlank(path)) {
56+ if (obj instanceof Map) {
57+ nodeId = ((Map<?, ?>) obj).get(path);
58+ } else {
59+ nodeId = BeanUtil.getProperty(obj, path);
60+ }
61+ } else {
62+ nodeId = obj;
63+ }
64+ try {
65+ clusterManager.setCurrentDatasource(nodeId.toString());
66+ return joinPoint.proceed();
67+ } finally {
68+ clusterManager.pool();
69+ }
70+ }
71+ 
72+ private Class<?>[] getParameterTypes(ProceedingJoinPoint joinPoint) {
73+ Signature signature = joinPoint.getSignature();
74+ if (signature instanceof MethodSignature) {
75+ MethodSignature methodSignature = (MethodSignature) signature;
76+ return methodSignature.getMethod().getParameterTypes();
77+ }
78+ 
79+ // unused for code check
80+ return DatasourceAspect.class.getClasses();
81+ }
82+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/aop/Ds.java+29-0
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.aop;
6+ 
7+import java.lang.annotation.ElementType;
8+import java.lang.annotation.Retention;
9+import java.lang.annotation.RetentionPolicy;
10+import java.lang.annotation.Target;
11+ 
12+/**
13+ * The core Annotation to switch datasource. It can be annotated at method.
14+ *
15+ * @since 202381
16+ */
17+@Target(ElementType.METHOD)
18+@Retention(RetentionPolicy.RUNTIME)
19+public @interface Ds {
20+ /**
21+ * parameter index of nodeId
22+ */
23+ int index() default 0;
24+ 
25+ /**
26+ * if parameter is a map or a bean, key name or property
27+ */
28+ String value() default "";
29+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/MybatisPlusConfig.java+1-0
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.config;5package com.nctigba.observability.instance.config;
5 6 
6import org.springframework.context.annotation.Bean;7import org.springframework.context.annotation.Bean;
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/DatabaseType.java+0-24
@@ -1,24 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.constants;
5- 
6-import lombok.Getter;
7-import lombok.RequiredArgsConstructor;
8- 
9-@RequiredArgsConstructor
10-@Getter
11-public enum DatabaseType {
12- 
13- DEFAULT("openGauss"),
14- 
15- OPENGAUSS("openGauss"),
16- 
17- VASTBASE_G100("vastbase G100"),
18- 
19- MYSQL("mysql"),
20- 
21- ORACLE("oracle");
22- 
23- private final String dbType;
24-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MetricsLine.java+45-9
@@ -11,7 +11,7 @@ import lombok.Getter;
11@Getter11@Getter
12public enum MetricsLine {12public enum MetricsLine {
13 // index13 // index
14- CPU(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode!='idle',host='ogbrench'}[5m]))by (cpu))) * 100"),14+ CPU(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode!='idle',host='ogbrench'}[5m]))by (cpu,instance))) * 100"),
15 MEMORY(Type.OS,15 MEMORY(Type.OS,
16 "(1 - (agent_memory_MemAvailable_bytes{host='ogbrench'} /"16 "(1 - (agent_memory_MemAvailable_bytes{host='ogbrench'} /"
17 + " (agent_memory_MemTotal_bytes{host='ogbrench'}))) * 100"),17 + " (agent_memory_MemTotal_bytes{host='ogbrench'}))) * 100"),
@@ -23,16 +23,39 @@ public enum MetricsLine {
23 DB_ACTIVE_SESSION(Type.DB, "gauss_thread_wait_status_count{instanceId='ogbrench'}", "{wait_status}"),23 DB_ACTIVE_SESSION(Type.DB, "gauss_thread_wait_status_count{instanceId='ogbrench'}", "{wait_status}"),
24 24 
25 // CPU25 // CPU
26- CPU_TOTAL(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode!='idle',host='ogbrench'}[5m]))by (cpu))) * 100"),26+ CPU_TOTAL(Type.OS,
27- CPU_USER(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode='user',host='ogbrench'}[5m]))by (cpu))) * 100"),27+ "clamp_max((avg(sum(irate(agent_cpu_seconds_total"
28- CPU_SYSTEM(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode='system',host='ogbrench'}[5m]))by (cpu))) * 100"),28+ + "{mode!='idle',host='ogbrench'}[5m]))by (instance,cpu))by (instance)) * 100, 100)"),
29- CPU_IOWAIT(Type.OS, "(avg(sum(irate(agent_cpu_seconds_total{mode='iowait',host='ogbrench'}[5m]))by (cpu))) * 100"),29+ CPU_USER(Type.OS,
30+ "(avg(sum(irate(agent_cpu_seconds_total{mode='user',host='ogbrench'}[5m]))"
31+ + "by (instance,cpu))by (instance)) * 100"),
32+ CPU_SYSTEM(Type.OS,
33+ "(avg(sum(irate(agent_cpu_seconds_total{mode='system',host='ogbrench'}[5m]))"
34+ + "by (instance,cpu))by (instance)) * 100"),
35+ CPU_IOWAIT(Type.OS,
36+ "(avg(sum(irate(agent_cpu_seconds_total{mode='iowait',host='ogbrench'}[5m]))"
37+ + "by (instance,cpu))by (instance)) * 100"),
38+ CPU_IRQ(Type.OS,
39+ "(avg(sum(irate(agent_cpu_seconds_total{mode='irq',host='ogbrench'}[5m]))"
40+ + "by (instance,cpu))by (instance)) * 100"),
41+ CPU_SOFTIRQ(Type.OS,
42+ "(avg(sum(irate(agent_cpu_seconds_total{mode='softirq',host='ogbrench'}[5m]))"
43+ + "by (instance,cpu))by (instance)) * 100"),
44+ CPU_NICE(Type.OS,
45+ "(avg(sum(irate(agent_cpu_seconds_total{mode='nice',host='ogbrench'}[5m]))"
46+ + "by (instance,cpu))by (instance)) * 100"),
47+ CPU_STEAL(Type.OS,
48+ "(avg(sum(irate(agent_cpu_seconds_total{mode='steal',host='ogbrench'}[5m]))"
49+ + "by (instance,cpu))by (instance)) * 100"),
50+ CPU_IDLE(Type.OS,
51+ "(avg(sum(irate(agent_cpu_seconds_total{mode='idle',host='ogbrench'}[5m]))"
52+ + "by (instance,cpu))by (instance)) * 100"),
30 CPU_DB(Type.DB, "top_db_cpu{instanceId='ogbrench'}"),53 CPU_DB(Type.DB, "top_db_cpu{instanceId='ogbrench'}"),
31 54 
32- CPU_TOTAL_5M_LOAD(Type.OS, "sum(agent_load5{host='ogbrench'})"),55+ CPU_TOTAL_5M_LOAD(Type.OS, "sum(agent_load5{host='ogbrench'})by (instance)"),
33- CPU_TOTAL_CORE_NUM(Type.OS, "count(agent_cpu_seconds_total{mode='system',host='ogbrench'}) by (host)"),56+ CPU_TOTAL_CORE_NUM(Type.OS, "count(agent_cpu_seconds_total{mode='system',host='ogbrench'}) by (host,instance)"),
34 CPU_TOTAL_AVERAGE_UTILIZATION(Type.OS,57 CPU_TOTAL_AVERAGE_UTILIZATION(Type.OS,
35- "avg(rate(agent_cpu_seconds_total{mode!='idle',host='ogbrench'}[5m])) by (host) * 100"),58+ "avg(rate(agent_cpu_seconds_total{mode!='idle',host='ogbrench'}[5m])) by (host,instance) * 100"),
36 CPU_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='CPU_TIME'}[5m])"),59 CPU_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='CPU_TIME'}[5m])"),
37 NET_SEND_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='NET_SEND_TIME'}[5m])"),60 NET_SEND_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='NET_SEND_TIME'}[5m])"),
38 DATA_IO_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='DATA_IO_TIME'}[5m])"),61 DATA_IO_TIME(Type.OS, "increase(gauss_instance_time_value{host='ogbrench',stat_name='DATA_IO_TIME'}[5m])"),
@@ -47,6 +70,7 @@ public enum MetricsLine {
47 70 
48 // IO71 // IO
49 IOPS_R(Type.OS, "sum(rate(agent_disk_rd_ios_total{host='ogbrench'}[5m]))by(device)", "{device}"),72 IOPS_R(Type.OS, "sum(rate(agent_disk_rd_ios_total{host='ogbrench'}[5m]))by(device)", "{device}"),
73+ IOPS_R_TOTAL(Type.OS, "sum(rate(agent_disk_rd_ios_total{host='ogbrench'}[5m]))"),
50 IOPS_W(Type.OS, "sum(rate(agent_disk_wr_ios_total{host='ogbrench'}[5m]))by(device)", "{device}"),74 IOPS_W(Type.OS, "sum(rate(agent_disk_wr_ios_total{host='ogbrench'}[5m]))by(device)", "{device}"),
51 IO_DISK_READ_BYTES_PER_SECOND(Type.OS, "sum(rate(agent_disk_rd_sectors_total{host='ogbrench'}[5m]))by(device) *512",75 IO_DISK_READ_BYTES_PER_SECOND(Type.OS, "sum(rate(agent_disk_rd_sectors_total{host='ogbrench'}[5m]))by(device) *512",
52 "{device}"),76 "{device}"),
@@ -99,6 +123,10 @@ public enum MetricsLine {
99 INSTANCE_DB_CONNECTION_TOTAL(Type.DB, "pg_connections_max_conn{instanceId='ogbrench'}"),123 INSTANCE_DB_CONNECTION_TOTAL(Type.DB, "pg_connections_max_conn{instanceId='ogbrench'}"),
100 124 
101 INSTANCE_DB_SLOWSQL(Type.DB, "pg_stat_activity_slow_count{instanceId='ogbrench',state!='idle'}"),125 INSTANCE_DB_SLOWSQL(Type.DB, "pg_stat_activity_slow_count{instanceId='ogbrench',state!='idle'}"),
126+ INSTANCE_DB_RESPONSETIME_P80(Type.DB, "gauss_statement_responsetime_percentile_p80{instanceId="
127+ + "'ogbrench'}"),
128+ INSTANCE_DB_RESPONSETIME_P95(Type.DB, "gauss_statement_responsetime_percentile_p95{instanceId="
129+ + "'ogbrench'}"),
102 130 
103 // opengauss session131 // opengauss session
104 SESSION_MAX_CONNECTION(Type.DB, "pg_connections_max_conn{instanceId='ogbrench'}"),132 SESSION_MAX_CONNECTION(Type.DB, "pg_connections_max_conn{instanceId='ogbrench'}"),
@@ -107,7 +135,15 @@ public enum MetricsLine {
107 SESSION_WAITING_CONNECTION(Type.DB, "pg_state_activity_group_count{state='waiting',instanceId='ogbrench'}"),135 SESSION_WAITING_CONNECTION(Type.DB, "pg_state_activity_group_count{state='waiting',instanceId='ogbrench'}"),
108 136 
109 // wait event !pg_wait_events_total_wait_time137 // wait event !pg_wait_events_total_wait_time
110- WAIT_EVENT_COUNT(Type.DB, "gauss_wait_events_value{instanceId='ogbrench'}", "{type}");138+ WAIT_EVENT_COUNT(Type.DB, "gauss_wait_events_value{instanceId='ogbrench'}", "{type}"),
139+ 
140+ // cluster
141+ CLUSTER_PRIMARY_WAL_WRITE_TOTAL(Type.DB, "increase(pg_wal_write_total_count{instanceId='ogbrench'}[1d])"),
142+ CLUSTER_PRIMARY_WAL_SEND_PRESSURE(Type.DB, "pg_wal_send_pressure_count{instanceId='ogbrench'}"),
143+ CLUSTER_PRIMARY_WAL_WRITE_PER_SEC(Type.DB, "rate(pg_wal_write_total_count{instanceId='ogbrench'}[2m])"),
144+ CLUSTER_WAL_RECEIVED_DELAY(Type.DB, "pg_wal_delay_received{instanceId='ogbrench'}"),
145+ CLUSTER_WAL_WRITE_DELAY(Type.DB, "pg_wal_delay_write{instanceId='ogbrench'}"),
146+ CLUSTER_WAL_REPLAY_DELAY(Type.DB, "pg_wal_delay_replay{instanceId='ogbrench'}");
111 147 
112 private enum Type {148 private enum Type {
113 OS,149 OS,
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringConstants.java+0-12
@@ -1,12 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.constants;
5- 
6-public class MonitoringConstants {
7- // prometheus query ?query={query}
8- public static final String PROMETHEUS_QUERY_POINT = "/api/v1/query";
9- 
10- // prometheus range query ?query={query}&start={start}&end={end}&step={step}
11- public static final String PROMETHEUS_QUERY_RANGE = "/api/v1/query_range";
12-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringResultType.java+0-24
@@ -1,24 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.constants;
5- 
6-import lombok.Getter;
7- 
8-/**
9- * Monitoring data return type
10- *
11- * @author yangjie
12- */
13-@Getter
14-public enum MonitoringResultType {
15- /**
16- * table
17- */
18- TABLE,
19- 
20- /**
21- * line
22- */
23- LINE,
24-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringType.java+0-16
@@ -1,16 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.constants;
5- 
6-import lombok.Getter;
7-import lombok.RequiredArgsConstructor;
8- 
9-@Getter
10-@RequiredArgsConstructor
11-public enum MonitoringType {
12- 
13- DEFAULT("prometheus");
14- 
15- private final String monitoringType;
16-}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/StateColor.java+55-0
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.constants;
6+ 
7+import java.util.Arrays;
8+import java.util.Set;
9+import java.util.stream.Collectors;
10+ 
11+import lombok.Getter;
12+import lombok.RequiredArgsConstructor;
13+ 
14+/**
15+ *
16+ * StateColor.java
17+ *
18+ * @since 2023-08-25
19+ */
20+@RequiredArgsConstructor
21+@Getter
22+public enum StateColor {
23+ GREEN("cluster.state.value.Normal", "cluster.node.state.Normal", "cluster.node.syncState.Streaming",
24+ "OS.bin.state.TASK_RUNNING"),
25+ YELLOW("cluster.state.value.Degraded", "cluster.node.state.Need repair", "cluster.node.state.Starting",
26+ "cluster.node.state.Wait promoting", "cluster.node.state.Promoting", "cluster.node.state.Demoting",
27+ "cluster.node.state.Building", "cluster.node.state.Catchup", "cluster.node.state.Coredump",
28+ "cluster.node.syncState.Catchup", "OS.bin.state.TASK_INTERRUPTIBLE", "OS.bin.state.TASK_STOPPED",
29+ "OS.bin.state.TASK_TRACED", "OS.bin.state.TASK_UNINTERRUPTIBLE"),
30+ RED("cluster.state.value.Unavailable", "cluster.node.state.Unknown", "OS.bin.state.EXIT_ZOMBIE",
31+ "OS.bin.state.EXIT_DEAD", "OS.bin.state.STOP"),
32+ GREY("cluster.state.value.Unknown", "OS.bin.state.UNKNOWN");
33+ 
34+ private String[] state;
35+ 
36+ StateColor(String... states) {
37+ this.state = states;
38+ }
39+ 
40+ /**
41+ * getColor
42+ *
43+ * @param state state
44+ * @return color
45+ */
46+ public static StateColor getColor(String state) {
47+ for (StateColor color : StateColor.values()) {
48+ Set<String> states = Arrays.stream(color.state).collect(Collectors.toSet());
49+ if (states.contains(state)) {
50+ return color;
51+ }
52+ }
53+ return GREY;
54+ }
55+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/AspController.java+49-0
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.controller;
6+ 
7+import org.opengauss.admin.common.core.domain.AjaxResult;
8+import org.springframework.web.bind.annotation.GetMapping;
9+import org.springframework.web.bind.annotation.RequestMapping;
10+import org.springframework.web.bind.annotation.RestController;
11+ 
12+import com.nctigba.observability.instance.dto.asp.AspCountReq;
13+import com.nctigba.observability.instance.service.AspService;
14+ 
15+import lombok.RequiredArgsConstructor;
16+ 
17+/**
18+ * AspController.java
19+ *
20+ * @since 2023-08-25
21+ */
22+@RestController
23+@RequestMapping("/instanceMonitoring/api/v1/asp")
24+@RequiredArgsConstructor
25+public class AspController extends ControllerConfig {
26+ private final AspService aspService;
27+ 
28+ /**
29+ * list
30+ *
31+ * @param req req
32+ * @return AjaxResult
33+ */
34+ @GetMapping(value = "/count")
35+ public AjaxResult list(AspCountReq req) {
36+ return AjaxResult.success(aspService.count(req));
37+ }
38+ 
39+ /**
40+ * analysis
41+ *
42+ * @param req req
43+ * @return AjaxResult
44+ */
45+ @GetMapping(value = "/analysis")
46+ public AjaxResult analysis(AspCountReq req) {
47+ return AjaxResult.success(aspService.analysis(req));
48+ }
49+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ClusterOpsController.java+93-0
@@ -0,0 +1,93 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.controller;
6+ 
7+import org.opengauss.admin.common.core.domain.AjaxResult;
8+import org.springframework.web.bind.annotation.GetMapping;
9+import org.springframework.web.bind.annotation.PathVariable;
10+import org.springframework.web.bind.annotation.RequestMapping;
11+import org.springframework.web.bind.annotation.RestController;
12+ 
13+import com.nctigba.observability.instance.service.ClusterOpsService;
14+ 
15+import lombok.RequiredArgsConstructor;
16+ 
17+/**
18+ * ClusterOpsController.java
19+ *
20+ * @since 2023-08-25
21+ */
22+@RestController
23+@RequestMapping("/instanceMonitoring/api/v1/clusters")
24+@RequiredArgsConstructor
25+public class ClusterOpsController {
26+ private final ClusterOpsService clusterOpsService;
27+ 
28+ /**
29+ * list
30+ *
31+ * @return AjaxResult
32+ */
33+ @GetMapping(value = "/list")
34+ public AjaxResult list() {
35+ return AjaxResult.success(clusterOpsService.listClusters());
36+ }
37+ 
38+ /**
39+ * nodes
40+ *
41+ * @param clusterId clusterId
42+ * @return AjaxResult
43+ */
44+ @GetMapping(value = "/{clusterId}/nodes")
45+ public AjaxResult nodes(@PathVariable("clusterId") String clusterId) {
46+ return AjaxResult.success(clusterOpsService.nodes(clusterId));
47+ }
48+ 
49+ /**
50+ * relation
51+ *
52+ * @param clusterId clusterId
53+ * @return AjaxResult
54+ */
55+ @GetMapping(value = "/{clusterId}/relation")
56+ public AjaxResult relation(@PathVariable("clusterId") String clusterId) {
57+ return AjaxResult.success(clusterOpsService.relation(clusterId));
58+ }
59+ 
60+ /**
61+ * allClusterState
62+ *
63+ * @return AjaxResult
64+ */
65+ @GetMapping(value = "/allClusterState")
66+ public AjaxResult allClusterState() {
67+ return AjaxResult.success(clusterOpsService.allClusterState());
68+ }
69+ 
70+ /**
71+ * allStandbyNodes
72+ *
73+ * @return AjaxResult
74+ */
75+ @GetMapping(value = "/nodes")
76+ public AjaxResult allStandbyNodes() {
77+ return AjaxResult.success(clusterOpsService.allStandbyNodes());
78+ }
79+ 
80+ /**
81+ * clusterMetrics
82+ *
83+ * @param clusterId clusterId
84+ * @param start start
85+ * @param end end
86+ * @param step step
87+ * @return AjaxResult
88+ */
89+ @GetMapping(value = "/{clusterId}/metrics")
90+ public AjaxResult clusterMetrics(@PathVariable("clusterId") String clusterId, Long start, Long end, Integer step) {
91+ return AjaxResult.success(clusterOpsService.clusterMetrics(clusterId, start, end, step));
92+ }
93+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ControllerConfig.java+19-15
@@ -1,29 +1,33 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4-package com.nctigba.observability.instance.controller;
5 4 
6-import org.springframework.beans.propertyeditors.CustomDateEditor;5+package com.nctigba.observability.instance.controller;
7-import org.springframework.web.bind.WebDataBinder;
8-import org.springframework.web.bind.annotation.InitBinder;
9 6 
10import java.text.ParsePosition;7import java.text.ParsePosition;
11import java.text.SimpleDateFormat;8import java.text.SimpleDateFormat;
12import java.util.Date;9import java.util.Date;
13 10 
14-public class ControllerConfig {11+import org.springframework.beans.propertyeditors.CustomDateEditor;
12+import org.springframework.web.bind.WebDataBinder;
13+import org.springframework.web.bind.annotation.InitBinder;
14+ 
15+import cn.hutool.core.util.StrUtil;
16+ 
17+public abstract class ControllerConfig {
15 @InitBinder18 @InitBinder
16 public void initBinder(WebDataBinder webDataBinder) {19 public void initBinder(WebDataBinder webDataBinder) {
17- webDataBinder.registerCustomEditor(Date.class,20+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") {
18- new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") {21+ private static final long serialVersionUID = 1L;
19- private static final long serialVersionUID = 1L;
20 22 
21- @Override23+ @Override
22- public Date parse(String text, ParsePosition pos) {24+ public Date parse(String text, ParsePosition pos) {
23- if (org.apache.commons.lang3.StringUtils.isBlank(text))25+ if (StrUtil.isBlank(text))
24- return null;26+ return null;
25- return super.parse(text, pos);27+ return super.parse(text, pos);
26- }28+ }
27- }, true));29+ };
30+ dateFormat.setTimeZone(java.util.TimeZone.getTimeZone(java.time.ZoneOffset.UTC));
31+ webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
28 }32 }
29}33}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/IndexController.java+100-15
@@ -1,33 +1,65 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
7+import java.io.IOException;
6import java.util.HashMap;8import java.util.HashMap;
7-import java.util.List;
8import java.util.Map;9import java.util.Map;
9 10 
10import org.opengauss.admin.common.core.domain.AjaxResult;11import org.opengauss.admin.common.core.domain.AjaxResult;
12+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
13+import org.opengauss.admin.common.exception.CustomException;
14+import org.opengauss.admin.system.plugin.facade.HostFacade;
15+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
16+import org.opengauss.admin.system.service.ops.IOpsClusterService;
17+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
18+import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.web.bind.annotation.GetMapping;19import org.springframework.web.bind.annotation.GetMapping;
12import org.springframework.web.bind.annotation.RequestMapping;20import org.springframework.web.bind.annotation.RequestMapping;
13import org.springframework.web.bind.annotation.RestController;21import org.springframework.web.bind.annotation.RestController;
14 22 
15-import com.alibaba.fastjson.JSONObject;23+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
24+import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
25+import com.nctigba.observability.instance.aop.Ds;
16import com.nctigba.observability.instance.constants.MetricsLine;26import com.nctigba.observability.instance.constants.MetricsLine;
17-import com.nctigba.observability.instance.dto.topsql.TopSQLNowReq;27+import com.nctigba.observability.instance.mapper.DbConfigMapper;
28+import com.nctigba.observability.instance.service.ClusterManager;
18import com.nctigba.observability.instance.service.MetricsService;29import com.nctigba.observability.instance.service.MetricsService;
19import com.nctigba.observability.instance.service.SessionService;30import com.nctigba.observability.instance.service.SessionService;
20import com.nctigba.observability.instance.service.TopSQLService;31import com.nctigba.observability.instance.service.TopSQLService;
32+import com.nctigba.observability.instance.util.SshSession;
21 33 
22-import lombok.RequiredArgsConstructor;34+import cn.hutool.core.util.StrUtil;
35+import lombok.extern.log4j.Log4j2;
23 36 
24@RestController37@RestController
25@RequestMapping("/instanceMonitoring/api/v1/")38@RequestMapping("/instanceMonitoring/api/v1/")
26-@RequiredArgsConstructor39+@Log4j2
27public class IndexController extends ControllerConfig {40public class IndexController extends ControllerConfig {
28- private final MetricsService metricsService;41+ @Autowired
29- private final TopSQLService topSQLService;42+ private MetricsService metricsService;
30- private final SessionService sessionService;43+ @Autowired
44+ private TopSQLService topSQLService;
45+ @Autowired
46+ private SessionService sessionService;
47+ @Autowired
48+ private ClusterManager clusterManager;
49+ @Autowired
50+ private DbConfigMapper configMapper;
51+ @AutowiredType(Type.MAIN_PLUGIN)
52+ @Autowired
53+ private IOpsClusterService opsClusterService;
54+ @AutowiredType(Type.MAIN_PLUGIN)
55+ @Autowired
56+ private HostFacade hostFacade;
57+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
58+ @Autowired
59+ private HostUserFacade hostUserFacade;
60+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
61+ @Autowired
62+ private EncryptionUtils encryptionUtils;
31 63 
32 private static final MetricsLine[] MAIN = {64 private static final MetricsLine[] MAIN = {
33 MetricsLine.CPU,65 MetricsLine.CPU,
@@ -40,19 +72,72 @@ public class IndexController extends ControllerConfig {
40 MetricsLine.DB_ACTIVE_SESSION72 MetricsLine.DB_ACTIVE_SESSION
41 };73 };
42 74 
75+ /**
76+ * nodeInfo
77+ *
78+ * @param id id
79+ * @return AjaxResult
80+ */
81+ @GetMapping("nodeInfo")
82+ @Ds
83+ public AjaxResult nodeInfo(String id) {
84+ Map<String, Object> result = new HashMap<>();
85+ // cluster info
86+ var node = clusterManager.getOpsNodeById(id);
87+ result.put("version", configMapper.version());
88+ result.put("time", configMapper.starttime());
89+ var env = configMapper.env();
90+ result.put("dbDataPath", env.get("datapath"));
91+ result.put("dbLogPath", env.get("datapath") + StrUtil.SLASH + env.get("log_directory"));
92+ result.put("archiveMode", configMapper.archiveMode());
93+ OpsHostEntity hostEntity = hostFacade.getById(node.getHostId());
94+ var user = hostUserFacade.listHostUserByHostId(hostEntity.getHostId()).stream().filter(e -> {
95+ return node.getInstallUserName().equals(e.getUsername());
96+ }).findFirst().orElse(null);
97+ try (var session = SshSession.connect(hostEntity.getPublicIp(), hostEntity.getPort(), node.getInstallUserName(),
98+ encryptionUtils.decrypt(user.getPassword()));) {
99+ result.put("osVersion", session.execute("cat /etc/system-release"));
100+ result.put("CPUmanufacturer", session.execute("cat /proc/cpuinfo | grep 'vendor_id' | head -n 1 | "
101+ + "awk -F: '{print $2}' | sed 's/^[ \\t]*//'"));
102+ result.put("CPUmodel", session.execute("cat /proc/cpuinfo | grep 'model name' | head -n 1 | "
103+ + "awk -F: '{print $2}' | sed 's/^[ \\t]*//'"));
104+ result.put("CPUcores", session.execute("nproc"));
105+ result.put("TotalMemory", session.execute("free -h | awk '/^Mem:/ {print $2}'"));
106+ } catch (IOException | CustomException e) {
107+ log.error("", e);
108+ }
109+ return AjaxResult.success(result);
110+ }
111+ 
112+ /**
113+ * mainMetrics
114+ *
115+ * @param id id
116+ * @param start start
117+ * @param end end
118+ * @param step step
119+ * @return AjaxResult
120+ */
43 @GetMapping("mainMetrics")121 @GetMapping("mainMetrics")
44 public AjaxResult mainMetrics(String id, Long start, Long end, Integer step) {122 public AjaxResult mainMetrics(String id, Long start, Long end, Integer step) {
45- HashMap<String, Object> metrics = metricsService.listBatch(MAIN, id, start, end, step);123+ Map<String, Object> metrics = metricsService.listBatch(MAIN, id, start, end, step);
46- JSONObject simpleStatistic = sessionService.simpleStatistic(id);124+ var simpleStatistic = sessionService.simpleStatistic(id);
47 metrics.putAll(simpleStatistic);125 metrics.putAll(simpleStatistic);
48 return AjaxResult.success(metrics);126 return AjaxResult.success(metrics);
49 }127 }
50 128 
51- @GetMapping(value = "/topSQLNow")129+ /**
52- public AjaxResult topSQLNow(TopSQLNowReq topSQLNowReq) {130+ * topSQLNow
53- Map<String, List<JSONObject>> blockAndLongTxc = sessionService.blockAndLongTxc(topSQLNowReq.getId());131+ *
54- List<JSONObject> topSQLNow = topSQLService.getTopSQLNow(topSQLNowReq);132+ * @param id id
55- blockAndLongTxc.put("topSQLNow", topSQLNow);133+ * @return AjaxResult
134+ */
135+ @GetMapping(value = "topSQLNow")
136+ @Ds
137+ public AjaxResult topSQLNow(String id) {
138+ var blockAndLongTxc = sessionService.blockAndLongTxc(id);
139+ blockAndLongTxc.put("topSQLNow", topSQLService.topSQLNow(id));
140+ blockAndLongTxc.put("waitEvents", configMapper.waitEvents());
56 return AjaxResult.success(blockAndLongTxc);141 return AjaxResult.success(blockAndLongTxc);
57 }142 }
58}143}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/MonitoringController.java+0-212
@@ -1,212 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.controller;
5- 
6-import java.util.Arrays;
7-import java.util.HashMap;
8-import java.util.List;
9-import java.util.Map;
10-import java.util.concurrent.ArrayBlockingQueue;
11-import java.util.concurrent.CountDownLatch;
12-import java.util.concurrent.ThreadPoolExecutor;
13-import java.util.concurrent.TimeUnit;
14- 
15-import org.opengauss.admin.common.core.domain.AjaxResult;
16-import org.opengauss.admin.common.exception.CustomException;
17-import org.springframework.web.bind.annotation.GetMapping;
18-import org.springframework.web.bind.annotation.PostMapping;
19-import org.springframework.web.bind.annotation.RequestBody;
20-import org.springframework.web.bind.annotation.RequestMapping;
21-import org.springframework.web.bind.annotation.RestController;
22- 
23-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
24-import com.nctigba.observability.instance.service.MonitoringService;
25- 
26-import lombok.RequiredArgsConstructor;
27- 
28-/**
29- * Monitoring data
30- *
31- * @author zhanggr@vastdata.com.cn
32- * @since 2022/9/5 17:04
33- */
34-@RestController
35-@RequestMapping("/observability/v1/monitoring")
36-@RequiredArgsConstructor
37-public class MonitoringController {
38- private final MonitoringService monitoringService;
39- private final ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 50, 60, TimeUnit.SECONDS,
40- new ArrayBlockingQueue<>(100));
41- 
42- private static final List<String> names = Arrays.asList("cpu", "memory", "disk_read", "disk_written",
43- "network_transmit", "network_receive", "load5", "run_time", "mem", "cpu_cnt", "uname", "os");
44- private static final List<String> metrics = Arrays.asList(
45- "(avg(sum(irate(node_cpu_seconds_total{mode!=\"idle\",instance=\"ogbrench\"}[5m]))by (cpu))) * 100",
46- "(1 - (node_memory_MemAvailable_bytes{instance=\"ogbrench\"} / (node_memory_MemTotal_bytes{instance=\"ogbrench\"}))) * 100",
47- "max(rate(node_disk_read_bytes_total{instance=\"ogbrench\"}[5m])) by (instance)",
48- "max(rate(node_disk_written_bytes_total{instance=\"ogbrench\"}[5m])) by (instance)",
49- "max(rate(node_network_transmit_bytes_total{instance=\"ogbrench\"}[5m])*8) by (instance)",
50- "max(rate(node_network_receive_bytes_total{instance=\"ogbrench\"}[5m])*8) by (instance)",
51- "node_load5{instance=\"ogbrench\"}",
52- "sum(time() - node_boot_time_seconds{instance=\"ogbrench\"})by(instance)",
53- "node_memory_MemTotal_bytes{instance=\"ogbrench\"}",
54- "count(node_cpu_seconds_total{mode='system',instance=\"ogbrench\"}) by (instance)",
55- "node_uname_info{instance=\"ogbrench\"}", "node_os_info{instance=\"ogbrench\"}",
56- "sum(avg(node_filesystem_size_bytes{fstype=~\"xfs|ext.*\",instance=\"ogbrench\"})by(device,instance))");
57- private static final List<String> databaseNames = Arrays.asList("CPU_TIME", "NET_SEND_TIME", "DATA_IO_TIME",
58- "gauss_wait_events_value", "totalCoreNum", "total5mLoad", "totalAverageUtilization1", "diskIOUsage",
59- "systemUsage", "userUsage", "totalUsage", "totalMemory", "usedMemory", "totalAverageUtilization2",
60- "totalDisks", "totalNumber", "totalAverageUtilization3", "read1", "write1", "read2", "write2", "upload",
61- "download", "TCP_alloc", "CurrEstab", "Tcp_OutSegs", "Tcp_InSegs", "UDP_inuse", "TCP_tw", "Tcp_RetransSegs",
62- "Sockets_used", "transactionRollbackNum", "transactionCommitments", "transactionAndRollbackTotal",
63- "queryTransactions", "currentIdleConnections", "currentActiveConnections", "currentConnections",
64- "totalConnections", "slowSqlNum", "longTransactions", "sqlResponseTime80", "sqlResponseTime95",
65- "accessExclusiveLock", "accessShareLock", "ExclusiveLock", "ShareUpdateExclusiveLock",
66- "ShareRowExclusiveLock", "RowShareLock", "RowExclusiveLock", "ShareLock", "queryCacheHitRate",
67- "databaseCacheHitRate", "readPhysicalFileBlockNum", "writePhysicalFileBlockNum", "lastBatchDirtyPageNum",
68- "currentRemainingDirtyPages");
69- private static final List<String> databaseMetrics = Arrays.asList(
70- "increase(gauss_instance_time_value{instance='ogbrench',stat_name='CPU_TIME'}[5m])",
71- "increase(gauss_instance_time_value{instance='ogbrench',stat_name='NET_SEND_TIME'}[5m])",
72- "increase(gauss_instance_time_value{instance='ogbrench',stat_name='DATA_IO_TIME'}[5m])",
73- "gauss_wait_events_value{instance='ogbrench'}",
74- "count(node_cpu_seconds_total{mode='system',instance='ogbrench'}) by (instance)",
75- "sum(node_load5{instance='ogbrench'})",
76- "avg(rate(node_cpu_seconds_total{mode!='idle',instance='ogbrench'}[5m])) by (instance) * 100",
77- "(avg(sum(irate(node_cpu_seconds_total{mode='iowait',instance='ogbrench'}[5m]))by (cpu))) * 100",
78- "(avg(sum(irate(node_cpu_seconds_total{mode='system',instance='ogbrench'}[5m]))by (cpu))) * 100",
79- "(avg(sum(irate(node_cpu_seconds_total{mode='user',instance='ogbrench'}[5m]))by (cpu))) * 100",
80- "(avg(sum(irate(node_cpu_seconds_total{mode!='idle',instance='ogbrench'}[5m]))by (cpu))) * 100",
81- "node_memory_MemTotal_bytes{instance='ogbrench'}",
82- "node_memory_MemTotal_bytes{instance='ogbrench'} - node_memory_MemAvailable_bytes{instance='ogbrench'}",
83- "(node_memory_MemTotal_bytes{instance='ogbrench'} - node_memory_MemAvailable_bytes{instance='ogbrench'})/node_memory_MemTotal_bytes{instance='ogbrench'} * 100",
84- "sum(avg(node_filesystem_size_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance))",
85- "sum(avg(node_filesystem_size_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance)) - sum(avg(node_filesystem_free_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance))",
86- "(sum(avg(node_filesystem_size_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance)) - sum(avg(node_filesystem_free_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance))) /(sum(avg(node_filesystem_avail_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance))+(sum(avg(node_filesystem_size_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance)) - sum(avg(node_filesystem_free_bytes{fstype=~'xfs|ext.*',instance='ogbrench'})by(device,instance)))) * 100",
87- "sum(rate(node_disk_reads_completed_total{instance='ogbrench'}[5m]))",
88- "sum(rate(node_disk_writes_completed_total{instance='ogbrench'}[5m]))",
89- "sum(rate(node_disk_read_bytes_total{instance='ogbrench'}[5m]))",
90- "sum(rate(node_disk_written_bytes_total{instance='ogbrench'}[5m]))",
91- "sum(rate(node_network_transmit_bytes_total{instance='ogbrench'}[5m])*8)",
92- "sum(rate(node_network_receive_bytes_total{instance='ogbrench'}[5m])*8)",
93- "node_sockstat_TCP_alloc{instance='ogbrench'}", "node_netstat_Tcp_CurrEstab{instance='ogbrench'}",
94- "rate(node_netstat_Tcp_OutSegs{instance='ogbrench'}[5m])",
95- "rate(node_netstat_Tcp_InSegs{instance='ogbrench'}[5m])", "node_sockstat_UDP_inuse{instance='ogbrench'}",
96- "node_sockstat_TCP_tw{instance='ogbrench'}", "rate(node_netstat_Tcp_RetransSegs{instance='ogbrench'}[5m])",
97- "node_sockstat_sockets_used{instance='ogbrench'}",
98- "sum(irate(pg_stat_database_xact_rollback{instance='ogbrench'}[5m]))",
99- "sum(irate(pg_stat_database_xact_commit{instance='ogbrench'}[5m]))",
100- "sum(irate(pg_stat_database_xact_rollback{instance='ogbrench'}[5m])) + sum(irate(pg_stat_database_xact_commit{instance='ogbrench'}[5m]))",
101- "sum(rate(gauss_workload_sql_count_select_count{instance='ogbrench'}[5m]))",
102- "sum(pg_stat_activity_count{instance='ogbrench',state='idle'})",
103- "sum(pg_stat_activity_count{instance='ogbrench',state='active'})",
104- "sum(pg_stat_activity_count{instance='ogbrench'})", "pg_settings_max_connections{instance='ogbrench'}",
105- "sum(pg_stat_activity_max_tx_duration{instance='ogbrench',state!='idle'}) > bool 3",
106- "sum(pg_stat_activity_max_tx_duration{instance='ogbrench',state!='idle'}) > bool 30",
107- "gauss_statement_responsetime_percentile_p80{instance='ogbrench'}",
108- "gauss_statement_responsetime_percentile_p95{instance='ogbrench'}",
109- "sum(pg_lock_count{mode='AccessExclusiveLock',instance='ogbrench'})",
110- "sum(pg_lock_count{mode='AccessShareLock',instance='ogbrench'})",
111- "sum(pg_lock_count{mode='ExclusiveLock',instance='ogbrench'})",
112- "sum(pg_lock_count{mode='ShareUpdateExclusiveLock',instance='ogbrench'})",
113- "sum(pg_lock_count{mode='ShareRowExclusiveLock',instance='ogbrench'})",
114- "sum(pg_lock_count{mode='RowShareLock',instance='ogbrench'})",
115- "sum(pg_lock_count{mode='RowExclusiveLock',instance='ogbrench'})",
116- "sum(pg_lock_count{mode='ShareLock',instance='ogbrench'})",
117- "(gauss_query_statement_cache_hit_rate{instance='ogbrench'}) * 100",
118- "(sum(pg_stat_database_blks_hit{instance='ogbrench'}) / (sum(pg_stat_database_blks_hit{instance='ogbrench'}) + sum(pg_stat_database_blks_read{instance='ogbrench'}))) * 100",
119- "rate(gauss_summary_file_iostat_total_phyblkrd{instance='ogbrench'}[5m])",
120- "rate(gauss_summary_file_iostat_total_phyblkwrt{instance='ogbrench'}[5m])",
121- "sum(gauss_global_pagewriter_status_pgwr_last_flush_num{instance='ogbrench'})",
122- "sum(gauss_global_pagewriter_status_remain_dirty_page_num{instance='ogbrench'})");
123- 
124- @PostMapping("/point")
125- public AjaxResult point(@RequestBody MonitoringParam monitoringParam) {
126- // The specified time point can only be returned in table data format
127- return AjaxResult.success(monitoringService.getPointMonitoringData(monitoringParam));
128- }
129- 
130- @PostMapping("/range")
131- public AjaxResult range(@RequestBody MonitoringParam monitoringParam) {
132- return AjaxResult.success(monitoringService.getRangeMonitoringData(monitoringParam));
133- }
134- 
135- @GetMapping("/server-metric")
136- public AjaxResult pointMetrics(MonitoringParam monitoringParam) {
137- Map<String, Object> map = new HashMap<>();
138- int i = 0;
139- for (String metric : metrics) {
140- if (i > 6) {
141- break;
142- }
143- handlePoint(map, monitoringParam, metric, i++);
144- }
145- return AjaxResult.success(map);
146- }
147- 
148- @GetMapping("/server-info")
149- public AjaxResult pointInfo(MonitoringParam monitoringParam) {
150- Map<String, Object> map = new HashMap<>();
151- int i = 0;
152- for (String metric : metrics) {
153- if (i < 7) {
154- i++;
155- continue;
156- }
157- handlePoint(map, monitoringParam, metric, i++);
158- }
159- return AjaxResult.success(map);
160- }
161- 
162- private void handlePoint(Map<String, Object> map, MonitoringParam monitoringParam, String metric, int i) {
163- monitoringParam.setQuery(metric.replace("ogbrench", monitoringParam.getId()));
164- Map<String, Object> pointMonitoringData = monitoringService.getPointMonitoringData(monitoringParam);
165- if (pointMonitoringData.containsKey("value")) {
166- map.put(names.get(i), pointMonitoringData.get("value"));
167- } else {
168- map.putAll(pointMonitoringData);
169- }
170- }
171- 
172- public interface MyRunnable extends Runnable {
173- MyRunnable setName(String name);
174- }
175- 
176- @GetMapping("/database-metrics")
177- public AjaxResult getDatabaseMetrics(MonitoringParam monitoringParam) {
178- Map<String, Object> map = new HashMap<>();
179- int i = 0;
180- CountDownLatch countDownLatch = new CountDownLatch(databaseMetrics.size());
181- for (String metric : databaseMetrics) {
182- pool.submit(new MyRunnable() {
183- String name;
184- 
185- @Override
186- public MyRunnable setName(String name) {
187- this.name = name;
188- return this;
189- }
190- 
191- @Override
192- public void run() {
193- try {
194- monitoringParam.setQuery(metric.replace("ogbrench", monitoringParam.getId()));
195- if ("gauss_wait_events_value".equals(name)) {
196- monitoringParam.setLegendName("event");
197- }
198- map.put(name, monitoringService.getRangeMonitoringData(monitoringParam).get(0));
199- } finally {
200- countDownLatch.countDown();
201- }
202- }
203- }.setName(databaseNames.get(i++)));
204- }
205- try {
206- countDownLatch.await(10, TimeUnit.SECONDS);
207- } catch (InterruptedException e) {
208- throw new CustomException(e.getMessage());
209- }
210- return AjaxResult.success(map);
211- }
212-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/PageController.java+33-27
@@ -15,10 +15,10 @@ import org.springframework.web.bind.annotation.GetMapping;
15import org.springframework.web.bind.annotation.RequestMapping;15import org.springframework.web.bind.annotation.RequestMapping;
16import org.springframework.web.bind.annotation.RestController;16import org.springframework.web.bind.annotation.RestController;
17 17 
18+import com.nctigba.observability.instance.aop.Ds;
18import com.nctigba.observability.instance.constants.MetricsLine;19import com.nctigba.observability.instance.constants.MetricsLine;
19import com.nctigba.observability.instance.constants.MetricsValue;20import com.nctigba.observability.instance.constants.MetricsValue;
20import com.nctigba.observability.instance.mapper.DbConfigMapper;21import com.nctigba.observability.instance.mapper.DbConfigMapper;
21-import com.nctigba.observability.instance.service.ClusterManager;
22import com.nctigba.observability.instance.service.MetricsService;22import com.nctigba.observability.instance.service.MetricsService;
23import com.nctigba.observability.instance.util.Language;23import com.nctigba.observability.instance.util.Language;
24 24 
@@ -102,41 +102,47 @@ public class PageController extends ControllerConfig {
102 102 
103 private final MetricsService metricsService;103 private final MetricsService metricsService;
104 private final DbConfigMapper dbConfigMapper;104 private final DbConfigMapper dbConfigMapper;
105- private final ClusterManager clusterManager;
106 private final MessageSource messageSource;105 private final MessageSource messageSource;
107 private final Language language;106 private final Language language;
108 107 
109 @GetMapping("memory")108 @GetMapping("memory")
109+ @Ds
110 public Map<String, Object> memory(String id, Long start, Long end, Integer step) {110 public Map<String, Object> memory(String id, Long start, Long end, Integer step) {
111- HashMap<String, Object> batch = metricsService.listBatch(MEMORY, id, start, end, step);111+ Map<String, Object> batch = metricsService.listBatch(MEMORY, id, start, end, step);
112- try {112+ // memory node detail
113- clusterManager.setCurrentDatasource(id, null);113+ List<Map<String, Object>> memoryNodeDetail = dbConfigMapper.memoryNodeDetail();
114- // memory node detail114+ memoryNodeDetail.forEach(map -> {
115- List<Map<String, Object>> memoryNodeDetail = dbConfigMapper.memoryNodeDetail();115+ var str = map.get("memorytype").toString();
116- memoryNodeDetail.forEach(map -> {116+ map.put("desc", messageSource.getMessage("memory.node." + str, null, str, language.getLocale()));
117- var str = map.get("memorytype").toString();117+ });
118- map.put("desc", messageSource.getMessage("memory.node." + str, null, str, language.getLocale()));118+ batch.put("memoryNodeDetail", memoryNodeDetail);
119- });119+ // memory config detail
120- batch.put("memoryNodeDetail", memoryNodeDetail);120+ List<Map<String, Object>> memoryConfig = dbConfigMapper.memoryConfig();
121- // memory config detail121+ memoryConfig.forEach(map -> {
122- List<Map<String, Object>> memoryConfig = dbConfigMapper.memoryConfig();122+ var str = map.get("name").toString();
123- memoryConfig.forEach(map -> {123+ map.put("desc", messageSource.getMessage("memory.config." + str, null, str, language.getLocale()));
124- var str = map.get("name").toString();124+ });
125- map.put("desc", messageSource.getMessage("memory.config." + str, null, str, language.getLocale()));125+ // db memory
126- });126+ var total = Long.parseLong(batch.get(MetricsValue.MEM_TOTAL.name()).toString());
127- batch.put("memoryConfig", memoryConfig);127+ var percents = batch.get(MetricsLine.MEMORY_DB_USED.name());
128- } finally {128+ if (percents instanceof List) {
129- clusterManager.pool();129+ var listPercents = (List<?>) percents;
130+ var percent = listPercents.get(listPercents.size() - 1);
131+ if (percent instanceof Number) {
132+ batch.put("MEMORY_DB_USED_CURR", total * ((Number) percent).doubleValue() / 100);
133+ }
130 }134 }
135+ 
136+ batch.put("memoryConfig", memoryConfig);
131 return AjaxResult.success(batch);137 return AjaxResult.success(batch);
132 }138 }
133 139 
134 @SuppressWarnings("unchecked")140 @SuppressWarnings("unchecked")
135 @GetMapping("io")141 @GetMapping("io")
136 public Map<String, Object> io(String id, Long start, Long end, Integer step) {142 public Map<String, Object> io(String id, Long start, Long end, Integer step) {
137- HashMap<String, Object> io = metricsService.listBatch(IO, id, start, end, step);143+ Map<String, Object> io = metricsService.listBatch(IO, id, start, end, step);
138- HashMap<String, Object> table = metricsService.listBatch(IO_TABLE, id, start, end, step);144+ Map<String, Object> table = metricsService.listBatch(IO_TABLE, id, start, end, step);
139- HashMap<String, Object> lines = new HashMap<>();145+ Map<String, Object> lines = new HashMap<>();
140 for (MetricsValue metric : IO_TABLE) {146 for (MetricsValue metric : IO_TABLE) {
141 var map = (Map<String, Object>) table.get(metric.name());147 var map = (Map<String, Object>) table.get(metric.name());
142 if (map == null) {148 if (map == null) {
@@ -158,9 +164,9 @@ public class PageController extends ControllerConfig {
158 @SuppressWarnings("unchecked")164 @SuppressWarnings("unchecked")
159 @GetMapping("network")165 @GetMapping("network")
160 public Map<String, Object> network(String id, Long start, Long end, Integer step) {166 public Map<String, Object> network(String id, Long start, Long end, Integer step) {
161- HashMap<String, Object> network = metricsService.listBatch(NETWORK, id, start, end, step);167+ Map<String, Object> network = metricsService.listBatch(NETWORK, id, start, end, step);
162- HashMap<String, Object> table = metricsService.listBatch(NETWORK_TABLE, id, start, end, step);168+ Map<String, Object> table = metricsService.listBatch(NETWORK_TABLE, id, start, end, step);
163- HashMap<String, Object> lines = new HashMap<>();169+ Map<String, Object> lines = new HashMap<>();
164 for (MetricsValue metric : NETWORK_TABLE) {170 for (MetricsValue metric : NETWORK_TABLE) {
165 var map = (Map<String, Object>) table.get(metric.name());171 var map = (Map<String, Object>) table.get(metric.name());
166 if (map == null) {172 if (map == null) {
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ParamInfoController.java+10-8
@@ -1,19 +1,22 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
6-import com.nctigba.common.web.exception.InstanceException;7+import java.util.List;
7-import com.nctigba.observability.instance.dto.param.ParamInfoDTO;8+ 
8-import com.nctigba.observability.instance.model.param.ParamQuery;
9-import com.nctigba.observability.instance.service.ParamInfoService;
10-import com.nctigba.observability.instance.util.MessageSourceUtil;
11-import lombok.RequiredArgsConstructor;
12import org.springframework.web.bind.annotation.GetMapping;9import org.springframework.web.bind.annotation.GetMapping;
13import org.springframework.web.bind.annotation.RequestMapping;10import org.springframework.web.bind.annotation.RequestMapping;
14import org.springframework.web.bind.annotation.RestController;11import org.springframework.web.bind.annotation.RestController;
15 12 
16-import java.util.List;13+import com.nctigba.observability.instance.dto.param.ParamInfoDTO;
14+import com.nctigba.observability.instance.exception.InstanceException;
15+import com.nctigba.observability.instance.model.ParamQuery;
16+import com.nctigba.observability.instance.service.ParamInfoService;
17+import com.nctigba.observability.instance.util.MessageSourceUtil;
18+ 
19+import lombok.RequiredArgsConstructor;
17 20 
18/**21/**
19 * ParamInfo22 * ParamInfo
@@ -25,7 +28,6 @@ import java.util.List;
25@RequestMapping("/observability/v1/param")28@RequestMapping("/observability/v1/param")
26@RequiredArgsConstructor29@RequiredArgsConstructor
27public class ParamInfoController {30public class ParamInfoController {
28- 
29 private final ParamInfoService paramInfoService;31 private final ParamInfoService paramInfoService;
30 32 
31 @GetMapping(value = "/paramInfo")33 @GetMapping(value = "/paramInfo")
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ResourceCPUController.java+10-2
@@ -1,12 +1,14 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
6import java.io.IOException;7import java.io.IOException;
7import java.util.ArrayList;8import java.util.ArrayList;
8import java.util.HashMap;9import java.util.HashMap;
9import java.util.List;10import java.util.List;
11+import java.util.Map;
10 12 
11import org.opengauss.admin.common.core.domain.AjaxResult;13import org.opengauss.admin.common.core.domain.AjaxResult;
12import org.opengauss.admin.common.exception.CustomException;14import org.opengauss.admin.common.exception.CustomException;
@@ -45,14 +47,20 @@ public class ResourceCPUController extends ControllerConfig {
45 MetricsLine.CPU_USER,47 MetricsLine.CPU_USER,
46 MetricsLine.CPU_SYSTEM,48 MetricsLine.CPU_SYSTEM,
47 MetricsLine.CPU_IOWAIT,49 MetricsLine.CPU_IOWAIT,
50+ MetricsLine.CPU_IRQ,
51+ MetricsLine.CPU_SOFTIRQ,
52+ MetricsLine.CPU_NICE,
53+ MetricsLine.CPU_STEAL,
54+ MetricsLine.CPU_IDLE,
48 MetricsLine.CPU_DB,55 MetricsLine.CPU_DB,
49 MetricsLine.CPU_TOTAL_5M_LOAD,56 MetricsLine.CPU_TOTAL_5M_LOAD,
50- MetricsLine.CPU_TOTAL_CORE_NUM, };57+ MetricsLine.CPU_TOTAL_CORE_NUM,
58+ };
51 59 
52 @SuppressWarnings("unchecked")60 @SuppressWarnings("unchecked")
53 @GetMapping("cpu")61 @GetMapping("cpu")
54 public AjaxResult cpu(String id, Long start, Long end, Integer step) {62 public AjaxResult cpu(String id, Long start, Long end, Integer step) {
55- HashMap<String, Object> cpu = metricsService.listBatch(CPU, id, start, end, step);63+ Map<String, Object> cpu = metricsService.listBatch(CPU, id, start, end, step);
56 int core = 1;64 int core = 1;
57 for (Number n : (List<Number>) cpu.get(MetricsLine.CPU_TOTAL_CORE_NUM.name())) {65 for (Number n : (List<Number>) cpu.get(MetricsLine.CPU_TOTAL_CORE_NUM.name())) {
58 if (n.intValue() != 0) {66 if (n.intValue() != 0) {
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/SessionController.java+1-66
@@ -1,88 +1,23 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
6-import java.util.HashMap;
7-import java.util.List;
8-import java.util.Map;
9-import java.util.concurrent.ExecutionException;
10-import java.util.concurrent.Future;
11- 
12import org.opengauss.admin.common.core.domain.AjaxResult;7import org.opengauss.admin.common.core.domain.AjaxResult;
13-import org.opengauss.admin.common.exception.CustomException;
14import org.springframework.web.bind.annotation.GetMapping;8import org.springframework.web.bind.annotation.GetMapping;
15import org.springframework.web.bind.annotation.RequestMapping;9import org.springframework.web.bind.annotation.RequestMapping;
16import org.springframework.web.bind.annotation.RestController;10import org.springframework.web.bind.annotation.RestController;
17 11 
18-import com.alibaba.fastjson.JSONObject;
19-import com.nctigba.observability.instance.constants.MetricsLine;
20-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
21-import com.nctigba.observability.instance.service.MetricsService;
22-import com.nctigba.observability.instance.service.MonitoringService;
23import com.nctigba.observability.instance.service.SessionService;12import com.nctigba.observability.instance.service.SessionService;
24 13 
25-import cn.hutool.core.thread.ThreadUtil;
26import lombok.RequiredArgsConstructor;14import lombok.RequiredArgsConstructor;
27-import lombok.extern.slf4j.Slf4j;
28 15 
29@RestController16@RestController
30@RequestMapping("/instanceMonitoring/api/v1/session/")17@RequestMapping("/instanceMonitoring/api/v1/session/")
31@RequiredArgsConstructor18@RequiredArgsConstructor
32-@Slf4j
33public class SessionController {19public class SessionController {
34 private final SessionService sessionService;20 private final SessionService sessionService;
35- private final MetricsService metricsService;
36- private final MonitoringService monitoringService;
37- 
38- private static final MetricsLine[] SESSION_STATISTIC = {
39- MetricsLine.SESSION_MAX_CONNECTION,
40- MetricsLine.SESSION_IDLE_CONNECTION,
41- MetricsLine.SESSION_ACTIVE_CONNECTION,
42- MetricsLine.SESSION_WAITING_CONNECTION,
43- MetricsLine.CPU_TIME,
44- MetricsLine.NET_SEND_TIME,
45- MetricsLine.DATA_IO_TIME
46- };
47- 
48- @GetMapping("sessionStatistic")
49- public AjaxResult sessionStatistic(String id, Long start, Long end, Integer step) {
50- MonitoringParam monitoringParam = new MonitoringParam();
51- monitoringParam.setId(id);
52- monitoringParam.setStart(String.valueOf(start));
53- monitoringParam.setEnd(String.valueOf(end));
54- monitoringParam.setStep(String.valueOf(step));
55- monitoringParam.setQuery(MetricsLine.WAIT_EVENT_COUNT.promQl(null, id));
56- monitoringParam.setType("LINE");
57- monitoringParam.setLegendName("event");
58- Future<Object> waitingEventFuture = ThreadUtil
59- .execAsync(() -> monitoringService.getRangeMonitoringData(monitoringParam).get(0));
60- Future<JSONObject> simpleFuture = ThreadUtil.execAsync(() -> sessionService.simpleStatistic(id));
61- Future<HashMap<String, Object>> metricsFuture = ThreadUtil
62- .execAsync(() -> metricsService.listBatch(SESSION_STATISTIC, id, start, end, step));
63- JSONObject simple;
64- Object waitEvent;
65- Map<String, Object> metrics;
66- try {
67- waitEvent = waitingEventFuture.get();
68- simple = simpleFuture.get();
69- metrics = metricsFuture.get();
70- } catch (InterruptedException | ExecutionException e) {
71- log.error("", e);
72- throw new CustomException("", e);
73- }
74- Map<String, Object> map = new HashMap<>();
75- map.put("gauss_wait_events_value", waitEvent);
76- var time = (List<?>) metrics.get("time");
77- for (Map.Entry<String, Object> entry : metrics.entrySet()) {
78- if (entry.getValue() == null) {
79- entry.setValue(new int[time.size()]);
80- }
81- }
82- map.putAll(metrics);
83- map.putAll(simple);
84- return AjaxResult.success(map);
85- }
86 21 
87 @GetMapping(value = "blockAndLongTxc")22 @GetMapping(value = "blockAndLongTxc")
88 public AjaxResult blockAndLongTxc(String id) {23 public AjaxResult blockAndLongTxc(String id) {
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/TopSQLController.java+40-23
@@ -1,9 +1,12 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
7+import java.util.HashMap;
6import java.util.List;8import java.util.List;
9+import java.util.Map;
7 10 
8import org.opengauss.admin.common.core.domain.AjaxResult;11import org.opengauss.admin.common.core.domain.AjaxResult;
9import org.springframework.web.bind.annotation.GetMapping;12import org.springframework.web.bind.annotation.GetMapping;
@@ -11,10 +14,10 @@ import org.springframework.web.bind.annotation.PathVariable;
11import org.springframework.web.bind.annotation.RequestMapping;14import org.springframework.web.bind.annotation.RequestMapping;
12import org.springframework.web.bind.annotation.RestController;15import org.springframework.web.bind.annotation.RestController;
13 16 
14-import com.alibaba.fastjson.JSONObject;17+import com.nctigba.observability.instance.constants.MetricsLine;
15-import com.nctigba.observability.instance.dto.topsql.TopSQLInfoReq;
16import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;18import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;
17import com.nctigba.observability.instance.service.ClusterManager;19import com.nctigba.observability.instance.service.ClusterManager;
20+import com.nctigba.observability.instance.service.MetricsService;
18import com.nctigba.observability.instance.service.TopSQLService;21import com.nctigba.observability.instance.service.TopSQLService;
19 22 
20import lombok.RequiredArgsConstructor;23import lombok.RequiredArgsConstructor;
@@ -33,15 +36,11 @@ import lombok.RequiredArgsConstructor;
33public class TopSQLController {36public class TopSQLController {
34 private final TopSQLService topSQLService;37 private final TopSQLService topSQLService;
35 private final ClusterManager clusterManager;38 private final ClusterManager clusterManager;
36- 39+ private final MetricsService metricsService;
37- @GetMapping(value = "/connect/{nodeId}")
38- public AjaxResult testConnection(@PathVariable("nodeId") String nodeId) {
39- return AjaxResult.success(topSQLService.testConnection(nodeId));
40- }
41 40 
42 @GetMapping(value = "/list")41 @GetMapping(value = "/list")
43 public AjaxResult top10(TopSQLListReq topSQLListReq) {42 public AjaxResult top10(TopSQLListReq topSQLListReq) {
44- List<JSONObject> list = topSQLService.getTopSQLList(topSQLListReq);43+ var list = topSQLService.topSQLList(topSQLListReq);
45 if (list == null) {44 if (list == null) {
46 return AjaxResult.error("602", "top sql pre check fail");45 return AjaxResult.error("602", "top sql pre check fail");
47 }46 }
@@ -49,32 +48,50 @@ public class TopSQLController {
49 }48 }
50 49 
51 @GetMapping(value = "/detail")50 @GetMapping(value = "/detail")
52- public AjaxResult detail(TopSQLInfoReq topSQLDetailReq) {51+ public AjaxResult detail(String id, String sqlId) {
53- return AjaxResult.success(topSQLService.getStatisticalInfo(topSQLDetailReq));52+ return AjaxResult.success(topSQLService.detail(id, sqlId));
54 }53 }
55 54 
56 @GetMapping(value = "/plan")55 @GetMapping(value = "/plan")
57- public AjaxResult plan(TopSQLInfoReq topSQLPlanReq) {56+ public AjaxResult plan(String id, String sqlId) {
58- JSONObject res = topSQLService.getExecutionPlan(topSQLPlanReq, "");57+ var plan = topSQLService.executionPlan(id, sqlId);
59- if (res == null) {58+ Map<String, Object> parsedResult = new HashMap<>();
60- return AjaxResult.error("602", "execution plan pre check fail");59+ parsedResult.put("data", List.of(plan));
61- }60+ parsedResult.put("total",
62- return AjaxResult.success(res);61+ Map.of("totalPlanRows", plan.totalPlanRows(), "totalPlanWidth", plan.totalPlanWidth()));
62+ return AjaxResult.success(parsedResult);
63 }63 }
64 64 
65- @GetMapping(value = "/partition")65+ @GetMapping("/sysResource")
66- public AjaxResult partition(TopSQLInfoReq topSQLPartitionReq) {66+ public AjaxResult sysResource(String id, Long start, Long end, Integer step) {
67- return AjaxResult.success(topSQLService.getPartitionList(topSQLPartitionReq));67+ MetricsLine[] metricKey = {
68+ MetricsLine.CPU_TOTAL,
69+ MetricsLine.CPU_USER,
70+ MetricsLine.CPU_SYSTEM,
71+ MetricsLine.CPU_IOWAIT,
72+ MetricsLine.CPU_IRQ,
73+ MetricsLine.CPU_SOFTIRQ,
74+ MetricsLine.CPU_NICE,
75+ MetricsLine.CPU_STEAL,
76+ MetricsLine.CPU_IDLE,
77+ MetricsLine.CPU_DB,
78+ MetricsLine.MEMORY_USED,
79+ MetricsLine.MEMORY_DB_USED,
80+ MetricsLine.IO_UTIL,
81+ MetricsLine.NETWORK_IN_TOTAL,
82+ MetricsLine.NETWORK_OUT_TOTAL
83+ };
84+ return AjaxResult.success(metricsService.listBatch(metricKey, id, start, end, step));
68 }85 }
69 86 
70 @GetMapping(value = "/index")87 @GetMapping(value = "/index")
71- public AjaxResult index(TopSQLInfoReq topSQLIndexReq) {88+ public AjaxResult index(String id, String sqlId) {
72- return AjaxResult.success(topSQLService.getIndexAdvice(topSQLIndexReq));89+ return AjaxResult.success(topSQLService.indexAdvice(id, sqlId));
73 }90 }
74 91 
75 @GetMapping(value = "/object")92 @GetMapping(value = "/object")
76- public AjaxResult object(TopSQLInfoReq topSQLObjectReq) {93+ public AjaxResult object(String id, String sqlId) {
77- return AjaxResult.success(topSQLService.getObjectInfo(topSQLObjectReq));94+ return AjaxResult.success(topSQLService.objectInfo(id, sqlId));
78 }95 }
79 96 
80 @GetMapping(value = "/cluster")97 @GetMapping(value = "/cluster")
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/WdrController.java+59-57
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.controller;5package com.nctigba.observability.instance.controller;
5 6 
6import java.beans.PropertyEditorSupport;7import java.beans.PropertyEditorSupport;
@@ -40,69 +41,70 @@ import com.nctigba.observability.instance.service.OpsWdrService;
40@RestController41@RestController
41@RequestMapping("/wdr")42@RequestMapping("/wdr")
42public class WdrController {43public class WdrController {
43- @InitBinder44+ @InitBinder
44- public void initBinder(WebDataBinder binder) {45+ public void initBinder(WebDataBinder binder) {
45- // Date format46+ // Date format
46- binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {47+ binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
47- @Override48+ @Override
48- public void setAsText(String text) {49+ public void setAsText(String text) {
49- setValue(DateUtils.parseDate(text));50+ setValue(DateUtils.parseDate(text));
50- }51+ }
51- });52+ });
52- }53+ }
53 54 
54- @Autowired55+ @Autowired
55- private OpsWdrService wdrService;56+ private OpsWdrService wdrService;
56 57 
57- @GetMapping("/listSnapshot")58+ @GetMapping("/listSnapshot")
58- public Page<?> listSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {59+ public Page<?> listSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {
59- return wdrService.listSnapshot(startPage(), clusterId, hostId);60+ return wdrService.listSnapshot(startPage(), clusterId, hostId);
60- }61+ }
61 62 
62- @GetMapping("/createSnapshot")63+ @GetMapping("/createSnapshot")
63- public AjaxResult createSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {64+ public AjaxResult createSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {
64- wdrService.createSnapshot(clusterId, hostId);65+ wdrService.createSnapshot(clusterId, hostId);
65- return AjaxResult.success();66+ return AjaxResult.success();
66- }67+ }
67 68 
68- @GetMapping("/list")69+ @GetMapping("/list")
69- public Page<OpsWdrEntity> list(@RequestParam String clusterId,70+ public Page<OpsWdrEntity> list(@RequestParam String clusterId,
70- @RequestParam(required = false, value = "wdrScope") WdrScopeEnum wdrScope,71+ @RequestParam(required = false, value = "wdrScope") WdrScopeEnum wdrScope,
71- @RequestParam(required = false, value = "wdrType") WdrTypeEnum wdrType,72+ @RequestParam(required = false, value = "wdrType") WdrTypeEnum wdrType,
72- @RequestParam(required = false, value = "hostId") String hostId,73+ @RequestParam(required = false, value = "hostId") String hostId,
73- @RequestParam(required = false, value = "start") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date start,74+ @RequestParam(required = false, value = "start")
74- @RequestParam(required = false, value = "end") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date end) {75+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date start,
75- return wdrService.listWdr(startPage(), clusterId, wdrScope, wdrType, hostId, start, end);76+ @RequestParam(required = false, value = "end") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date end) {
76- }77+ return wdrService.listWdr(startPage(), clusterId, wdrScope, wdrType, hostId, start, end);
78+ }
77 79 
78- @DeleteMapping("/del/{id}")80+ @DeleteMapping("/del/{id}")
79- public AjaxResult del(@PathVariable("id") String id) {81+ public AjaxResult del(@PathVariable("id") String id) {
80- wdrService.del(id);82+ wdrService.del(id);
81- return AjaxResult.success();83+ return AjaxResult.success();
82- }84+ }
83 85 
84- @PostMapping("/generate")86+ @PostMapping("/generate")
85- public AjaxResult generate(@RequestBody @Validated WdrGeneratorBody wdrGeneratorBody) {87+ public AjaxResult generate(@RequestBody @Validated WdrGeneratorBody wdrGeneratorBody) {
86- wdrService.generate(wdrGeneratorBody);88+ wdrService.generate(wdrGeneratorBody);
87- return AjaxResult.success();89+ return AjaxResult.success();
88- }90+ }
89 91 
90- @GetMapping("/downloadWdr")92+ @GetMapping("/downloadWdr")
91- public void downloadWdr(@RequestParam String wdrId, HttpServletResponse response) {93+ public void downloadWdr(@RequestParam String wdrId, HttpServletResponse response) {
92- wdrService.downloadWdr(wdrId, response);94+ wdrService.downloadWdr(wdrId, response);
93- }95+ }
94 96 
95- @SuppressWarnings("rawtypes")97+ @SuppressWarnings("rawtypes")
96- protected Page startPage() {98+ protected Page startPage() {
97- Page page = new Page();99+ Page page = new Page();
98- Integer pageNum = ServletUtils.getParameterToInt("pageNum");100+ Integer pageNum = ServletUtils.getParameterToInt("pageNum");
99- Integer pageSize = ServletUtils.getParameterToInt("pageSize");101+ Integer pageSize = ServletUtils.getParameterToInt("pageSize");
100- if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) {102+ if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) {
101- page.setCurrent((long) pageNum);103+ page.setCurrent(pageNum);
102- page.setSize((long) pageSize);104+ page.setSize(pageSize);
103- page.setOptimizeCountSql(false);105+ page.setOptimizeCountSql(false);
104- page.setMaxLimit(500L);106+ page.setMaxLimit(500L);
105- }107+ }
106- return page;108+ return page;
107- }109+ }
108}110}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/WdrSnapController.java+40-0
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.controller;
6+ 
7+import java.util.Date;
8+ 
9+import org.opengauss.admin.common.core.domain.AjaxResult;
10+import org.springframework.beans.factory.annotation.Autowired;
11+import org.springframework.web.bind.annotation.GetMapping;
12+import org.springframework.web.bind.annotation.RequestMapping;
13+import org.springframework.web.bind.annotation.RestController;
14+ 
15+import com.nctigba.observability.instance.service.OpsWdrService;
16+ 
17+/**
18+ * WdrSnapController.java
19+ *
20+ * @since 2023-08-25
21+ */
22+@RestController
23+@RequestMapping("/wdr")
24+public class WdrSnapController extends ControllerConfig {
25+ @Autowired
26+ private OpsWdrService wdrService;
27+ 
28+ /**
29+ * findSnapshot
30+ *
31+ * @param id id
32+ * @param start start
33+ * @param end end
34+ * @return AjaxResult
35+ */
36+ @GetMapping("/findSnapshot")
37+ public AjaxResult findSnapshot(String id, Date start, Date end) {
38+ return AjaxResult.success(wdrService.findSnapshot(id, start, end));
39+ }
40+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/asp/AnalysisDto.java+40-0
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.asp;
6+ 
7+import lombok.AllArgsConstructor;
8+import lombok.Data;
9+import lombok.NoArgsConstructor;
10+ 
11+/**
12+ * AnalysisDto
13+ *
14+ * @author liupengfei
15+ * @since 2023/8/25
16+ */
17+@Data
18+@AllArgsConstructor
19+@NoArgsConstructor
20+public class AnalysisDto {
21+ private String sampleid;
22+ private String sampleTime;
23+ private String databaseid;
24+ private String threadId;
25+ private String sessionid;
26+ private String startTime;
27+ private String event;
28+ private String userid;
29+ private String applicationName;
30+ private String clientAddr;
31+ private String clientHostname;
32+ private String clientPort;
33+ private String queryId;
34+ private String uniqueQueryId;
35+ private String userId;
36+ private String cnId;
37+ private String uniqueQuery;
38+ private String lockmode;
39+ private String waitStatus;
40+}
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/ResInfo.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/asp/AspCountReq.java+13-9
@@ -2,19 +2,23 @@
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4 4 
5-package com.nctigba.observability.instance.model;5+package com.nctigba.observability.instance.dto.asp;
6 6 
7import lombok.AllArgsConstructor;7import lombok.AllArgsConstructor;
8import lombok.Data;8import lombok.Data;
9+import lombok.NoArgsConstructor;
9 10 
11+/**
12+ * AspCountReq
13+ *
14+ * @author liupengfei
15+ * @since 2023/8/25
16+ */
10@Data17@Data
18+@NoArgsConstructor
11@AllArgsConstructor19@AllArgsConstructor
12-public class ResInfo {20+public class AspCountReq {
13- private int exitStatus;21+ private String id;
14- private String outRes;22+ private String startTime;
15- private String errRes;23+ private String finishTime;
16- 
17- public ResInfo() {
18- this.exitStatus = -2147483648;
19- }
20}24}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/ClusterHealthState.java+24-0
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import lombok.Data;
8+ 
9+import java.util.Map;
10+ 
11+/**
12+ * ClusterHealthState
13+ *
14+ * @author liupengfei
15+ * @since 2023/8/25
16+ */
17+@Data
18+public class ClusterHealthState {
19+ private String clusterState;
20+ private Map<String, String> nodeState;
21+ private Map<String, String> nodeRole;
22+ private Map<String, String> nodeName;
23+ private Map<String, String> cmState;
24+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/ClusterStateDto.java+64-0
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import com.nctigba.observability.instance.util.MessageSourceUtil;
8+import lombok.AllArgsConstructor;
9+import lombok.Data;
10+import lombok.NoArgsConstructor;
11+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
12+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
13+ 
14+import java.util.Map;
15+import java.util.Optional;
16+ 
17+/**
18+ * ClusterStateDto
19+ *
20+ * @author liupengfei
21+ * @since 2023/8/25
22+ */
23+@Data
24+@AllArgsConstructor
25+@NoArgsConstructor
26+public class ClusterStateDto {
27+ private String clusterId;
28+ private State clusterState;
29+ private String desc;
30+ private String primaryNodeId;
31+ 
32+ /**
33+ * get ClusterStateDto
34+ *
35+ * @param clusterVO OpsClusterVO
36+ * @param stateCache ClusterHealthState
37+ * @return ClusterStateDto
38+ */
39+ public static ClusterStateDto of(OpsClusterVO clusterVO, ClusterHealthState stateCache) {
40+ ClusterStateDto dto = new ClusterStateDto();
41+ dto.setClusterId(clusterVO.getClusterId());
42+ String state = Optional.ofNullable(stateCache.getClusterState()).orElse(
43+ "Unknown");
44+ dto.setClusterState(state);
45+ dto.setDesc(state);
46+ Optional<Map.Entry<String, String>> primary = stateCache.getNodeRole().entrySet().stream().filter(
47+ en -> en.getValue().equals("Primary")).findFirst();
48+ if (primary.isPresent()) {
49+ String primaryIp = primary.get().getKey();
50+ OpsClusterNodeVO nodeVO = clusterVO.getClusterNodes().stream().filter(
51+ node -> node.getPublicIp().equals(primaryIp)).findFirst().get();
52+ dto.setPrimaryNodeId(nodeVO.getNodeId());
53+ }
54+ return dto;
55+ }
56+ 
57+ public void setClusterState(String clusterState) {
58+ this.clusterState = new State("cluster.state.value." + clusterState);
59+ }
60+ 
61+ public void setDesc(String state) {
62+ this.desc = MessageSourceUtil.getMsg("cluster.state.desc." + state);
63+ }
64+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/ClustersDto.java+52-0
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import java.util.List;
8+ 
9+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
10+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
11+ 
12+import com.nctigba.observability.instance.util.MessageSourceUtil;
13+ 
14+import lombok.Data;
15+import lombok.NoArgsConstructor;
16+ 
17+/**
18+ * ClustersDto
19+ *
20+ * @author liupengfei
21+ * @since 2023/8/25
22+ */
23+@Data
24+@NoArgsConstructor
25+public class ClustersDto {
26+ private String clusterId;
27+ private String versionNum;
28+ private String version;
29+ private String envPath;
30+ private Integer nodeCount;
31+ private String arch;
32+ 
33+ /**
34+ * get ClustersDto
35+ *
36+ * @param opsClusterVO OpsClusterVO
37+ * @return ClustersDto
38+ */
39+ public static ClustersDto of(OpsClusterVO opsClusterVO) {
40+ ClustersDto clustersDto = new ClustersDto();
41+ clustersDto.setClusterId(opsClusterVO.getClusterId());
42+ clustersDto.setVersionNum(opsClusterVO.getVersionNum());
43+ clustersDto.setEnvPath(opsClusterVO.getEnvPath());
44+ clustersDto.setVersion(opsClusterVO.getVersion());
45+ clustersDto.setNodeCount(opsClusterVO.getClusterNodes().size());
46+ List<OpsClusterNodeVO> nodes = opsClusterVO.getClusterNodes();
47+ Long primaryNum = nodes.stream().filter(node -> "MASTER".equalsIgnoreCase(node.getClusterRole())).count();
48+ Long standbyNum = nodes.stream().filter(node -> !"MASTER".equalsIgnoreCase(node.getClusterRole())).count();
49+ clustersDto.setArch(MessageSourceUtil.getMsg("cluster.arch", primaryNum.toString(), standbyNum.toString()));
50+ return clustersDto;
51+ }
52+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/NodeAndCompDto.java+82-0
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
8+ 
9+import lombok.AllArgsConstructor;
10+import lombok.Data;
11+import lombok.Getter;
12+import lombok.NoArgsConstructor;
13+ 
14+/**
15+ * SyncSituation
16+ *
17+ * @author liupengfei
18+ * @since 2023/8/25
19+ */
20+@Data
21+@NoArgsConstructor
22+public class NodeAndCompDto {
23+ private State cmServerState;
24+ private State omMonitorState;
25+ private State cmAgentState;
26+ 
27+ /**
28+ * get NodeAndCompDto
29+ *
30+ * @param vo OpsClusterNodeVO
31+ * @return NodeAndCompDto
32+ */
33+ public static NodeAndCompDto of(OpsClusterNodeVO vo) {
34+ return new NodeAndCompDto();
35+ }
36+ 
37+ public void setCmServerState(String cmServerState) {
38+ this.cmServerState = new State("OS.bin.state." + BinState.getState(cmServerState));
39+ }
40+ 
41+ public void setOmMonitorState(String omMonitorState) {
42+ this.omMonitorState = new State("OS.bin.state." + BinState.getState(omMonitorState));
43+ }
44+ 
45+ public void setCmAgentState(String cmAgentState) {
46+ this.cmAgentState = new State("OS.bin.state." + BinState.getState(cmAgentState));
47+ }
48+ 
49+ /**
50+ * BinState
51+ */
52+ @Getter
53+ @AllArgsConstructor
54+ public enum BinState {
55+ TASK_UNINTERRUPTIBLE("D"),
56+ TASK_RUNNING("R"),
57+ TASK_INTERRUPTIBLE("S"),
58+ TASK_STOPPED("T"),
59+ TASK_TRACED("t"),
60+ EXIT_ZOMBIE("Z"),
61+ EXIT_DEAD("X"),
62+ STOP("stop"),
63+ UNKNOWN("Unknown");
64+ 
65+ private final String code;
66+ 
67+ /**
68+ * getState
69+ *
70+ * @param s String
71+ * @return BinState
72+ */
73+ public static BinState getState(String s) {
74+ for (BinState state : BinState.values()) {
75+ if (s.contains(state.code)) {
76+ return state;
77+ }
78+ }
79+ return UNKNOWN;
80+ }
81+ }
82+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/NodeRelationDto.java+56-0
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import lombok.Data;
8+import lombok.NoArgsConstructor;
9+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * NodeRelationDto
15+ *
16+ * @author liupengfei
17+ * @since 2023/8/25
18+ */
19+@Data
20+@NoArgsConstructor
21+public class NodeRelationDto {
22+ private String hostIp;
23+ private String role;
24+ private String state;
25+ private String replayDelay;
26+ private String syncState;
27+ private List<NodeRelationDto> children;
28+ 
29+ /**
30+ * getDefaultRelationDto
31+ *
32+ * @param stateCache ClusterHealthState
33+ * @param node OpsClusterNodeVO
34+ * @param standbyList List<NodeRelationDto>
35+ * @return NodeRelationDto
36+ */
37+ public static NodeRelationDto getDefaultRelationDto(ClusterHealthState stateCache, OpsClusterNodeVO node,
38+ List<NodeRelationDto> standbyList) {
39+ NodeRelationDto primary = new NodeRelationDto();
40+ primary.setHostIp(node.getPublicIp());
41+ setNodeState(primary, stateCache);
42+ primary.setChildren(standbyList);
43+ return primary;
44+ }
45+ 
46+ /**
47+ * setNodeState
48+ *
49+ * @param node NodeRelationDto
50+ * @param stateCache ClusterHealthState
51+ */
52+ public static void setNodeState(NodeRelationDto node, ClusterHealthState stateCache) {
53+ node.setRole(stateCache.getNodeRole().getOrDefault(node.getHostIp(), "Unknown"));
54+ node.setState(stateCache.getNodeState().getOrDefault(node.getHostIp(), "Unknown"));
55+ }
56+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/State.java+38-0
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import com.nctigba.observability.instance.constants.StateColor;
8+import com.nctigba.observability.instance.util.MessageSourceUtil;
9+import lombok.AllArgsConstructor;
10+import lombok.Data;
11+import lombok.NoArgsConstructor;
12+ 
13+/**
14+ * State
15+ *
16+ * @author liupengfei
17+ * @since 2023/8/25
18+ */
19+@Data
20+@AllArgsConstructor
21+@NoArgsConstructor
22+public class State {
23+ private String value;
24+ private StateColor color;
25+ 
26+ State(String value) {
27+ this.value = value;
28+ setColor();
29+ }
30+ 
31+ public void setColor() {
32+ this.color = StateColor.getColor(value);
33+ }
34+ 
35+ public String getValue() {
36+ return MessageSourceUtil.getMsg(value);
37+ }
38+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/SyncSituation.java+28-0
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import lombok.AllArgsConstructor;
8+import lombok.Data;
9+import lombok.NoArgsConstructor;
10+ 
11+/**
12+ * SyncSituation
13+ *
14+ * @author liupengfei
15+ * @since 2023/8/25
16+ */
17+@Data
18+@AllArgsConstructor
19+@NoArgsConstructor
20+public class SyncSituation {
21+ private String hostIp;
22+ private String sync;
23+ private String walSyncState;
24+ private String syncPriority;
25+ private String receivedDelay;
26+ private String writeDelay;
27+ private String replayDelay;
28+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/cluster/SyncSituationDto.java+82-0
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.dto.cluster;
6+ 
7+import com.fasterxml.jackson.annotation.JsonIgnore;
8+import com.nctigba.observability.instance.util.MessageSourceUtil;
9+import lombok.Data;
10+import lombok.NoArgsConstructor;
11+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
12+ 
13+import java.util.List;
14+import java.util.Optional;
15+ 
16+/**
17+ * SyncSituationDto
18+ *
19+ * @author liupengfei
20+ * @since 2023/8/25
21+ */
22+@Data
23+@NoArgsConstructor
24+public class SyncSituationDto {
25+ private String clusterId;
26+ private String nodeId;
27+ private String hostIp;
28+ private String nodeName;
29+ private String primaryAddr;
30+ private String localAddr;
31+ private String role;
32+ private State nodeState;
33+ private String sync;
34+ private State syncState;
35+ private String syncPriority;
36+ private String receivedDelay;
37+ private String writeDelay;
38+ private String replayDelay;
39+ @JsonIgnore
40+ private String walSyncState;
41+ 
42+ /**
43+ * getDefaultSituationDto
44+ *
45+ * @param syncSituation SyncSituation
46+ * @param clusterNodes List<OpsClusterNodeVO>
47+ * @param clusterId String
48+ * @return SyncSituationDto
49+ */
50+ public static SyncSituationDto getDefaultSituationDto(SyncSituation syncSituation,
51+ List<OpsClusterNodeVO> clusterNodes, String clusterId) {
52+ SyncSituationDto dto = new SyncSituationDto();
53+ dto.setClusterId(clusterId);
54+ Optional<OpsClusterNodeVO> nodeVO = clusterNodes.stream().filter(
55+ n -> n.getPublicIp().equals(syncSituation.getHostIp())).findFirst();
56+ nodeVO.ifPresent(node -> dto.setNodeId(node.getNodeId()));
57+ dto.setHostIp(syncSituation.getHostIp());
58+ dto.setSync(syncSituation.getSync());
59+ dto.setSyncState(syncSituation.getWalSyncState());
60+ dto.setSyncPriority(syncSituation.getSyncPriority());
61+ dto.setReceivedDelay(syncSituation.getReceivedDelay());
62+ dto.setWriteDelay(syncSituation.getWriteDelay());
63+ dto.setReplayDelay(syncSituation.getReplayDelay());
64+ return dto;
65+ }
66+ 
67+ public void setNodeState(String state) {
68+ this.nodeState = new State("cluster.node.state." + state);
69+ }
70+ 
71+ public String getRole() {
72+ return MessageSourceUtil.getMsg("cluster.node.role." + role);
73+ }
74+ 
75+ public String getSync() {
76+ return MessageSourceUtil.getMsg("cluster.node.sync." + sync);
77+ }
78+ 
79+ public void setSyncState(String state) {
80+ this.syncState = new State("cluster.node.syncState." + state);
81+ }
82+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/topsql/TopSQLListReq.java+31-1
@@ -4,6 +4,9 @@
4 4 
5package com.nctigba.observability.instance.dto.topsql;5package com.nctigba.observability.instance.dto.topsql;
6 6 
7+import java.sql.Timestamp;
8+ 
9+import cn.hutool.core.util.StrUtil;
7import lombok.Data;10import lombok.Data;
8 11 
9/**12/**
@@ -20,4 +23,31 @@ public class TopSQLListReq {
20 private String startTime;23 private String startTime;
21 private String finishTime;24 private String finishTime;
22 private String orderField;25 private String orderField;
23-}26+ 
27+ /**
28+ * getStartTimeTime
29+ *
30+ * @return Timestamp
31+ */
32+ public Timestamp getStartTimeTime() {
33+ return Timestamp.valueOf(getStartTime());
34+ }
35+ 
36+ /**
37+ * getFinishTimeTime
38+ *
39+ * @return Timestamp
40+ */
41+ public Timestamp getFinishTimeTime() {
42+ return Timestamp.valueOf(getFinishTime());
43+ }
44+ 
45+ /**
46+ * getOrderField
47+ *
48+ * @return String
49+ */
50+ public String getOrderField() {
51+ return StrUtil.isBlank(orderField) ? "execution_time" : orderField;
52+ }
53+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/ParamInfo.java+3-3
@@ -26,7 +26,7 @@ public class ParamInfo {
26 @TableId(type = IdType.AUTO)26 @TableId(type = IdType.AUTO)
27 Integer id;27 Integer id;
28 @TableField("paramType")28 @TableField("paramType")
29- type paramType;29+ ParamType paramType;
30 @TableField("paramName")30 @TableField("paramName")
31 String paramName;31 String paramName;
32 String paramDetail;32 String paramDetail;
@@ -39,7 +39,7 @@ public class ParamInfo {
39 @TableField("diagnosisRule")39 @TableField("diagnosisRule")
40 String diagnosisRule;40 String diagnosisRule;
41 41 
42- public enum type {42+ public enum ParamType {
43 OS,43 OS,
44 DB44 DB
45 }45 }
@@ -50,7 +50,7 @@ public class ParamInfo {
50 var info = new ParamInfo();50 var info = new ParamInfo();
51 info.setId(rs.getInt("id"));51 info.setId(rs.getInt("id"));
52 info.setParamName(rs.getString("paramName"));52 info.setParamName(rs.getString("paramName"));
53- info.setParamType(type.valueOf(rs.getString("paramType")));53+ info.setParamType(ParamType.valueOf(rs.getString("paramType")));
54 info.setSuggestValue(rs.getString("suggestValue"));54 info.setSuggestValue(rs.getString("suggestValue"));
55 info.setDefaultValue(rs.getString("defaultValue"));55 info.setDefaultValue(rs.getString("defaultValue"));
56 list.add(info);56 list.add(info);
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/PgSettings.java+35-0
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableName;
8+ 
9+import lombok.Data;
10+ 
11+/**
12+ * PgSettings.java
13+ *
14+ * @since 2023-08-25
15+ */
16+@Data
17+@TableName("pg_settings")
18+public class PgSettings {
19+ String name;
20+ String setting;
21+ String unit;
22+ String category;
23+ String shortDesc;
24+ String extraDesc;
25+ String context;
26+ String vartype;
27+ String source;
28+ String minVal;
29+ String maxVal;
30+ String enumvals;
31+ String bootVal;
32+ String resetVal;
33+ String sourcefile;
34+ String sourceline;
35+}
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/WdrSnapshotVO.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/Snapshot.java+23-23
@@ -1,24 +1,24 @@
1-/*1+/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */3+ */
4- 4+ 
5-package com.nctigba.observability.instance.model;5+package com.nctigba.observability.instance.entity;
6- 6+ 
7-import java.util.Date;7+import java.util.Date;
8- 8+ 
9-import com.baomidou.mybatisplus.annotation.TableField;9+import com.baomidou.mybatisplus.annotation.TableName;
10-import com.baomidou.mybatisplus.annotation.TableId;10+ 
11-import com.baomidou.mybatisplus.annotation.TableName;11+import lombok.Data;
12- 12+ 
13-import lombok.Data;13+/**
14- 14+ * Snapshot.java
15-@Data15+ *
16-@TableName(value = "snapshot.snapshot", autoResultMap = true)16+ * @since 2023-08-25
17-public class WdrSnapshotVO {17+ */
18- @TableId("snapshot_id")18+@Data
19- private Integer snapshotId;19+@TableName("snapshot.snapshot")
20- @TableField("start_ts")20+public class Snapshot {
21- private Date startTs;21+ private Long snapshotId;
22- @TableField("end_ts")22+ private Date startTs;
23- private Date endTs;23+ private Date endTs;
24}24}
Rplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/BaseI18nException.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/exception/BaseI18nException.java+1-1
@@ -2,7 +2,7 @@
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4 4 
5-package com.nctigba.common.web.exception;5+package com.nctigba.observability.instance.exception;
6 6 
7import com.nctigba.observability.instance.util.MessageSourceUtil;7import com.nctigba.observability.instance.util.MessageSourceUtil;
8 8 
Rplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/InstanceException.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/exception/InstanceException.java+1-1
@@ -2,7 +2,7 @@
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4 4 
5-package com.nctigba.common.web.exception;5+package com.nctigba.observability.instance.exception;
6 6 
7import lombok.Data;7import lombok.Data;
8import lombok.EqualsAndHashCode;8import lombok.EqualsAndHashCode;
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/MonitoringHandlerFactory.java+0-57
@@ -1,57 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.factory;
5- 
6-import javax.annotation.PostConstruct;
7-import javax.annotation.Resource;
8-import java.util.List;
9-import java.util.Map;
10-import java.util.function.Function;
11-import java.util.stream.Collectors;
12- 
13-import com.alibaba.fastjson.JSON;
14-import com.nctigba.observability.instance.constants.MonitoringType;
15-import com.nctigba.observability.instance.handler.monitoring.MonitoringHandler;
16- 
17-import lombok.extern.slf4j.Slf4j;
18-import org.springframework.aop.support.AopUtils;
19-import org.springframework.stereotype.Component;
20- 
21-/**
22- * The monitoring implementation class of MonitoringHandler uses the self
23- * implemented bean name of monitoringType, such as prometheus<br>
24- *
25- * @author yangjie
26- */
27- 
28-@Slf4j
29-@Component
30-public class MonitoringHandlerFactory {
31- 
32- private Map<String, MonitoringHandler> handlerMap;
33- 
34- @Resource
35- private List<MonitoringHandler> handlerList;
36- 
37- @PostConstruct
38- public void init() {
39- handlerMap = handlerList.stream()
40- .collect(Collectors.toMap(MonitoringHandler::getMonitorType, Function.identity()));
41- log.info("load MonitoringHandler complete. entity:{}", JSON.toJSONString(handlerMap.entrySet().stream()
42- .collect(Collectors.toMap(Map.Entry::getKey, entity -> AopUtils.getTargetClass(entity.getValue())))));
43- 
44- }
45- 
46- /**
47- * Return the processing class of the monitoring according to the monitoring
48- * type. If it is not found, return the default processing class
49- *
50- * @param monitoringType Monitoring type
51- * @return MonitoringHandler Monitoring and processing class
52- */
53- public MonitoringHandler getInstance(String monitoringType) {
54- return handlerMap.containsKey(monitoringType) ? handlerMap.get(monitoringType)
55- : handlerMap.get(MonitoringType.DEFAULT.getMonitoringType());
56- }
57-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/SessionHandlerFactory.java+0-47
@@ -1,47 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.factory;
5- 
6-import com.alibaba.fastjson.JSON;
7-import com.nctigba.observability.instance.constants.DatabaseType;
8-import com.nctigba.observability.instance.handler.session.SessionHandler;
9-import lombok.extern.slf4j.Slf4j;
10-import org.springframework.aop.support.AopUtils;
11-import org.springframework.stereotype.Component;
12- 
13-import javax.annotation.PostConstruct;
14-import javax.annotation.Resource;
15-import java.util.List;
16-import java.util.Map;
17-import java.util.function.Function;
18-import java.util.stream.Collectors;
19- 
20-@Slf4j
21-@Component
22-public class SessionHandlerFactory {
23- private Map<String, SessionHandler> handlerMap;
24- 
25- @Resource
26- private List<SessionHandler> handlerList;
27- 
28- @PostConstruct
29- public void init() {
30- handlerMap = handlerList.stream()
31- .collect(Collectors.toMap(SessionHandler::getDatabaseType, Function.identity()));
32- log.info("load TopSQLHandler complete. entity:{}", JSON.toJSONString(handlerMap.entrySet().stream()
33- .collect(Collectors.toMap(Map.Entry::getKey, entity -> AopUtils.getTargetClass(entity.getValue())))));
34- 
35- }
36- 
37- /**
38- * get TopSQL handler for different types of databases
39- *
40- * @param databaseType database type
41- * @return TopSQL handler
42- */
43- public SessionHandler getInstance(String databaseType) {
44- return handlerMap.containsKey(databaseType) ? handlerMap.get(databaseType)
45- : handlerMap.get(DatabaseType.DEFAULT.getDbType());
46- }
47-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/TopSQLHandlerFactory.java+0-56
@@ -1,56 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.factory;
5- 
6-import com.alibaba.fastjson.JSON;
7-import com.nctigba.observability.instance.constants.DatabaseType;
8-import com.nctigba.observability.instance.handler.topsql.TopSQLHandler;
9- 
10-import lombok.extern.slf4j.Slf4j;
11-import org.springframework.aop.support.AopUtils;
12-import org.springframework.stereotype.Component;
13- 
14-import javax.annotation.PostConstruct;
15-import javax.annotation.Resource;
16-import java.util.List;
17-import java.util.Map;
18-import java.util.function.Function;
19-import java.util.stream.Collectors;
20- 
21-/**
22- * <p>
23- * store TopSQL Handlers
24- * </p>
25- *
26- * @author zhanggr@vastdata.com.cn
27- * @since 2022/9/15 15:58
28- */
29-@Slf4j
30-@Component
31-public class TopSQLHandlerFactory {
32- private Map<String, TopSQLHandler> handlerMap;
33- 
34- @Resource
35- private List<TopSQLHandler> handlerList;
36- 
37- @PostConstruct
38- public void init() {
39- handlerMap = handlerList.stream()
40- .collect(Collectors.toMap(TopSQLHandler::getDatabaseType, Function.identity()));
41- log.info("load TopSQLHandler complete. entity:{}", JSON.toJSONString(handlerMap.entrySet().stream()
42- .collect(Collectors.toMap(Map.Entry::getKey, entity -> AopUtils.getTargetClass(entity.getValue())))));
43- 
44- }
45- 
46- /**
47- * get TopSQL handler for different types of databases
48- *
49- * @param databaseType database type
50- * @return TopSQL handler
51- */
52- public TopSQLHandler getInstance(String databaseType) {
53- return handlerMap.containsKey(databaseType) ? handlerMap.get(databaseType)
54- : handlerMap.get(DatabaseType.DEFAULT.getDbType());
55- }
56-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/monitoring/MonitoringHandler.java+0-66
@@ -1,66 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.handler.monitoring;
5- 
6-import java.util.List;
7-import java.util.Map;
8- 
9-import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;
10-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
11- 
12-public interface MonitoringHandler {
13- /**
14- * Monitoring system type
15- *
16- * @return
17- */
18- String getMonitorType();
19- 
20- /**
21- * Get time period monitoring data
22- *
23- * @param query
24- * @param start prot
25- * @param end Connect Users
26- * @param step Connection password
27- * @return Indicator set
28- */
29- List<MonitoringMetric> rangeQuery(String query, String start, String end, String step);
30- 
31- /**
32- * Get the monitoring data of the specified time node
33- *
34- * @param query ip
35- * @param time Specify time
36- * @return Indicator set
37- */
38- List<MonitoringMetric> pointQuery(String query, String time);
39- 
40- /**
41- * Convert indicator data format to table
42- *
43- * @param rangeMetricList Range Indicator List
44- * @param param Indicator Query Parameters
45- * @return Indicator set in tabular format
46- */
47- List<Object> metricToTable(List<MonitoringMetric> rangeMetricList, MonitoringParam param);
48- 
49- /**
50- * Convert indicator data format to line chart
51- *
52- * @param rangeMetricList Range Indicator List
53- * @param param query parameters
54- * @return Indicator set in polyline format
55- */
56- List<Object> metricToLine(List<MonitoringMetric> rangeMetricList, MonitoringParam param);
57- 
58- /**
59- * Sort the monitoring data
60- *
61- * @param tableList Table Data
62- * @param param Monitoring query parameters
63- * @return Sorted indicator set
64- */
65- List<Map<String, String>> sortList(List<Object> tableList, MonitoringParam param);
66-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/monitoring/NormalMonitoringHandler.java+0-386
@@ -1,386 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.handler.monitoring;
5- 
6-import java.math.BigDecimal;
7-import java.text.SimpleDateFormat;
8-import java.util.ArrayList;
9-import java.util.Collections;
10-import java.util.Date;
11-import java.util.HashMap;
12-import java.util.HashSet;
13-import java.util.LinkedHashMap;
14-import java.util.List;
15-import java.util.Map;
16-import java.util.Set;
17-import java.util.stream.Collectors;
18- 
19-import org.apache.commons.lang3.ObjectUtils;
20-import org.apache.commons.lang3.StringUtils;
21-import org.opengauss.admin.common.exception.CustomException;
22-import org.opengauss.admin.system.plugin.facade.HostFacade;
23-import org.springframework.beans.factory.annotation.Autowired;
24-import org.springframework.stereotype.Component;
25-import org.springframework.util.CollectionUtils;
26-import org.springframework.web.util.UriComponentsBuilder;
27- 
28-import com.alibaba.fastjson.JSON;
29-import com.alibaba.fastjson.JSONArray;
30-import com.alibaba.fastjson.JSONObject;
31-import com.alibaba.fastjson.TypeReference;
32-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
33-import com.gitee.starblues.bootstrap.annotation.AutowiredType;
34-import com.nctigba.observability.instance.constants.CommonConstants;
35-import com.nctigba.observability.instance.constants.MonitoringConstants;
36-import com.nctigba.observability.instance.constants.MonitoringType;
37-import com.nctigba.observability.instance.entity.NctigbaEnv;
38-import com.nctigba.observability.instance.entity.NctigbaEnv.envType;
39-import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
40-import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;
41-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
42-import com.nctigba.observability.instance.util.HttpUtils;
43- 
44-import cn.hutool.core.map.MapUtil;
45-import lombok.extern.slf4j.Slf4j;
46- 
47-@Slf4j
48-@Component
49-public class NormalMonitoringHandler implements MonitoringHandler {
50- @Autowired
51- protected NctigbaEnvMapper envMapper;
52- @Autowired
53- @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
54- protected HostFacade hostFacade;
55- 
56- private String getPrometheusUrl() {
57- var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, envType.PROMETHEUS));
58- if (env == null)
59- throw new RuntimeException("Prometheus not found");
60- var host = hostFacade.getById(env.getHostid());
61- return "http://" + host.getPublicIp() + ":" + env.getPort();
62- }
63- 
64- @Override
65- public String getMonitorType() {
66- return MonitoringType.DEFAULT.getMonitoringType();
67- }
68- 
69- /**
70- * Querying prometheus data for a period of time
71- *
72- * @param query Indicator parameters
73- * @param start start time
74- * @param end End time
75- * @param step step
76- * @return List<MonitoringMetric>
77- */
78- @Override
79- public List<MonitoringMetric> rangeQuery(String query, String start, String end, String step) {
80- List<MonitoringMetric> monitoringMetricList = null;
81- UriComponentsBuilder builder = UriComponentsBuilder
82- .fromHttpUrl(getPrometheusUrl() + MonitoringConstants.PROMETHEUS_QUERY_RANGE);
83- builder.queryParam("query", query);
84- builder.queryParam("start", start);
85- builder.queryParam("end", end);
86- builder.queryParam("step", step);
87- String url = builder.build().encode().toUriString();
88- log.info("request url:[{}]", url);
89- try {
90- String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
91- JSONObject responseJson = JSONObject.parseObject(response);
92- if ("success".equals(responseJson.get("status"))) {
93- JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
94- monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
95- } else {
96- log.info("query prometheus range data failed ! please check the log, the error message is:{}",
97- response);
98- throw new CustomException("");
99- }
100- } catch (CustomException e) {
101- log.error(e.getMessage());
102- throw new CustomException("create URI failed");
103- }
104- return monitoringMetricList;
105- }
106- 
107- /**
108- * Query prometheus data at a specified time
109- *
110- * @param query Indicator parameters
111- * @param time Specify the timestamp. The default is the current system time of
112- * prometheus
113- * @return List<MonitoringMetric>
114- */
115- @Override
116- public List<MonitoringMetric> pointQuery(String query, String time) {
117- List<MonitoringMetric> monitoringMetricList;
118- String baseUrl = getPrometheusUrl() + MonitoringConstants.PROMETHEUS_QUERY_POINT;
119- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
120- builder.queryParam("query", query);
121- if (StringUtils.isNotEmpty(time)) {
122- builder.queryParam("time", time);
123- }
124- String url = builder.build().encode().toUriString();
125- log.info("request url:[{}]", url);
126- try {
127- String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
128- JSONObject responseJson = JSONObject.parseObject(response);
129- if ("success".equals(responseJson.get("status"))) {
130- JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
131- monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
132- } else {
133- log.info("query prometheus range data failed ! please check the log, the error message is:{}",
134- response);
135- throw new CustomException("");
136- }
137- } catch (CustomException e) {
138- log.error(e.getMessage());
139- throw new CustomException("create URI failed");
140- }
141- return monitoringMetricList;
142- }
143- 
144- @Override
145- public List<Object> metricToTable(List<MonitoringMetric> metricList, MonitoringParam param) {
146- log.info("Monitoring data starts to be converted into tabular data");
147- List<Object> tableList = null;
148- // Get timestamp data in descending order
149- Map<String, List<Object>> timeMetricMap = transToTimeList(metricList);
150- if (MapUtil.isEmpty(timeMetricMap)) {
151- log.error("prometheus to table: data is empty!");
152- throw new CustomException("601", 601);
153- }
154- List<Object> data = new ArrayList<>();
155- List<String> columnNames = new ArrayList<>();
156- for (List<Object> timeMetricList : timeMetricMap.values()) {
157- List<Map<String, Object>> timeMertic = JSON.parseObject(JSON.toJSONString(timeMetricList),
158- new TypeReference<List<Map<String, Object>>>() {
159- });
160- ArrayList<ArrayList<String>> result = new ArrayList<>();
161- // Record the metricData length
162- int max = 0;
163- for (Map<String, Object> timeMetricObj : timeMertic) {
164- ArrayList<String> metricData = JSON.parseObject(timeMetricObj.get("metricData").toString(),
165- new TypeReference<ArrayList<String>>() {
166- });
167- if (ObjectUtils.isNotEmpty(metricData) && metricData.size() > 0) {
168- max = Math.max(max, metricData.size());
169- String title = timeMetricObj.get("metricName").toString();
170- if (!columnNames.contains(title)) {
171- columnNames.add(title);
172- }
173- result.add(metricData);
174- }
175- }
176- // Store the converted structure into data in turn
177- for (int i = 0; i < max; i++) {
178- Map<String, Object> map = new HashMap<>();
179- for (int j = 0; j < columnNames.size(); j++) {
180- String title = columnNames.get(j);
181- map.put(title, result.get(j).get(i));
182- }
183- data.add(map);
184- }
185- }
186- tableList = data;
187- // Determine whether to sort
188- if (StringUtils.isNotEmpty(param.getField())) {
189- log.info("Start of monitoring data sorting");
190- List<Map<String, String>> tableSortList = this.sortList(tableList, param);
191- JSONArray jsonArray = new JSONArray();
192- jsonArray.addAll(tableSortList);
193- log.info("Monitoring data sorting completed");
194- return jsonArray.toJavaList(Object.class);
195- }
196- log.info("Monitoring data completion converted to tabular data");
197- return tableList;
198- }
199- 
200- private Map<String, List<Object>> transToTimeList(List<MonitoringMetric> metricList) {
201- if (CollectionUtils.isEmpty(metricList)) {
202- log.error("transToTimeList: prometheus data is empty!");
203- throw new CustomException("601", 601);
204- }
205- // 1. Processing of original data and extracting duplicates__ name__ Value Data
206- Map<String, List<MonitoringMetric>> metricMap = new HashMap<>();
207- for (MonitoringMetric metric : metricList) {
208- String metricName = metric.getMetric().getString(CommonConstants.NAME);
209- List<MonitoringMetric> list;
210- if (metricMap.containsKey(metricName)) {
211- list = metricMap.get(metricName);
212- } else {
213- list = new ArrayList<>();
214- }
215- list.add(metric);
216- metricMap.put(metricName, list);
217- }
218- // 2. Process the data and return it in the form of a map. The key is a
219- // timestamp, the value is a metric list, and the timeMetricMap is used to store
220- // the processed data
221- if (metricMap.isEmpty()) {
222- log.error("The first processing of the raw data results in a null result!");
223- throw new CustomException("601", 601);
224- }
225- Map<String, List<Object>> timeMetricMap = new HashMap<>();
226- for (List<MonitoringMetric> metrics : metricMap.values()) {
227- for (MonitoringMetric metric : metrics) {
228- // Get the corresponding field
229- String metricName = metric.getMetric().getString(CommonConstants.NAME);
230- String warningMsg = metric.getMetric().getString("warning_msg");
231- Object metricData = JSON.parse(metric.getMetric().getString("table"));
232- JSONArray values = metric.getValues();
233- for (Object value : values) {
234- JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());
235- List<Object> list;
236- String curTimeStamp = valueArray.get(0).toString();
237- if (timeMetricMap.containsKey(curTimeStamp)) {
238- list = timeMetricMap.get(curTimeStamp);
239- } else {
240- list = new ArrayList<>();
241- }
242- Map<String, Object> map = new HashMap<>();
243- map.put("metricData", metricData);
244- map.put("metricName", metricName);
245- map.put("warning_msg", warningMsg);
246- list.add(map);
247- timeMetricMap.put(curTimeStamp, list);
248- }
249- }
250- }
251- // Sort in descending order
252- LinkedHashMap<String, List<Object>> result = new LinkedHashMap<>();
253- timeMetricMap.entrySet().stream().sorted((c1, c2) -> c2.getKey().compareTo(c1.getKey()))
254- .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
255- return result;
256- }
257- 
258- @Override
259- public List<Object> metricToLine(List<MonitoringMetric> metricList, MonitoringParam param) {
260- log.info("Monitoring data starts to be converted into line data");
261- if (CollectionUtils.isEmpty(metricList)) {
262- return Collections.singletonList(metricList);
263- }
264- List<Map<String, Object>> result = new ArrayList<>();
265- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
266- for (MonitoringMetric metric : metricList) {
267- // Index name
268- String metricName = metric.getMetric().getString(CommonConstants.NAME);
269- if (StringUtils.isNotBlank(param.getLegendName())) {
270- metricName = metric.getMetric().getString(param.getLegendName());
271- }
272- JSONArray lineValues = metric.getValues();
273- Map<String, Object> item = new HashMap<>();
274- // Get data of time and value
275- List<String> timeList = new ArrayList<>();
276- List<String> dataList = new ArrayList<>();
277- for (Object value : lineValues) {
278- JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());
279- if (valueArray.size() > 1) {
280- // Convert timestamp to date format
281- String time = simpleDateFormat
282- .format(new Date(Long.parseLong(valueArray.get(0).toString()) * 1000));
283- timeList.add(time);
284- dataList.add(valueArray.get(1).toString());
285- }
286- }
287- 
288- // Encapsulated into the echarts data
289- item.put("name", metricName);
290- item.put("data", dataList);
291- item.put("time", timeList);
292- item.put("type", "line");
293- 
294- result.add(item);
295- }
296- log.info("Monitoring data completed convert to line data");
297- return Collections.singletonList(result);
298- }
299- 
300- @Override
301- public List<Map<String, String>> sortList(List<Object> tableList, MonitoringParam param) {
302- String field = param.getField();
303- if (StringUtils.isEmpty(field)) {
304- log.error("field cannot be null!");
305- throw new CustomException("field cannot be null!", 400);
306- }
307- if (StringUtils.isEmpty(param.getOrder())) {
308- log.error("order cannot be null!");
309- throw new CustomException("order cannot be null!", 400);
310- }
311- if (ObjectUtils.isEmpty(tableList)) {
312- log.error("result is null!");
313- throw new CustomException("result cannot be null!", 400);
314- }
315- // Sort by field
316- List<Map<String, String>> listMapSort = JSON.parseObject(JSON.toJSONString(tableList),
317- new TypeReference<List<Map<String, String>>>() {
318- });
319- // Sorting is divided into numerical type and time interval type
320- String firstValue = listMapSort.get(0).get(field);
321- if (firstValue.contains(":")) {
322- // Interval type
323- listMapSort = listMapSort.stream()
324- .sorted((x, y) -> (int) (resolutionInterval(y.get(field)) - resolutionInterval(x.get(field))))
325- .collect(Collectors.toList());
326- 
327- } else {
328- // Digital
329- listMapSort = listMapSort.stream()
330- .sorted((x, y) -> BigDecimal.valueOf(Double.parseDouble(y.get(field)))
331- .compareTo(BigDecimal.valueOf(Double.parseDouble(x.get(field)))))
332- .collect(Collectors.toList());
333- }
334- // De duplication according to filter
335- if (StringUtils.isNotEmpty(param.getFilter())) {
336- listMapSort = distinctByKey(listMapSort, param.getFilter());
337- }
338- // Intercept the first 10 lines of listMapSort
339- int listMapSortLength = listMapSort.size();
340- if (listMapSortLength <= 10) {
341- return listMapSort;
342- }
343- // Desc in reverse order, the default is positive order
344- if (!"desc".equalsIgnoreCase(param.getOrder())) {
345- Collections.reverse(listMapSort);
346- }
347- log.info("monitoring data sort finish! sortType:{}", param.getOrder());
348- return listMapSort.subList(0, 10);
349- }
350- 
351- private List<Map<String, String>> distinctByKey(List<Map<String, String>> listMapSort, String filter) {
352- Set<String> set = new HashSet<>();
353- List<Map<String, String>> newListMapSort = new ArrayList<>();
354- for (Map<String, String> stringStringMap : listMapSort) {
355- String text = stringStringMap.get(filter);
356- if (!set.contains(text)) {
357- set.add(text);
358- newListMapSort.add(stringStringMap);
359- }
360- }
361- log.info("monitoring data distinct finish! distinct filed:{}", filter);
362- return newListMapSort;
363- }
364- 
365- private long resolutionInterval(String interval) {
366- if (StringUtils.isEmpty(interval)) {
367- return 0;
368- }
369- long stamp = 0;
370- String time = interval;
371- if (interval.contains(",")) {
372- String[] intervalArr = interval.split(",");
373- String timeDay = intervalArr[0].trim();
374- time = intervalArr[1].trim();
375- long timeDayNumber = Long.parseLong(timeDay.substring(0, timeDay.indexOf("day")).trim());
376- stamp = stamp + timeDayNumber * 86400;
377- }
378- if (interval.contains(".")) {
379- String[] timeArr = time.split(".")[0].split(":");
380- int timeCount = Integer.parseInt(timeArr[0]) * 3600 + Integer.parseInt(timeArr[1]) * 60
381- + Integer.parseInt(timeArr[2]);
382- stamp = stamp + timeCount;
383- }
384- return stamp;
385- }
386-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/session/SessionHandler.java+0-36
@@ -1,36 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.handler.session;
5- 
6-import com.alibaba.fastjson.JSONObject;
7-import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
8-import com.nctigba.observability.instance.model.InstanceNodeInfo;
9- 
10-import java.io.Closeable;
11-import java.sql.Connection;
12-import java.util.List;
13- 
14-public interface SessionHandler {
15- /**
16- * target database type
17- * @return database type
18- */
19- String getDatabaseType();
20- 
21- Connection getConnection(InstanceNodeInfo nodeInfo);
22- 
23- void close(Connection connection);
24- 
25- JSONObject detailGeneral(Connection conn, String sessionid);
26- 
27- List<DetailStatisticDto> detailStatistic(Connection conn, String sessionid);
28- 
29- List<JSONObject> detailWaiting(Connection conn, String sessionid);
30- 
31- List<JSONObject> detailBlockTree(Connection conn, String sessionid);
32- 
33- JSONObject simpleStatistic(Connection conn);
34- 
35- List<JSONObject> longTxc(Connection conn);
36-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/topsql/OpenGaussTopSQLHandler.java+0-719
@@ -1,719 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.handler.topsql;
6- 
7-import com.alibaba.fastjson.JSON;
8-import com.alibaba.fastjson.JSONArray;
9-import com.alibaba.fastjson.JSONObject;
10-import com.nctigba.observability.instance.constants.CommonConstants;
11-import com.nctigba.observability.instance.constants.DatabaseType;
12-import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;
13-import com.nctigba.observability.instance.dto.topsql.TopSQLNowReq;
14-import com.nctigba.observability.instance.model.ExecutionPlan;
15-import com.nctigba.observability.instance.model.IndexAdvice;
16-import com.nctigba.observability.instance.model.InstanceNodeInfo;
17-import com.nctigba.observability.instance.service.ClusterManager;
18-import lombok.RequiredArgsConstructor;
19-import lombok.extern.slf4j.Slf4j;
20-import org.apache.commons.lang3.ObjectUtils;
21-import org.apache.commons.lang3.StringUtils;
22-import org.opengauss.admin.common.exception.CustomException;
23-import org.springframework.stereotype.Component;
24- 
25-import java.sql.Connection;
26-import java.sql.PreparedStatement;
27-import java.sql.ResultSet;
28-import java.sql.SQLException;
29-import java.sql.Statement;
30-import java.sql.Timestamp;
31-import java.util.ArrayList;
32-import java.util.Arrays;
33-import java.util.LinkedList;
34-import java.util.List;
35- 
36-/**
37- * <p>
38- * TopSQL business of openGauss
39- * </p>
40- *
41- * @author zhanggr@vastdata.com.cn
42- * @since 2022/9/15 14:39
43- */
44-@Component
45-@RequiredArgsConstructor
46-@Slf4j
47-public class OpenGaussTopSQLHandler implements TopSQLHandler {
48- private static final String TEST_SQL = "select 1";
49- private static final String TOP_SQL_LIST_SQL = "select unique_query_id,debug_query_id,db_name,schema_name,user_name,application_name,start_time,finish_time,db_time,cpu_time,execution_time from dbe_perf.statement_history where debug_query_id != 0 and finish_time >= ? and finish_time <= ? order by %s desc,execution_time desc,cpu_time desc,db_time desc limit 10";
50- private static final String TOP_SQL_Now_SQL = "select round(extract(epoch FROM (now() - query_start)),2) as duration,query_start,unique_sql_id,datname ,usename,application_name ,datid,pid,sessionid,usesysid,usename,client_addr ,client_hostname,client_port,backend_start ,xact_start ,state_change ,waiting,enqueue,state ,resource_pool,query_id ,query ,connection_info,trace_id\n"
51- + "from pg_stat_activity where query_start is not null and unique_sql_id != 0 and duration != 0 order by (now() - query_start) desc limit 10";
52- // private static final String TOP_SQL_LIST_SQL = "select
53- // unique_query_id,debug_query_id,db_name,schema_name,user_name,application_name,start_time,finish_time,db_time,cpu_time,execution_time
54- // from dbe_perf.statement_history where debug_query_id != 0 and finish_time >=
55- // ? and finish_time <= ? order by %s desc,execution_time desc,cpu_time
56- // desc,db_time desc";
57- private static final String PRE_CHECK_SET_SQL = "select name,setting from pg_settings where name in('enable_stmt_track','enable_resource_track','track_stmt_stat_level')";
58- private static final String STATISTICAL_INFO_SQL = "select query,debug_query_id,unique_query_id,db_name,schema_name,\n"
59- + "substring(start_time,0,20) start_time,substring(finish_time,0,20) finish_time,\n"
60- + "user_name,application_name,client_addr||':'||client_port socket,\n"
61- + "n_returned_rows,n_tuples_fetched,n_tuples_returned,n_tuples_inserted,n_tuples_updated,n_tuples_deleted,lock_count,lock_wait_count,lock_max_count,\n"
62- + "(case when n_blocks_fetched=0 then '-' else substring((n_blocks_hit/n_blocks_fetched)*100,0,6)||'%' end) as blocks_hit_rate,\n"
63- + "net_send_info::json->'size' net_send_info_size,net_recv_info::json->'size' net_recv_info_size,\n"
64- + "net_stream_send_info::json->'size' net_stream_send_info_size,net_stream_recv_info::json->'size' net_stream_recv_info_size,\n"
65- + "net_send_info::json->'n_calls' net_send_info_calls,net_recv_info::json->'n_calls' net_recv_info_calls,\n"
66- + "net_stream_send_info::json->'n_calls' net_stream_send_info_calls,net_stream_recv_info::json->'n_calls' net_stream_recv_info_calls,\n"
67- + "net_send_info::json->'time' net_send_info_time,net_recv_info::json->'time' net_recv_info_time,\n"
68- + "net_stream_send_info::json->'time' net_stream_send_info_time,net_stream_recv_info::json->'time' net_stream_recv_info_time,\n"
69- + "n_soft_parse,n_hard_parse,db_time,cpu_time,(db_time-cpu_time) wait_time,lock_time,lock_wait_time,\n"
70- + "execution_time,parse_time,plan_time,rewrite_time,pl_execution_time,pl_compilation_time,data_io_time\n"
71- + "from dbe_perf.statement_history\n" + "where debug_query_id=?";
72- private static final String EXECUTION_PLAN_SQL = "select query_plan,query from dbe_perf.statement_history where debug_query_id=?;";
73- private static final String SELECT_QUERY_SQL = "select query from dbe_perf.statement_history where debug_query_id=?";
74- private static final String TABLE_METADATA_SQL = "select row_to_json(t) from (select schemaname,t1.relname,pg_relation_size(relid) object_size, relkind object_type,n_live_tup,n_dead_tup,\n"
75- + "case when n_live_tup+n_dead_tup=0 then '0.00%' else round(n_dead_tup*100/(n_dead_tup+n_live_tup),2)||'%' end dead_tup_ratio,\n"
76- + "last_vacuum,last_autovacuum,last_analyze,last_autoanalyze\n" + "from pg_catalog.pg_stat_all_tables t1\n"
77- + "left join pg_catalog.pg_class t2 on t1.relid = t2.oid\n" + "where t1.relname=?)t";
78- private static final String INDEX_SQL = "select row_to_json(t) from (select c2.relname,i.indisprimary,i.indisunique,i.indisclustered,i.indisvalid,\n"
79- + "i.indisreplident,pg_catalog.pg_get_indexdef(i.indexrelid,0,true) as def\n"
80- + "from pg_catalog.pg_class c, pg_catalog.pg_class c2,pg_catalog.pg_index i\n"
81- + "where c.relname=? and c.oid=i.indrelid and c2.oid=i.indexrelid) t";
82- private static final String TABLE_STRUCTURE_SQL = "select row_to_json(t) from (select a.attnum,a.attname,t.typname,a.attlen,a.attnotnull,b.description\n"
83- + "from pg_catalog.pg_class c,pg_catalog.pg_attribute a\n"
84- + "left outer join pg_catalog.pg_description b on a.attrelid=b.objoid and a.attnum=b.objsubid,pg_catalog.pg_type t\n"
85- + "where c.relname=? and a.attnum>0 and a.attrelid=c.oid and a.atttypid=t.oid\n" + "order by a.attnum) t";
86- private static final String WORK_MEM_SQL = "select setting from pg_settings WHERE name = 'work_mem'";
87- private static final String PARTITION_LIST_SQL = "select partstrategy, partkey, relpages, reltuples, relallvisible, interval from pg_partition WHERE parttype = 'r'";
88- private int totalPlanRows = 0;
89- private int totalPlanWidth = 0;
90- private List<String> objectNameList = new ArrayList<>();
91- private final ClusterManager clusterManager;
92- 
93- private boolean testConnection(Connection conn) {
94- if (ObjectUtils.isNotEmpty(conn)) {
95- try (PreparedStatement preparedStatement = conn.prepareStatement(TEST_SQL)) {
96- try (ResultSet rs = preparedStatement.executeQuery()) {
97- return true;
98- }
99- } catch (Exception e) {
100- log.error("test connection fail:{}", e.getMessage());
101- throw new CustomException(e.getMessage());
102- }
103- }
104- return false;
105- }
106- 
107- @Override
108- public Connection getConnection(InstanceNodeInfo nodeInfo) {
109- Connection connection = clusterManager.getConnectionByNodeInfo(nodeInfo);
110- if (!testConnection(connection)) {
111- return null;
112- }
113- return connection;
114- }
115- 
116- @Override
117- public String getDatabaseType() {
118- return DatabaseType.DEFAULT.getDbType();
119- }
120- 
121- @Override
122- public List<JSONObject> getTopSQLList(InstanceNodeInfo nodeInfo, TopSQLListReq topSQLListReq) {
123- // nodeInfo.setDbUserPassword(RSAUtil.decrypt(nodeInfo.getDbUserPassword()));
124- List<JSONObject> list = new ArrayList<>();
125- try (Connection conn = getConnection(nodeInfo)) {
126- if (conn == null) {
127- throw new CustomException(CommonConstants.CONNECTION_FAIL);
128- }
129- // param pre check
130- if (topSqlListPreCheck(conn)) {
131- return null;
132- }
133- try (PreparedStatement preparedStatement = conn.prepareStatement(String.format(TOP_SQL_LIST_SQL,
134- StringUtils.isBlank(topSQLListReq.getOrderField()) ? "execution_time"
135- : topSQLListReq.getOrderField()))) {
136- preparedStatement.setTimestamp(1, Timestamp.valueOf(topSQLListReq.getStartTime()));
137- preparedStatement.setTimestamp(2, Timestamp.valueOf(topSQLListReq.getFinishTime()));
138- try (ResultSet rs = preparedStatement.executeQuery()) {
139- while (rs.next()) {
140- JSONObject object = new JSONObject();
141- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
142- object.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
143- }
144- list.add(object);
145- }
146- }
147- }
148- } catch (SQLException e) {
149- log.error("get TopSQL list fail:{}", e.getMessage());
150- throw new CustomException(e.getMessage());
151- }
152- return list;
153- }
154- 
155- @Override
156- public List<JSONObject> getTopSQLNow(InstanceNodeInfo nodeInfo, TopSQLNowReq topSQLNowReq) {
157- List<JSONObject> list = new ArrayList<>();
158- try (Connection conn = getConnection(nodeInfo)) {
159- if (conn == null) {
160- throw new CustomException(CommonConstants.CONNECTION_FAIL);
161- }
162- // param pre check
163- if (topSqlListPreCheck(conn)) {
164- return null;
165- }
166- try (PreparedStatement preparedStatement = conn.prepareStatement(TOP_SQL_Now_SQL)) {
167- try (ResultSet rs = preparedStatement.executeQuery()) {
168- while (rs.next()) {
169- JSONObject object = new JSONObject();
170- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
171- object.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
172- }
173- list.add(object);
174- }
175- }
176- }
177- } catch (SQLException e) {
178- log.error("get TopSQL list fail:{}", e.getMessage());
179- throw new CustomException(e.getMessage());
180- }
181- return list;
182- }
183- 
184- /**
185- * pre-check top sql list job
186- *
187- * @param connection connection info
188- */
189- private boolean topSqlListPreCheck(Connection connection) {
190- try (Statement statement = connection.createStatement()) {
191- try (ResultSet rs = statement.executeQuery(PRE_CHECK_SET_SQL)) {
192- while (rs.next()) {
193- String name = rs.getString(1);
194- String setting = rs.getString(2);
195- // "track_stmt_stat_level".equalsIgnoreCase(name) &&
196- // StringUtils.startsWith(setting, "OFF") || "off".equalsIgnoreCase(setting)
197- if (("enable_resource_track".equalsIgnoreCase(name) && "off".equalsIgnoreCase(setting))
198- || ("enable_stmt_track".equalsIgnoreCase(name) && "off".equalsIgnoreCase(setting))
199- || ("track_stmt_stat_level".equalsIgnoreCase(name) && dealTrackStmtStatLevel(setting))) {
200- return true;
201- }
202- }
203- }
204- } catch (SQLException e) {
205- log.error("pre check fail:{}", e.getMessage());
206- throw new CustomException(e.getMessage());
207- }
208- return false;
209- }
210- 
211- private boolean dealTrackStmtStatLevel(String setting) {
212- if (StringUtils.isEmpty(setting) || !setting.contains(",")) {
213- return true;
214- }
215- String[] settingArr = setting.split(",");
216- if ("off".equalsIgnoreCase(settingArr[0])) {
217- return true;
218- }
219- return false;
220- }
221- 
222- @Override
223- public JSONObject getStatisticalInfo(InstanceNodeInfo nodeInfo, String sqlId) {
224- // nodeInfo.setDbUserPassword(RSAUtil.decrypt(nodeInfo.getDbUserPassword()));
225- JSONObject result = new JSONObject();
226- // get connection then execute query
227- try (Connection connection = getConnection(nodeInfo)) {
228- if (connection == null) {
229- throw new CustomException(CommonConstants.CONNECTION_FAIL);
230- }
231- // get prepared statement then put parameters in
232- try (PreparedStatement preparedStatement = connection.prepareStatement(STATISTICAL_INFO_SQL)) {
233- preparedStatement.setString(1, sqlId);
234- // get result from result set
235- try (ResultSet rs = preparedStatement.executeQuery()) {
236- while (rs.next()) {
237- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
238- result.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
239- }
240- }
241- }
242- }
243- } catch (SQLException e) {
244- log.error("get TopSQL statistical info fail:{}", e.getMessage());
245- throw new CustomException(e.getMessage());
246- }
247- return result;
248- }
249- 
250- public String getPeakMemory(String planStr) {
251- if (StringUtils.isEmpty(planStr)) {
252- return "";
253- }
254- if (planStr.contains("Peak Memory")) {
255- String[] planArr = planStr.split("Peak Memory:");
256- if (planArr.length > 1) {
257- return planArr[1].substring(0, planArr[1].indexOf("(KB)"));
258- }
259- }
260- return "";
261- }
262- 
263- public String getWorkMem(Connection connection) throws SQLException {
264- try (PreparedStatement preparedStatement = connection.prepareStatement(WORK_MEM_SQL)) {
265- try (ResultSet rs = preparedStatement.executeQuery()) {
266- while (rs.next()) {
267- return rs.getString(1);
268- }
269- }
270- }
271- return "";
272- }
273- 
274- public List<JSONObject> getRowsDiff(String planStr) {
275- List<JSONObject> result = new ArrayList<>();
276- List<String> planStrList = Arrays.asList(planStr.split("\n"));
277- LinkedList<String> modifyLines = new LinkedList<>(planStrList);
278- try {
279- for (String item : planStrList) {
280- if ((item.contains("cost=") && !item.contains("Result")) || item.contains(CommonConstants.HASH_COND)
281- || item.contains("->")) {
282- continue;
283- }
284- modifyLines.remove(item);
285- }
286- for (String mItem : modifyLines) {
287- String[] mItemArr = mItem.split("rows=");
288- if (mItem.length() <= 2) {
289- continue;
290- }
291- // there are two rows
292- JSONObject jsonObject = new JSONObject();
293- String firstStr = mItemArr[0];
294- if (!firstStr.contains(CommonConstants.COST)) {
295- continue;
296- }
297- if (firstStr.contains("->")) {
298- jsonObject.put("stepName", firstStr
299- .substring(firstStr.indexOf("->") + 2, firstStr.indexOf(CommonConstants.COST)).trim());
300- } else {
301- jsonObject.put("stepName", firstStr.substring(0, firstStr.indexOf(CommonConstants.COST)).trim());
302- }
303- jsonObject.put("estimateRows", mItemArr[1].substring(0, mItemArr[1].indexOf(CommonConstants.BLANK)));
304- jsonObject.put("actualRows", mItemArr[2].substring(0, mItemArr[2].indexOf(CommonConstants.BLANK)));
305- result.add(jsonObject);
306- }
307- return result;
308- } catch (Exception e) {
309- log.error("get Rows diff fail:{}", e.getMessage());
310- return result;
311- }
312- }
313- 
314- @Override
315- public JSONObject getExecutionPlan(InstanceNodeInfo nodeInfo, String sqlId, String type) {
316- // nodeInfo.setDbUserPassword(RSAUtil.decrypt(nodeInfo.getDbUserPassword()));
317- // store return result
318- JSONObject parsedResult = new JSONObject();
319- // get connection then execute query
320- try (Connection connection = getConnection(nodeInfo)) {
321- if (connection == null) {
322- throw new CustomException(CommonConstants.CONNECTION_FAIL);
323- }
324- // pre-check track_stmt_stat_leve full sql level at least L1
325- if (executionPlanPreCheck(connection)) {
326- return null;
327- }
328- // get prepared statement
329- try (PreparedStatement preparedStatement = connection.prepareStatement(EXECUTION_PLAN_SQL)) {
330- preparedStatement.setString(1, sqlId);
331- // native plan
332- if ("native".equals(type)) {
333- JSONArray nativePlanArray = new JSONArray();
334- try (ResultSet rs = preparedStatement.executeQuery()) {
335- while (rs.next()) {
336- JSONObject jsonObject = new JSONObject();
337- String planStr = rs.getString(1);
338- jsonObject.put("rowsDiff", getRowsDiff(planStr));
339- jsonObject.put("queryPlan", planStr);
340- jsonObject.put("peakMem", getPeakMemory(planStr));
341- jsonObject.put("workMem", getWorkMem(connection));
342- nativePlanArray.add(jsonObject);
343- }
344- }
345- parsedResult.put("plan", nativePlanArray);
346- return parsedResult;
347- }
348- // json plan
349- // get result from result set
350- try (ResultSet rs = preparedStatement.executeQuery()) {
351- while (rs.next()) {
352- List<String> splitLines = new ArrayList<>();
353- String executionPlan = rs.getString(1);
354- if (StringUtils.isBlank(executionPlan)) {
355- throw new CustomException("failGetExecutionPlan");
356- } else {
357- splitLines = Arrays.asList(executionPlan.split("\n"));
358- }
359- LinkedList<String> modifySplitLines = new LinkedList<>(splitLines);
360- // remove non-operation or non-condition lines
361- for (String line : splitLines) {
362- if ((line.contains("cost=") && !line.contains("Result"))
363- || line.contains(CommonConstants.HASH_COND)) {
364- continue;
365- }
366- modifySplitLines.remove(line);
367- }
368- if (modifySplitLines.size() == 0) {
369- throw new CustomException("failResolveExecutionPlan");
370- }
371- // get base execution plan object
372- totalPlanWidth = 0;
373- totalPlanRows = 0;
374- ExecutionPlan plan = processExecutionPlanString(modifySplitLines.get(0));
375- // get tree shape execution plan object
376- assert plan != null;
377- processExecutionPlan(modifySplitLines.subList(1, modifySplitLines.size()), 0, plan,
378- plan.getChildren());
379- // put plan node
380- JSONObject result = JSONObject.parseObject(JSON.toJSONString(plan));
381- JSONArray data = new JSONArray();
382- data.add(result);
383- parsedResult.put("data", data);
384- // put total rows and width
385- JSONObject total = new JSONObject();
386- total.put("totalPlanRows", totalPlanRows);
387- total.put("totalPlanWidth", totalPlanWidth);
388- parsedResult.put("total", total);
389- }
390- }
391- }
392- } catch (SQLException e) {
393- log.error("get TopSQL execute plan fail:{}", e.getMessage());
394- throw new CustomException(e.getMessage());
395- }
396- return parsedResult;
397- }
398- 
399- /**
400- * pre-check execution plan job
401- *
402- * @param connection connection info
403- */
404- private boolean executionPlanPreCheck(Connection connection) {
405- try (Statement statement = connection.createStatement()) {
406- try (ResultSet rs = statement.executeQuery(PRE_CHECK_SET_SQL)) {
407- while (rs.next()) {
408- String name = rs.getString(1);
409- String setting = rs.getString(2);
410- if ("track_stmt_stat_level".equalsIgnoreCase(name)) {
411- if (StringUtils.startsWith(setting, "OFF") || StringUtils.startsWith(setting, "L0")) {
412- return true;
413- }
414- }
415- }
416- }
417- } catch (SQLException e) {
418- log.error("pre check fail:{}", e.getMessage());
419- throw new CustomException(e.getMessage());
420- }
421- return false;
422- }
423- 
424- /**
425- * generate execution plan tree shape json object
426- *
427- * @param lines lines of execution plan and condition
428- * @param previousIndent last level indent length
429- * @param plan last level plan
430- * @param children last level plan children
431- */
432- public void processExecutionPlan(List<String> lines, int previousIndent, ExecutionPlan plan,
433- List<ExecutionPlan> children) {
434- for (int i = 0; i < lines.size(); i++) {
435- String line = lines.get(i);
436- // process condition
437- if (line.contains(CommonConstants.HASH_COND)) {
438- String[] split = line.split(": ");
439- plan.setJoinType(split[1]);
440- continue;
441- }
442- // skip processed lines
443- if (line.isEmpty()) {
444- continue;
445- }
446- String[] indentPlanSplit = line.split("->");
447- int currentIndent = indentPlanSplit[0].length();
448- // when current indent greater than previous indent, add new children node and
449- // go into next tree level
450- if (currentIndent > previousIndent) {
451- lines.set(i, "");
452- ExecutionPlan subPlan = processExecutionPlanString(line);
453- children.add(subPlan);
454- assert subPlan != null;
455- processExecutionPlan(lines.subList(i + 1, lines.size()), currentIndent, subPlan, subPlan.getChildren());
456- }
457- // when current indent less than or equals previous indent, return to last level
458- if (currentIndent <= previousIndent) {
459- return;
460- }
461- }
462- }
463- 
464- /**
465- * parse execution plan line string into object
466- *
467- * @param line execution plan line string
468- * @return execution plan line object
469- */
470- public ExecutionPlan processExecutionPlanString(String line) {
471- if (line.contains(CommonConstants.HASH_COND)) {
472- return null;
473- }
474- ExecutionPlan plan = new ExecutionPlan();
475- // check if first line
476- String planString;
477- if (line.contains("->")) {
478- String[] indentPlanSplit = line.split("->");
479- planString = indentPlanSplit[1];
480- } else {
481- planString = line;
482- }
483- String[] operationParameterSplit = planString.split("\\(");
484- // set operation name and alias name
485- String operation = operationParameterSplit[0];
486- if (operation.contains(" on ")) {
487- String[] split = operation.split(" on ");
488- plan.setNodeType(split[0].trim());
489- // only show object name, remove alias name
490- String objectName = split[1];
491- String alias;
492- if (objectName.contains(CommonConstants.BLANK)) {
493- String[] aliasSplit = objectName.split(CommonConstants.BLANK);
494- alias = aliasSplit[0];
495- } else {
496- alias = objectName;
497- }
498- plan.setAlias(alias);
499- if (!objectNameList.contains(alias)) {
500- objectNameList.add(alias);
501- }
502- } else {
503- plan.setNodeType(operation.trim());
504- }
505- // set parameters
506- String parameters = operationParameterSplit[1].replace(")", "");
507- String[] parametersSplit = parameters.split(CommonConstants.BLANK);
508- for (String parameterSplit : parametersSplit) {
509- if (StringUtils.startsWith(parameterSplit, "cost")) {
510- // set start cost and total cost
511- String[] costSplit = parameterSplit.split("=");
512- String[] startCostTotalCostSplit = costSplit[1].split("\\.\\.");
513- plan.setStartupCost(Double.parseDouble(startCostTotalCostSplit[0]));
514- plan.setTotalCost(Double.parseDouble(startCostTotalCostSplit[1]));
515- } else if (StringUtils.startsWith(parameterSplit, "rows=")) {
516- // set plan rows
517- String[] rowSplit = parameterSplit.split("=");
518- int rowsNum = Integer.parseInt(rowSplit[1]);
519- plan.setPlanRows(rowsNum);
520- totalPlanRows += rowsNum;
521- } else if (StringUtils.startsWith(parameterSplit, "width=")) {
522- // set plan width
523- String[] widthSplit = parameterSplit.split("=");
524- int widthNum = Integer.parseInt(widthSplit[1]);
525- plan.setPlanWidth(widthNum);
526- totalPlanWidth += widthNum;
527- }
528- }
529- return plan;
530- }
531- 
532- @Override
533- public List<JSONObject> getPartitionList(InstanceNodeInfo nodeInfo, String sqlId) {
534- // nodeInfo.setDbUserPassword(RSAUtil.decrypt(nodeInfo.getDbUserPassword()));
535- List<JSONObject> results = new ArrayList<>();
536- try (Connection connection = getConnection(nodeInfo)) {
537- if (connection == null) {
538- throw new CustomException(CommonConstants.CONNECTION_FAIL);
539- }
540- // get prepared statement then put parameters in
541- try (PreparedStatement preparedStatement = connection.prepareStatement(PARTITION_LIST_SQL)) {
542- try (ResultSet rs = preparedStatement.executeQuery()) {
543- while (rs.next()) {
544- JSONObject jsonObject = new JSONObject();
545- jsonObject.put("partstrategy", rs.getString(1));
546- jsonObject.put("partkey", rs.getString(2));
547- jsonObject.put("relpages", rs.getString(3));
548- jsonObject.put("reltuples", rs.getString(4));
549- jsonObject.put("relallvisible", rs.getString(5));
550- jsonObject.put("interval", rs.getString(6));
551- results.add(jsonObject);
552- }
553- }
554- }
555- } catch (SQLException e) {
556- log.error("get TopSQL Partition List fail:{}", e.getMessage());
557- return results;
558- }
559- return results;
560- }
561- 
562- @Override
563- public List<String> getIndexAdvice(InstanceNodeInfo nodeInfo, String sqlId) {
564- // nodeInfo.setDbUserPassword(RSAUtil.decrypt(nodeInfo.getDbUserPassword()));
565- List<String> results = new ArrayList<>();
566- List<IndexAdvice> advices = new ArrayList<>();
567- String queryText = "";
568- // get connection then execute query
569- try (Connection connection = getConnection(nodeInfo)) {
570- if (connection == null) {
571- throw new CustomException(CommonConstants.CONNECTION_FAIL);
572- }
573- // get prepared statement then put parameters in
574- try (PreparedStatement preparedStatement = connection.prepareStatement(SELECT_QUERY_SQL)) {
575- preparedStatement.setString(1, sqlId);
576- // get query text from result set
577- try (ResultSet rs = preparedStatement.executeQuery()) {
578- while (rs.next()) {
579- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
580- queryText = rs.getString(i);
581- }
582- }
583- }
584- }
585- // check if queryText is empty
586- if (queryText.isEmpty()) {
587- throw new CustomException("get query text fail");
588- }
589- String completeQueryText = "select * from gs_index_advise('"
590- + queryText.replace("\n", CommonConstants.BLANK).replace("'", "''") + "')";
591- log.info("get complete query text: {}", completeQueryText);
592- // get prepared statement
593- try (PreparedStatement preparedStatement = connection.prepareStatement(completeQueryText)) {
594- // get index advice from result set
595- try (ResultSet rs = preparedStatement.executeQuery()) {
596- while (rs.next()) {
597- IndexAdvice advice = new IndexAdvice();
598- advice.setSchema(rs.getString(1));
599- advice.setTable(rs.getString(2));
600- advice.setColumn(rs.getString(3));
601- advice.setIndexType(rs.getString(4));
602- advices.add(advice);
603- }
604- }
605- }
606- } catch (SQLException e) {
607- log.error("get TopSQL index advice fail:{}", e.getMessage());
608- results.add("No index suggestions");
609- return results;
610- }
611- processAdvice(results, advices);
612- return results;
613- }
614- 
615- /**
616- * generate readable index advice
617- *
618- * @param results return result
619- * @param advices list of index advice
620- */
621- private void processAdvice(List<String> results, List<IndexAdvice> advices) {
622- String indexTemplate = "建议为%s模式下的%t表的%c列创建索引";
623- String multiColumnIndexTemplate = "建议为%s模式下的%t表的%c创建复合索引";
624- // return specific message when get no index advice
625- if (advices.isEmpty()) {
626- results.add("No index suggestions");
627- return;
628- }
629- // process index advice for every returned line
630- for (IndexAdvice advice : advices) {
631- String column = advice.getColumn();
632- if (StringUtils.isNotEmpty(column)) {
633- String result = column.contains(",") ? multiColumnIndexTemplate : indexTemplate;
634- result = result.replace("%s", advice.getSchema());
635- result = result.replace("%t", advice.getTable());
636- result = result.replace("%c", advice.getColumn());
637- results.add(result);
638- }
639- }
640- }
641- 
642- @Override
643- public JSONObject getObjectInfo(InstanceNodeInfo nodeInfo, String sqlId) {
644- // init objectNameList via get execution plan
645- this.objectNameList = new ArrayList<>();
646- if (null == this.getExecutionPlan(nodeInfo, sqlId, "")) {
647- throw new CustomException("failGetExecutionPlan");
648- }
649- List<String> curObjectNameList = this.objectNameList;
650- LinkedList<String> modifyObjectNameList = new LinkedList<>(curObjectNameList);
651- // get connection then execute query
652- JSONObject results = new JSONObject();
653- JSONObject tableMetadata = new JSONObject();
654- JSONObject tableStructure = new JSONObject();
655- JSONObject tableIndex = new JSONObject();
656- try (Connection connection = getConnection(nodeInfo)) {
657- if (connection == null) {
658- throw new CustomException(CommonConstants.CONNECTION_FAIL);
659- }
660- // query table metadata
661- try (PreparedStatement preparedStatement = connection.prepareStatement(TABLE_METADATA_SQL)) {
662- for (String name : curObjectNameList) {
663- preparedStatement.setString(1, name);
664- // get result from result set
665- try (ResultSet rs = preparedStatement.executeQuery()) {
666- while (rs.next()) {
667- JSONObject result = JSONObject.parseObject(rs.getString(1));
668- tableMetadata.put(name, result);
669- }
670- } catch (SQLException e) {
671- modifyObjectNameList.remove(name);
672- }
673- }
674- }
675- // query table structure
676- try (PreparedStatement preparedStatement = connection.prepareStatement(TABLE_STRUCTURE_SQL)) {
677- for (String name : curObjectNameList) {
678- preparedStatement.setString(1, name);
679- // get result from result set
680- try (ResultSet rs = preparedStatement.executeQuery()) {
681- JSONArray resultArray = new JSONArray();
682- while (rs.next()) {
683- resultArray.add(JSONObject.parseObject(rs.getString(1)));
684- }
685- tableStructure.put(name, resultArray);
686- } catch (SQLException e) {
687- modifyObjectNameList.remove(name);
688- }
689- }
690- }
691- // query index info
692- try (PreparedStatement preparedStatement = connection.prepareStatement(INDEX_SQL)) {
693- for (String name : curObjectNameList) {
694- preparedStatement.setString(1, name);
695- // get result from result set
696- try (ResultSet rs = preparedStatement.executeQuery()) {
697- JSONArray parsedResult = new JSONArray();
698- while (rs.next()) {
699- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
700- parsedResult.add(JSONObject.parseObject(rs.getString(1)));
701- }
702- }
703- tableIndex.put(name, parsedResult);
704- } catch (SQLException e) {
705- modifyObjectNameList.remove(name);
706- }
707- }
708- }
709- } catch (SQLException e) {
710- log.error("get TopSQL object information fail:{}", e.getMessage());
711- throw new CustomException(e.getMessage());
712- }
713- results.put("object_name_list", modifyObjectNameList);
714- results.put("table_metadata", tableMetadata);
715- results.put("table_structure", tableStructure);
716- results.put("table_index", tableIndex);
717- return results;
718- }
719-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/topsql/TopSQLHandler.java+0-92
@@ -1,92 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.handler.topsql;
5- 
6-import java.sql.Connection;
7-import java.util.List;
8- 
9-import com.alibaba.fastjson.JSONObject;
10-import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;
11-import com.nctigba.observability.instance.dto.topsql.TopSQLNowReq;
12-import com.nctigba.observability.instance.model.InstanceNodeInfo;
13- 
14-public interface TopSQLHandler {
15- /**
16- * target database type
17- *
18- * @return database type
19- */
20- String getDatabaseType();
21- 
22- /**
23- * test instance is success
24- *
25- * @param nodeInfo instance node info
26- * @return Connection
27- */
28- Connection getConnection(InstanceNodeInfo nodeInfo);
29- 
30- /**
31- * fetch TopSQL list from database
32- *
33- * @param nodeInfo instance node info
34- * @param topSQLListReq dto
35- * @return TopSQL list
36- */
37- List<JSONObject> getTopSQLList(InstanceNodeInfo nodeInfo, TopSQLListReq topSQLListReq);
38- 
39- /**
40- * fetch TopSQL now list from database
41- *
42- * @param nodeInfo instance node info
43- * @param topSQLNowReq dto
44- * @return TopSQL list
45- */
46- List<JSONObject> getTopSQLNow(InstanceNodeInfo nodeInfo, TopSQLNowReq topSQLNowReq);
47- 
48- /**
49- * fetch TopSQL statistical information from database
50- *
51- * @param nodeInfo instance node info
52- * @param sqlId TopSQL debug query id
53- * @return TopSQL statistical information
54- */
55- JSONObject getStatisticalInfo(InstanceNodeInfo nodeInfo, String sqlId);
56- 
57- /**
58- * fetch TopSQL execution plan from database
59- *
60- * @param nodeInfo instance node info
61- * @param sqlId TopSQL debug query id
62- * @return TopSQL execution plan
63- */
64- JSONObject getExecutionPlan(InstanceNodeInfo nodeInfo, String sqlId, String type);
65- 
66- /**
67- * fetch TopSQL Partition List from database
68- *
69- * @param nodeInfo instance node info
70- * @param sqlId TopSQL debug query id
71- * @return TopSQL Partition List
72- */
73- List<JSONObject> getPartitionList(InstanceNodeInfo nodeInfo, String sqlId);
74- 
75- /**
76- * fetch TopSQL index advice from database
77- *
78- * @param nodeInfo instance node info
79- * @param sqlId TopSQL debug query id
80- * @return TopSQL index advice
81- */
82- List<String> getIndexAdvice(InstanceNodeInfo nodeInfo, String sqlId);
83- 
84- /**
85- * fetch TopSQL object information from database
86- *
87- * @param nodeInfo instance node info
88- * @param sqlId TopSQL debug query id
89- * @return TopSQL object information
90- */
91- JSONObject getObjectInfo(InstanceNodeInfo nodeInfo, String sqlId);
92-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/listener/PluginListener.java+2-0
@@ -37,6 +37,8 @@ public class PluginListener implements ApplicationListener<ApplicationEvent> {
37 firstMenu.getMenuId());37 firstMenu.getMenuId());
38 menuFacade.savePluginRoute(pluginId, "会话详情", "Session Details", "vem/sessionDetail",38 menuFacade.savePluginRoute(pluginId, "会话详情", "Session Details", "vem/sessionDetail",
39 firstMenu.getMenuId());39 firstMenu.getMenuId());
40+ menuFacade.savePluginMenu(pluginId, "集群监控", "Cluster OPS", 10, "vem/dashboard/clusters",
41+ firstMenu.getMenuId());
40 }42 }
41 } else if (event instanceof ContextClosedEvent) {43 } else if (event instanceof ContextClosedEvent) {
42 MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext()44 MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext()
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/AspMapper.java+67-0
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import java.util.List;
8+import java.util.Map;
9+ 
10+import org.apache.ibatis.annotations.Mapper;
11+import org.apache.ibatis.annotations.Select;
12+ 
13+import com.nctigba.observability.instance.dto.asp.AnalysisDto;
14+import com.nctigba.observability.instance.dto.asp.AspCountReq;
15+ 
16+/**
17+ * AspMapper.java
18+ *
19+ * @since 2023-08-25
20+ */
21+@Mapper
22+public interface AspMapper {
23+ /**
24+ * count
25+ *
26+ * @param req AspCountReq
27+ * @return List<Map<String, Object>>
28+ */
29+ @Select("select TO_CHAR(sample_time,'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') sample_time,a.session_count from ( "
30+ + "select LOCAL_ACTIVE_SESSION1.sampleid,sample_time,session_count from ( "
31+ + "select sampleid,count(*) session_count from dbe_perf.LOCAL_ACTIVE_SESSION " + "group by sampleid "
32+ + ")LOCAL_ACTIVE_SESSION1 " + "left join ( "
33+ + "SELECT sampleid, MIN(sample_time) AS sample_time FROM dbe_perf.LOCAL_ACTIVE_SESSION "
34+ + "GROUP BY sampleid "
35+ + ")LOCAL_ACTIVE_SESSION2 on LOCAL_ACTIVE_SESSION1.sampleid = LOCAL_ACTIVE_SESSION2.sampleid "
36+ + "union all " + "select gs_asp1.sampleid,sample_time,session_count from ( "
37+ + "select sampleid,count(*) session_count from gs_asp " + "group by sampleid " + ")gs_asp1 "
38+ + "left join ( " + "SELECT sampleid, MIN(sample_time) AS sample_time FROM gs_asp " + "GROUP BY sampleid "
39+ + ")gs_asp2 on gs_asp1.sampleid = gs_asp2.sampleid " + ")a "
40+ + "where a.sample_time >= #{startTime} and a.sample_time <= #{finishTime} " + "order by a.sample_time ")
41+ List<Map<String, Object>> count(AspCountReq req);
42+ 
43+ /**
44+ * analysis
45+ *
46+ * @param req AspCountReq
47+ * @return List<AnalysisDto>
48+ */
49+ @Select("select * from (select gs_asp1.sampleid,TO_CHAR(gs_asp2.sample_time,"
50+ + "'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') sample_time,gs_asp1.databaseid, "
51+ + "gs_asp1.thread_id,gs_asp1.sessionid,gs_asp1.start_time,gs_asp1.event,gs_asp1.userid, "
52+ + "gs_asp1.application_name,host(gs_asp1.client_addr) client_addr,gs_asp1.client_hostname, "
53+ + "gs_asp1.client_port,gs_asp1.query_id,gs_asp1.unique_query_id,gs_asp1.user_id, "
54+ + "gs_asp1.cn_id,gs_asp1.unique_query,gs_asp1.lockmode,gs_asp1.wait_status " + "from GS_ASP gs_asp1 "
55+ + "left join ( " + "SELECT sampleid, MIN(sample_time) AS sample_time FROM gs_asp "
56+ + "GROUP BY sampleid) gs_asp2 on gs_asp1.sampleid = gs_asp2.sampleid "
57+ + "where gs_asp2.sample_time >= #{startTime} and gs_asp2.sample_time <= " + "#{finishTime} "
58+ + "union all " + "select s1.sampleid,TO_CHAR(s2.sample_time,'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') "
59+ + "sample_time,s1.databaseid, " + "s1.thread_id,s1.sessionid,s1.start_time,s1.event,s1.userid, "
60+ + "s1.application_name,host(s1.client_addr) client_addr,s1.client_hostname, "
61+ + "s1.client_port,s1.query_id,s1.unique_query_id,s1.user_id, "
62+ + "s1.cn_id,s1.unique_query,s1.lockmode,s1.wait_status " + "from dbe_perf.LOCAL_ACTIVE_SESSION s1 "
63+ + "left join ( " + "SELECT sampleid, MIN(sample_time) AS sample_time FROM dbe_perf.LOCAL_ACTIVE_SESSION "
64+ + "GROUP BY sampleid) s2 on s1.sampleid = s2.sampleid "
65+ + "where s2.sample_time >= #{startTime} and s2.sample_time <= #{finishTime}) " + "order by sample_time; ")
66+ List<AnalysisDto> analysis(AspCountReq req);
67+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ClustersMapper.java+51-0
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import java.util.List;
8+ 
9+import org.apache.ibatis.annotations.Mapper;
10+import org.apache.ibatis.annotations.Select;
11+ 
12+import com.nctigba.observability.instance.aop.Ds;
13+import com.nctigba.observability.instance.dto.cluster.NodeRelationDto;
14+import com.nctigba.observability.instance.dto.cluster.SyncSituation;
15+ 
16+/**
17+ * ClustersMapper
18+ *
19+ * @author liupengfei
20+ * @since 2023/8/25
21+ */
22+@Mapper
23+public interface ClustersMapper {
24+ /**
25+ * getSyncSituation
26+ *
27+ * @param id String
28+ * @return List<SyncSituation>
29+ */
30+ @Select("SELECT host(r.client_addr) host_ip, "
31+ + "pg_size_pretty(pg_xlog_location_diff(s.sender_sent_location,s.receiver_received_location)) "
32+ + "received_delay, "
33+ + "pg_size_pretty(pg_xlog_location_diff(s.sender_write_location,s.receiver_write_location)) write_delay, "
34+ + "pg_size_pretty(pg_xlog_location_diff(s.sender_replay_location,s.receiver_replay_location)) "
35+ + "replay_delay, " + "s.sync_state as sync, s.state as wal_sync_state, s.sync_priority "
36+ + "FROM pg_stat_replication r , pg_stat_get_wal_senders() s " + "where r.pid = s.pid;")
37+ @Ds
38+ List<SyncSituation> getSyncSituation(String id);
39+ 
40+ /**
41+ * relation
42+ *
43+ * @param id String
44+ * @return List<NodeRelationDto>
45+ */
46+ @Select("SELECT host(client_addr) host_ip, "
47+ + "pg_xlog_location_diff(sender_sent_location,receiver_replay_location) replay_delay, "
48+ + "sync_state FROM pg_stat_replication")
49+ @Ds
50+ List<NodeRelationDto> relation(String id);
51+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/DbConfigMapper.java+81-4
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.mapper;5package com.nctigba.observability.instance.mapper;
5 6 
6import java.util.List;7import java.util.List;
@@ -9,14 +10,90 @@ import java.util.Map;
9import org.apache.ibatis.annotations.Mapper;10import org.apache.ibatis.annotations.Mapper;
10import org.apache.ibatis.annotations.Select;11import org.apache.ibatis.annotations.Select;
11 12 
13+/**
14+ * DbConfigMapper.java
15+ *
16+ * @since 2023-08-28
17+ */
12@Mapper18@Mapper
13public interface DbConfigMapper {19public interface DbConfigMapper {
20+ /**
21+ * memoryNodeDetail
22+ *
23+ * @return List
24+ */
14 @Select("select memorytype, memorymbytes from dbe_perf.memory_node_detail")25 @Select("select memorytype, memorymbytes from dbe_perf.memory_node_detail")
15 List<Map<String, Object>> memoryNodeDetail();26 List<Map<String, Object>> memoryNodeDetail();
16 27 
17- @Select("select name, decode(unit,null,setting,setting||'('||unit||')') as value "28+ /**
18- + "from dbe_perf.GLOBAL_CONFIG_SETTINGS where name in ('max_process_memory',"29+ * memoryConfig
19- + "'shared_buffers','temp_buffers','work_mem','query_mem','query_max_mem',"30+ *
20- + "'maintenance_work_mem','cstore_buffers','memorypool_enable','memorypool_size')")31+ * @return List
32+ */
33+ @Select("select name, case when unit = '8kB' then CAST(CAST(setting AS DECIMAL(10)) * 8 as varchar(10)) || '(kB)' "
34+ + "else decode(unit,null,setting,setting||'('||unit||')') end as value from dbe_perf.GLOBAL_CONFIG_SETTINGS"
35+ + " where name in ('max_process_memory', 'shared_buffers', 'temp_buffers', 'work_mem', 'query_mem', "
36+ + "'query_max_mem', 'maintenance_work_mem', 'cstore_buffers', 'memorypool_enable', 'memorypool_size')")
21 List<Map<String, Object>> memoryConfig();37 List<Map<String, Object>> memoryConfig();
38+ 
39+ /**
40+ * settings
41+ *
42+ * @return List
43+ */
44+ @Select("select name,setting from pg_settings")
45+ List<Map<String, String>> settings();
46+ 
47+ /**
48+ * workMem
49+ *
50+ * @return String
51+ */
52+ @Select("select setting from pg_settings WHERE name = 'work_mem'")
53+ String workMem();
54+ 
55+ /**
56+ * version
57+ *
58+ * @return String
59+ */
60+ @Select("SELECT version()")
61+ String version();
62+ 
63+ /**
64+ * starttime
65+ *
66+ * @return String
67+ */
68+ @Select("SELECT pg_postmaster_start_time()")
69+ String starttime();
70+ 
71+ /**
72+ * archiveMode
73+ *
74+ * @return String
75+ */
76+ @Select("SHOW archive_mode")
77+ String archiveMode();
78+ 
79+ /**
80+ * env
81+ *
82+ * @return Map
83+ */
84+ @Select("select node_name, host, process, port, installpath, datapath, log_directory from pg_node_env")
85+ Map<String, String> env();
86+ 
87+ /**
88+ * waitEvents
89+ *
90+ * @return List
91+ */
92+ @Select("select s.node_name, s.db_name, s.thread_name, s.lwtid tid, a.sessionid, "
93+ + "coalesce(s.block_sessionid, 0) block_sessionid, a.query_id, s.wait_status, s.wait_event, "
94+ + "coalesce(s.lockmode, ' ' ) lockmode, coalesce(s.locktag, ' ') locktag, "
95+ + "coalesce(locktag_decode(locktag), ' ') tag from pg_stat_activity a "
96+ + "left join dbe_perf.THREAD_WAIT_STATUS s on a.sessionid = s.sessionid "
97+ + "where state = 'active' and a.pid <> pg_backend_pid()")
98+ List<Map<String, Object>> waitEvents();
22}99}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ParamInfoMapper.java+5-5
@@ -16,14 +16,14 @@ import org.springframework.stereotype.Service;
16import com.nctigba.observability.instance.config.ParamInfoInitConfig;16import com.nctigba.observability.instance.config.ParamInfoInitConfig;
17import com.nctigba.observability.instance.constants.CommonConstants;17import com.nctigba.observability.instance.constants.CommonConstants;
18import com.nctigba.observability.instance.entity.ParamInfo;18import com.nctigba.observability.instance.entity.ParamInfo;
19-import com.nctigba.observability.instance.entity.ParamInfo.type;19+import com.nctigba.observability.instance.entity.ParamInfo.ParamType;
20 20 
21@Service21@Service
22public class ParamInfoMapper implements InitializingBean {22public class ParamInfoMapper implements InitializingBean {
23 private static final String SQL = "select * from param_info";23 private static final String SQL = "select * from param_info";
24 private static final List<ParamInfo> LIST = new ArrayList<>();24 private static final List<ParamInfo> LIST = new ArrayList<>();
25 private static final Map<Integer, ParamInfo> IDS = new HashMap<>();25 private static final Map<Integer, ParamInfo> IDS = new HashMap<>();
26- private static final Map<ParamInfo.type, Map<String, ParamInfo>> MAP = new HashMap<>();26+ private static final Map<ParamInfo.ParamType, Map<String, ParamInfo>> MAP = new HashMap<>();
27 27 
28 @Override28 @Override
29 public void afterPropertiesSet() throws Exception {29 public void afterPropertiesSet() throws Exception {
@@ -41,7 +41,7 @@ public class ParamInfoMapper implements InitializingBean {
41 IDS.put(paramInfo.getId(), paramInfo);41 IDS.put(paramInfo.getId(), paramInfo);
42 }42 }
43 synchronized (MAP) {43 synchronized (MAP) {
44- for (type v : ParamInfo.type.values())44+ for (ParamType v : ParamInfo.ParamType.values())
45 MAP.put(v, new HashMap<>());45 MAP.put(v, new HashMap<>());
46 for (ParamInfo paramInfo : list)46 for (ParamInfo paramInfo : list)
47 MAP.get(paramInfo.getParamType()).put(paramInfo.getParamName(), paramInfo);47 MAP.get(paramInfo.getParamType()).put(paramInfo.getParamName(), paramInfo);
@@ -60,11 +60,11 @@ public class ParamInfoMapper implements InitializingBean {
60 return LIST;60 return LIST;
61 }61 }
62 62 
63- public static ParamInfo getParamInfo(ParamInfo.type type, String name) {63+ public static ParamInfo getParamInfo(ParamInfo.ParamType type, String name) {
64 return MAP.get(type).get(name);64 return MAP.get(type).get(name);
65 }65 }
66 66 
67- public static List<Integer> getIds(type t) {67+ public static List<Integer> getIds(ParamType t) {
68 return MAP.get(t).values().stream().map(ParamInfo::getId).collect(Collectors.toList());68 return MAP.get(t).values().stream().map(ParamInfo::getId).collect(Collectors.toList());
69 }69 }
70}70}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/PgSettingMapper.java+19-0
@@ -0,0 +1,19 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import org.apache.ibatis.annotations.Mapper;
8+ 
9+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
10+import com.nctigba.observability.instance.entity.PgSettings;
11+ 
12+/**
13+ * PgSettingMapper.java
14+ *
15+ * @since 2023-08-25
16+ */
17+@Mapper
18+public interface PgSettingMapper extends BaseMapper<PgSettings> {
19+}
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/session/OpenGaussSessionHandler.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/SessionMapper.java+172-345
@@ -1,346 +1,173 @@
1-/*1+/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */3+ */
4- 4+ 
5-package com.nctigba.observability.instance.handler.session;5+package com.nctigba.observability.instance.mapper;
6- 6+ 
7-import com.alibaba.fastjson.JSONObject;7+import java.util.List;
8-import com.nctigba.common.web.exception.InstanceException;8+import java.util.Map;
9-import com.nctigba.observability.instance.constants.CommonConstants;9+ 
10-import com.nctigba.observability.instance.constants.DatabaseType;10+import org.apache.ibatis.annotations.Mapper;
11-import com.nctigba.observability.instance.dto.session.DetailStatisticDto;11+import org.apache.ibatis.annotations.Select;
12-import com.nctigba.observability.instance.model.InstanceNodeInfo;12+ 
13-import com.nctigba.observability.instance.service.ClusterManager;13+/**
14-import lombok.RequiredArgsConstructor;14+ * SessionMapper
15-import lombok.extern.slf4j.Slf4j;15+ *
16-import org.apache.commons.lang3.ObjectUtils;16+ * @author liupengfei
17-import org.apache.commons.lang3.StringUtils;17+ * @since 2023/8/25
18-import org.springframework.stereotype.Component;18+ */
19- 19+@Mapper
20-import java.sql.Connection;20+public interface SessionMapper {
21-import java.sql.PreparedStatement;21+ /**
22-import java.sql.ResultSet;22+ * sessionIsWaiting
23-import java.sql.SQLException;23+ *
24-import java.util.ArrayList;24+ * @param session session
25-import java.util.HashMap;25+ * @return int
26-import java.util.LinkedList;26+ */
27-import java.util.List;27+ @Select("select count(waiting) as count from pg_stat_activity where sessionid = #{session} and waiting")
28-import java.util.Map;28+ int sessionIsWaiting(String session);
29-import java.util.Set;29+ 
30-import java.util.stream.Collectors;30+ /**
31- 31+ * generalMesList
32-@Component32+ *
33-@RequiredArgsConstructor33+ * @param session session
34-@Slf4j34+ * @return List
35-public class OpenGaussSessionHandler implements SessionHandler {35+ */
36- private static final String TEST_SQL = "select 1";36+ @Select("select a.state, a.sessionid, a.datname, a.usename, "
37- private static final String CHECK_SESSION_IS_WAIT_SQL = "select count(waiting) as count from pg_stat_activity "37+ + "a.resource_pool, b.lwtid , TO_CHAR(a.backend_start,'YYYY-MM-DD HH:MI:SS') as backend_start, "
38- + "where sessionid = ? and waiting";38+ + "TO_CHAR((now()- a.backend_start),'HH24:MI:SS') as backend_runtime, "
39- private static final String SESSION_GENERAL_MES_SQL = "select a.state, a.sessionid, a.datname, a.usename, "39+ + "a.client_addr, a.application_name, a.client_hostname, a.client_port, "
40- + "a.resource_pool, b.lwtid , TO_CHAR(a.backend_start,'YYYY-MM-DD HH:MI:SS') as backend_start, "40+ + "TO_CHAR(a.xact_start ,'YYYY-MM-DD HH:MI:SS') as xact_start, "
41- + "TO_CHAR((now()- a.backend_start),'HH24:MI:SS') as backend_runtime, "41+ + "TO_CHAR(a.query_start ,'YYYY-MM-DD HH:MI:SS') as query_start, " + "a.datname, a.query_id, a.query "
42- + "a.client_addr, a.application_name, a.client_hostname, a.client_port, "42+ + "from pg_stat_activity a left join pg_thread_wait_status b on a.sessionid=b.sessionid "
43- + "TO_CHAR(a.xact_start ,'YYYY-MM-DD HH:MI:SS') as xact_start, "43+ + "where a.sessionid = #{session}")
44- + "TO_CHAR(a.query_start ,'YYYY-MM-DD HH:MI:SS') as query_start, " + "a.datname, a.query_id, a.query "44+ List<Map<String, Object>> generalMesList(String session);
45- + "from pg_stat_activity a left join pg_thread_wait_status b on a.sessionid=b.sessionid "45+ 
46- + "where a.sessionid = ?";46+ /**
47- private static final String SESSION_BLOCK_MES_SQL = "select a.block_sessionid, pg_relation_filepath(b.relation)"47+ * blockList
48- + " as filepath, b.page, b.tuple, b.bucket, a.wait_status, a.wait_event, a.lockmode, "48+ *
49- + "(select d.nspname || '.' || c.relname from pg_class c left join pg_namespace d "49+ * @param session String
50- + "on c.relnamespace = d.oid " + "where c.oid = b.relation) as namespace_relation "50+ * @return List
51- + "from pg_thread_wait_status a left join pg_locks b on a.sessionid = b.sessionid "51+ */
52- + "where a.sessionid = ? and not granted";52+ @Select("select a.block_sessionid, pg_relation_filepath(b.relation)"
53- private static final String SESSION_STATISTIC_SQL = "select stat_name,value from GS_SESSION_TIME "53+ + " as filepath, b.page, b.tuple, b.bucket, a.wait_status, a.wait_event, a.lockmode, "
54- + "where sessid like '%' || ?";54+ + "(select d.nspname || '.' || c.relname from pg_class c left join pg_namespace d "
55- private static final String SESSION_RUNTIME_SQL = "select statname,value from GS_SESSION_STAT "55+ + "on c.relnamespace = d.oid " + "where c.oid = b.relation) as namespace_relation "
56- + "where sessid like '%' || ?";56+ + "from pg_thread_wait_status a left join pg_locks b on a.sessionid = b.sessionid "
57- private static final String SESSION_WAITING_REC_SQL = "select sample_time,wait_status,event,lockmode,locktag_decode"57+ + "where a.sessionid = #{session} and not granted")
58- + "(locktag) as locktag from GS_ASP where sessionid = ? order by sample_time desc limit 10";58+ List<Map<String, Object>> blockList(String session);
59- private static final String SESSION_BLOCK_TREE = "with recursive tmp_lock as ( " + " select distinct "59+ 
60- + " w.sessionid as id, " + " r.sessionid as parentid, "60+ /**
61- + " w.wait_status as wait_status, " + " w.wait_event as wait_event, "61+ * statistic
62- + " w.lockmode as lockmode " + " from ( "62+ *
63- + " select a.sessionid,a.locktype,a.database, "63+ * @param session session
64- + " a.relation,a.page,a.tuple,a.classid, "64+ * @return List
65- + " a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid, "65+ */
66- + " a.transactionid, b.query as query, "66+ @Select("select stat_name,value from GS_SESSION_TIME where sessid like '%' || #{session}")
67- + " b.xact_start,b.query_start,b.usename,b.datname ,c.wait_status,c.wait_event,c.lockmode "67+ List<Map<String, Object>> statistic(String session);
68- + " from pg_locks a " + " left join pg_stat_activity b on a.sessionid=b.sessionid "68+ 
69- + " left join pg_thread_wait_status c on b.sessionid=c.sessionid "69+ /**
70- + " where not a.granted " + " ) w, " + " ( "70+ * runtime
71- + " select a.sessionid,a.locktype,a.database, "71+ *
72- + " a.relation,a.page,a.tuple,a.classid, "72+ * @param session session
73- + " a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid, "73+ * @return List
74- + " a.transactionid, b.query as query, "74+ */
75- + " b.xact_start,b.query_start,b.usename,b.datname ,c.wait_status,c.wait_event,c.lockmode "75+ @Select("select statname,value from GS_SESSION_STAT where sessid like '%' || #{session}")
76- + " from pg_locks a " + " left join pg_stat_activity b on a.sessionid=b.sessionid "76+ List<Map<String, Object>> runtime(String session);
77- + " left join pg_thread_wait_status c on b.sessionid=c.sessionid "77+ 
78- + " where a.granted " + " ) r " + " where 1=1 "78+ /**
79- + " and r.locktype is not distinct from w.locktype "79+ * detailWaiting
80- + " and r.database is not distinct from w.database "80+ *
81- + " and r.relation is not distinct from w.relation "81+ * @param session session
82- + " and r.page is not distinct from w.page " + " and r.tuple is not distinct from w.tuple "82+ * @return List
83- + " and r.classid is not distinct from w.classid "83+ */
84- + " and r.objid is not distinct from w.objid "84+ @Select("select sample_time,wait_status,event,lockmode,locktag_decode"
85- + " and r.objsubid is not distinct from w.objsubid "85+ + "(locktag) as locktag from GS_ASP where sessionid = #{session} order by sample_time desc limit 10")
86- + " and r.transactionid is not distinct from w.transactionid "86+ List<Map<String, Object>> detailWaiting(String session);
87- + " and r.sessionid <> w.sessionid " + "),tmp0 as ( " + " select * " + " from tmp_lock tl "87+ 
88- + " union all " + " select t1.parentid,0::int4,p.wait_status,p.wait_event,null "88+ /**
89- + " from tmp_lock t1 left join pg_thread_wait_status p on t1.parentid=p.sessionid " + " where 1=1 "89+ * blockTree
90- + " and t1.parentid not in (select id from tmp_lock) " + "), " + " tmp3 as ( "90+ *
91- + " SELECT array[id]::text[] as pathid,1 as depth,id,parentid,wait_status,wait_event,lockmode "91+ * @return List
92- + " FROM tmp0 " + " where 1=1 " + " and parentid=0 " + " union "92+ */
93- + " SELECT t0.pathid||array[t1.id]::text[] as pathid,t0.depth+1 as depth,t1.id,t1.parentid, "93+ @Select("with recursive tmp_lock as ( " + " select distinct " + " w.sessionid as id, "
94- + " t1.wait_status,t1.wait_event,t1.lockmode " + " FROM tmp0 t1, " + " tmp3 t0 "94+ + " r.sessionid as parentid, " + " w.wait_status as wait_status, "
95- + " where 1=1 " + " and t1.parentid=t0.id " + ") " + "select distinct "95+ + " w.wait_event as wait_event, " + " w.lockmode as lockmode " + " from ( "
96- + " '/'||array_to_string(a0.pathid,'/') as pathid, " + " a0.depth, "96+ + " select a.sessionid,a.locktype,a.database, "
97- + " a0.id,a0.parentid, a0.pathid[1] as tree_id, "97+ + " a.relation,a.page,a.tuple,a.classid, "
98- + " lpad(a0.id::text, 2*a0.depth-1+length(a0.id::text),' ') as tree, "98+ + " a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid, "
99- + " a2.datname,a2.usename,a2.application_name,a2.client_addr,a2.state, "99+ + " a.transactionid, b.query as query, "
100- + " TO_CHAR(a2.backend_start,'YYYY-MM-DD HH:MI:SS') as backend_start,a2.query, "100+ + " b.xact_start,b.query_start,b.usename,b.datname ,c.wait_status,c.wait_event,c.lockmode "
101- + " a0.wait_status,a0.wait_event,a0.lockmode " + " from tmp3 a0 "101+ + " from pg_locks a " + " left join pg_stat_activity b on a.sessionid=b.sessionid "
102- + " left outer join (select distinct '/'||id||'/' as prefix_id,id "102+ + " left join pg_thread_wait_status c on b.sessionid=c.sessionid "
103- + " from tmp0 " + " where 1=1 ) a1 "103+ + " where not a.granted " + " ) w, " + " ( "
104- + " on position( a1.prefix_id in '/'||array_to_string(a0.pathid,'/')||'/' ) >0 "104+ + " select a.sessionid,a.locktype,a.database, "
105- + " left outer join pg_stat_activity a2 " + " on a0.id = a2.sessionid "105+ + " a.relation,a.page,a.tuple,a.classid, "
106- + "order by '/'||array_to_string(a0.pathid,'/'),a0.depth;";106+ + " a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid, "
107- private final String SESSION_SIMPLE_MES_SQL = "select a.max_conn, b.active, c.waiting, d.max_runtime from "107+ + " a.transactionid, b.query as query, "
108- + "(select setting::int max_conn, 1 as key from pg_settings where name='max_connections') a "108+ + " b.xact_start,b.query_start,b.usename,b.datname ,c.wait_status,c.wait_event,c.lockmode "
109- + "left join " + "(select count(*) as active, 1 as key from pg_stat_activity where state = 'active') b "109+ + " from pg_locks a " + " left join pg_stat_activity b on a.sessionid=b.sessionid "
110- + "on a.key=b.key " + " left join "110+ + " left join pg_thread_wait_status c on b.sessionid=c.sessionid "
111- + " (select count(*) as waiting, 1 as key from pg_stat_activity where waiting is true) c "111+ + " where a.granted " + " ) r " + " where 1=1 "
112- + " on b.key=c.key " + " left join "112+ + " and r.locktype is not distinct from w.locktype "
113- + " (select extract(EPOCH from(max(now() - backend_start)))::INTEGER as max_runtime, 1 as key from "113+ + " and r.database is not distinct from w.database "
114- + "pg_stat_activity where application_name not in "114+ + " and r.relation is not distinct from w.relation "
115- + "('WLMArbiter','workload','WorkloadMonitor','WDRSnapshot','JobScheduler','PercentileJob'"115+ + " and r.page is not distinct from w.page " + " and r.tuple is not distinct from w.tuple "
116- + ",'statement flush thread','Asp','ApplyLauncher') and application_name not like 'DataKit%' ) d "116+ + " and r.classid is not distinct from w.classid "
117- + " on c.key = d.key";117+ + " and r.objid is not distinct from w.objid "
118- private final String LONG_TXC_SQL = "SELECT pid,sessionid,usename,datname,application_name,"118+ + " and r.objsubid is not distinct from w.objsubid "
119- + "client_addr,query, xact_start, now() - xact_start xact_duration, query_start, now() - query_start "119+ + " and r.transactionid is not distinct from w.transactionid "
120- + "query_duration, STATE FROM pg_stat_activity WHERE STATE <>'idle' and application_name not in "120+ + " and r.sessionid <> w.sessionid " + "),tmp0 as ( " + " select * " + " from tmp_lock tl "
121- + "('WLMArbiter','workload',"121+ + " union all " + " select t1.parentid,0::int4,p.wait_status,p.wait_event,null "
122- + "'WorkloadMonitor','WDRSnapshot','JobScheduler','PercentileJob','statement flush thread','Asp',"122+ + " from tmp_lock t1 left join pg_thread_wait_status p on t1.parentid=p.sessionid " + " where 1=1 "
123- + "'ApplyLauncher') and application_name not like 'DataKit%' "123+ + " and t1.parentid not in (select id from tmp_lock) " + "), " + " tmp3 as ( "
124- + "and now()-xact_start > interval '30 SECOND' " + "ORDER BY xact_start;";124+ + " SELECT array[id]::text[] as pathid,1 as depth,id,parentid,wait_status,wait_event,lockmode "
125- private final ClusterManager clusterManager;125+ + " FROM tmp0 " + " where 1=1 " + " and parentid=0 " + " union "
126- 126+ + " SELECT t0.pathid||array[t1.id]::text[] as pathid,t0.depth+1 as depth,t1.id,t1.parentid, "
127- @Override127+ + " t1.wait_status,t1.wait_event,t1.lockmode " + " FROM tmp0 t1, " + " tmp3 t0 "
128- public String getDatabaseType() {128+ + " where 1=1 " + " and t1.parentid=t0.id " + ") " + "select distinct "
129- return DatabaseType.DEFAULT.getDbType();129+ + " '/'||array_to_string(a0.pathid,'/') as pathid, " + " a0.depth, "
130- }130+ + " a0.id,a0.parentid, a0.pathid[1] as tree_id, "
131- 131+ + " lpad(a0.id::text, 2*a0.depth-1+length(a0.id::text),' ') as tree, "
132- @Override132+ + " a2.datname,a2.usename,a2.application_name,a2.client_addr::text client_addr,a2.state, "
133- public Connection getConnection(InstanceNodeInfo nodeInfo) {133+ + " TO_CHAR(a2.backend_start,'YYYY-MM-DD HH:MI:SS') as backend_start,a2.query, "
134- Connection connection = clusterManager.getConnectionByNodeInfo(nodeInfo);134+ + " a0.wait_status,a0.wait_event,a0.lockmode " + " from tmp3 a0 "
135- if (!testConnection(connection)) {135+ + " left outer join (select distinct '/'||id||'/' as prefix_id,id "
136- return null;136+ + " from tmp0 " + " where 1=1 ) a1 "
137- }137+ + " on position( a1.prefix_id in '/'||array_to_string(a0.pathid,'/')||'/' ) >0 "
138- return connection;138+ + " left outer join pg_stat_activity a2 " + " on a0.id = a2.sessionid "
139- }139+ + "order by '/'||array_to_string(a0.pathid,'/'),a0.depth;")
140- 140+ List<Map<String, Object>> blockTree();
141- @Override141+ 
142- public void close(Connection connection) {142+ /**
143- if (connection != null) {143+ * simpleStatistic
144- try {144+ *
145- connection.close();145+ * @return Map
146- } catch (SQLException e) {146+ */
147- throw new InstanceException("", e);147+ @Select("select a.max_conn, b.active, c.waiting, d.max_runtime from "
148- }148+ + "(select setting::int max_conn, 1 as key from pg_settings where name='max_connections') a "
149- }149+ + "left join " + "(select count(*) as active, 1 as key from pg_stat_activity where state = 'active') b "
150- }150+ + "on a.key=b.key " + " left join "
151- 151+ + " (select count(*) as waiting, 1 as key from pg_stat_activity where waiting is true) c "
152- @Override152+ + " on b.key=c.key " + " left join "
153- public JSONObject detailGeneral(Connection conn, String sessionid) {153+ + " (select extract(EPOCH from(max(now() - backend_start)))::INTEGER as max_runtime, 1 as key from "
154- JSONObject res = new JSONObject();154+ + "pg_stat_activity where application_name not in "
155- try {155+ + "('WLMArbiter','workload','WorkloadMonitor','WDRSnapshot','JobScheduler','PercentileJob'"
156- if (conn == null) {156+ + ",'statement flush thread','Asp','ApplyLauncher') and application_name not like 'DataKit%' ) d "
157- throw new InstanceException(CommonConstants.CONNECTION_FAIL);157+ + " on c.key = d.key")
158- }158+ Map<String, Object> simpleStatistic();
159- PreparedStatement generalMesStm = conn.prepareStatement(SESSION_GENERAL_MES_SQL);159+ 
160- generalMesStm.setLong(1, Long.parseLong(sessionid));160+ /**
161- List<JSONObject> generalMesList = executeQuery(generalMesStm);161+ * longTxc
162- if (generalMesList.size() == 0) {162+ *
163- throw new InstanceException("session.detail.general.message");163+ * @return List
164- }164+ */
165- res.putAll(generalMesList.get(0));165+ @Select("SELECT pid,sessionid,usename,datname,application_name,"
166- if (!checkSessionIsWaiting(conn, sessionid)) {166+ + "client_addr::text client_addr,query, xact_start, (now() - xact_start)::text xact_duration, query_start,"
167- return res;167+ + " (now() - query_start)::text query_duration, STATE FROM pg_stat_activity WHERE STATE <>'idle' "
168- }168+ + "and application_name not in ('WLMArbiter','workload',"
169- PreparedStatement blockStm = conn.prepareStatement(SESSION_BLOCK_MES_SQL);169+ + "'WorkloadMonitor','WDRSnapshot','JobScheduler','PercentileJob','statement flush thread','Asp',"
170- blockStm.setLong(1, Long.parseLong(sessionid));170+ + "'ApplyLauncher') and application_name not like 'DataKit%' "
171- List<JSONObject> blockList = executeQuery(blockStm);171+ + "and now()-xact_start > interval '30 SECOND' " + "ORDER BY xact_start;")
172- if (blockList.size() != 0) {172+ List<Map<String, Object>> longTxc();
173- res.putAll(blockList.get(0));
174- // throw new InstanceException(SESSION_DETAIL_BLOCK_MESSAGE, blockList.size());
175- }
176- } catch (SQLException e) {
177- log.error("get session_general_mes list fail", e);
178- throw new InstanceException(e.getMessage(), e);
179- }
180- return res;
181- }
182- 
183- @Override
184- public List<DetailStatisticDto> detailStatistic(Connection conn, String sessionid) {
185- ArrayList<DetailStatisticDto> list = new ArrayList<>();
186- if (conn == null) {
187- throw new InstanceException(CommonConstants.CONNECTION_FAIL);
188- }
189- try {
190- PreparedStatement statisticStm = conn.prepareStatement(SESSION_STATISTIC_SQL);
191- statisticStm.setLong(1, Long.parseLong(sessionid));
192- List<JSONObject> statisticList = executeQuery(statisticStm);
193- PreparedStatement runtimeStm = conn.prepareStatement(SESSION_RUNTIME_SQL);
194- runtimeStm.setLong(1, Long.parseLong(sessionid));
195- List<JSONObject> runtimeList = executeQuery(runtimeStm);
196- List<DetailStatisticDto> list1 = statisticList.stream()
197- .map(ob -> new DetailStatisticDto(DetailStatisticDto.Type.STATUS, ob.getString("stat_name"),
198- ob.getString("value")))
199- .collect(Collectors.toList());
200- List<DetailStatisticDto> list2 = runtimeList.stream()
201- .map(ob -> new DetailStatisticDto(DetailStatisticDto.Type.RUNTIME, ob.getString("statname"),
202- ob.getString("value")))
203- .collect(Collectors.toList());
204- list.addAll(list1);
205- list.addAll(list2);
206- } catch (SQLException e) {
207- throw new RuntimeException(e);
208- }
209- return list;
210- }
211- 
212- @Override
213- public List<JSONObject> detailWaiting(Connection conn, String sessionid) {
214- List<JSONObject> list = new ArrayList<>();
215- if (conn == null) {
216- throw new InstanceException(CommonConstants.CONNECTION_FAIL);
217- }
218- try {
219- PreparedStatement stm = conn.prepareStatement(SESSION_WAITING_REC_SQL);
220- stm.setLong(1, Long.parseLong(sessionid));
221- list = executeQuery(stm);
222- } catch (SQLException e) {
223- throw new RuntimeException(e);
224- }
225- return list;
226- }
227- 
228- @Override
229- public List<JSONObject> detailBlockTree(Connection conn, String sessionid) {
230- List<JSONObject> queryList;
231- if (conn == null) {
232- throw new InstanceException(CommonConstants.CONNECTION_FAIL);
233- }
234- try {
235- PreparedStatement stm = conn.prepareStatement(SESSION_BLOCK_TREE);
236- queryList = executeQuery(stm);
237- } catch (SQLException e) {
238- throw new RuntimeException(e);
239- }
240- if (StringUtils.isNotEmpty(sessionid)) {
241- Set<String> treeIdSet = queryList.stream().filter(obj -> obj.getString("pathid").contains(sessionid))
242- .map(obj -> obj.getString("tree_id")).collect(Collectors.toSet());
243- queryList = queryList.stream().filter(obj -> treeIdSet.contains(obj.getString("tree_id")))
244- .collect(Collectors.toList());
245- }
246- return toTreeData(queryList);
247- }
248- 
249- @Override
250- public JSONObject simpleStatistic(Connection conn) {
251- List<JSONObject> queryList;
252- if (conn == null) {
253- throw new InstanceException(CommonConstants.CONNECTION_FAIL);
254- }
255- try {
256- PreparedStatement stm = conn.prepareStatement(SESSION_SIMPLE_MES_SQL);
257- queryList = executeQuery(stm);
258- } catch (SQLException e) {
259- throw new RuntimeException(e);
260- }
261- return queryList.get(0);
262- }
263- 
264- @Override
265- public List<JSONObject> longTxc(Connection conn) {
266- List<JSONObject> queryList;
267- if (conn == null) {
268- throw new InstanceException(CommonConstants.CONNECTION_FAIL);
269- }
270- try {
271- PreparedStatement stm = conn.prepareStatement(LONG_TXC_SQL);
272- queryList = executeQuery(stm);
273- } catch (SQLException e) {
274- throw new RuntimeException(e);
275- }
276- return queryList;
277- }
278- 
279- private List<JSONObject> executeQuery(PreparedStatement stm) throws SQLException {
280- List<JSONObject> resList = new ArrayList<>();
281- ResultSet rs = stm.executeQuery();
282- while (rs.next()) {
283- JSONObject object = new JSONObject();
284- for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
285- object.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
286- }
287- resList.add(object);
288- }
289- return resList;
290- }
291- 
292- private boolean testConnection(Connection conn) {
293- if (ObjectUtils.isNotEmpty(conn)) {
294- try (PreparedStatement preparedStatement = conn.prepareStatement(TEST_SQL)) {
295- try (ResultSet rs = preparedStatement.executeQuery()) {
296- return true;
297- }
298- } catch (Exception e) {
299- log.error("test connection fail:{}", e.getMessage());
300- throw new InstanceException(e.getMessage());
301- }
302- }
303- return false;
304- }
305- 
306- private List<JSONObject> toTreeData(List<JSONObject> list) {
307- // Classify by parentid
308- Map<String, List<JSONObject>> map = new HashMap<>();
309- for (JSONObject object : list) {
310- object.put("children", new ArrayList<>());
311- if (!map.containsKey(object.getString("parentid"))) {
312- LinkedList<JSONObject> obs = new LinkedList<>();
313- obs.add(object);
314- map.put(object.getString("parentid"), obs);
315- } else {
316- List<JSONObject> obs = map.get(object.getString("parentid"));
317- obs.add(object);
318- }
319- }
320- // Get top parent list, wrapper the final tree result
321- List<JSONObject> topParentList = map.getOrDefault("0", new ArrayList<>());
322- // Get all child values recursively
323- recursionTreeChild(map, topParentList);
324- 
325- return topParentList;
326- }
327- 
328- @SuppressWarnings("unchecked")
329- public void recursionTreeChild(Map<String, List<JSONObject>> map, List<JSONObject> parentList) {
330- for (JSONObject object : parentList) {
331- if (map.containsKey(object.getString("id"))) {
332- List<JSONObject> newChildren = map.get(object.getString("id"));
333- List<JSONObject> allChildren = (List<JSONObject>) object.get("children");
334- allChildren.addAll(newChildren);
335- recursionTreeChild(map, allChildren);
336- }
337- }
338- }
339- 
340- private boolean checkSessionIsWaiting(Connection conn, String sessionid) throws SQLException {
341- PreparedStatement statement = conn.prepareStatement(CHECK_SESSION_IS_WAIT_SQL);
342- statement.setLong(1, Long.parseLong(sessionid));
343- List<JSONObject> query = executeQuery(statement);
344- return query.get(0).getIntValue("count") != 0;
345- }
346}173}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/SnapshotMapper.java+38-0
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import java.util.Date;
8+ 
9+import org.apache.ibatis.annotations.Mapper;
10+ 
11+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
12+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
13+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
14+import com.nctigba.observability.instance.aop.Ds;
15+import com.nctigba.observability.instance.entity.Snapshot;
16+ 
17+/**
18+ * SnapshotMapper.java
19+ *
20+ * @since 2023-08-28
21+ */
22+@Mapper
23+public interface SnapshotMapper extends BaseMapper<Snapshot> {
24+ /**
25+ * getIdByTime
26+ *
27+ * @param id id
28+ * @param column column
29+ * @param start start
30+ * @param end end
31+ * @return Long
32+ */
33+ @Ds
34+ default Long getIdByTime(String id, SFunction<Snapshot, ?> column, Date start, Date end) {
35+ var snapshot = selectOne(Wrappers.<Snapshot>lambdaQuery().between(column, start, end).last(" limit 1"));
36+ return snapshot == null ? 0 : snapshot.getSnapshotId();
37+ }
38+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/TopSqlMapper.java+189-1
@@ -1,13 +1,201 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.mapper;5package com.nctigba.observability.instance.mapper;
5 6 
7+import java.util.Collections;
8+import java.util.List;
9+import java.util.Map;
10+ 
6import org.apache.ibatis.annotations.Mapper;11import org.apache.ibatis.annotations.Mapper;
7import org.apache.ibatis.annotations.Select;12import org.apache.ibatis.annotations.Select;
13+import org.opengauss.util.PSQLException;
14+ 
15+import com.nctigba.observability.instance.constants.CommonConstants;
16+import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;
17+import com.nctigba.observability.instance.model.IndexAdvice;
18+ 
19+import cn.hutool.core.util.StrUtil;
8 20 
9@Mapper21@Mapper
10public interface TopSqlMapper {22public interface TopSqlMapper {
11- @Select("select statement_detail_decode(details,'plaintext',false) w from dbe_perf.statement_history where debug_query_id=#{id}")23+ /**
24+ * current top sql list
25+ *
26+ * @return curr list top sql
27+ */
28+ @Select("select round(extract(epoch FROM (now() - query_start)),2) as duration,query_start,unique_sql_id,datname ,"
29+ + "usename,application_name ,datid,pid,sessionid,usesysid,usename,client_addr ,client_hostname,client_port,"
30+ + "backend_start ,xact_start ,state_change ,waiting,enqueue,state ,resource_pool,query_id ,query ,"
31+ + "connection_info,trace_id from pg_stat_activity where query_start is not null and unique_sql_id != 0 "
32+ + "and duration != 0 order by (now() - query_start) desc limit 10")
33+ List<Map<String, Object>> currentTopsqlList();
34+ 
35+ /**
36+ * history top sql list
37+ *
38+ * @param topSQLListReq req
39+ * @return list top sql
40+ */
41+ @Select("select unique_query_id,debug_query_id,db_name,schema_name,user_name,application_name,query,start_time,"
42+ + "finish_time,db_time,cpu_time,execution_time, data_io_time "
43+ + "from dbe_perf.statement_history where debug_query_id != 0 "
44+ + "and finish_time >= #{startTimeTime} and finish_time <= #{finishTimeTime} order by ${orderField} desc,"
45+ + "execution_time desc,cpu_time desc,db_time desc limit 10")
46+ List<Map<String, Object>> historyTopsqlList(TopSQLListReq topSQLListReq);
47+ 
48+ /**
49+ * sql detail
50+ *
51+ * @param id sqlId
52+ * @return detail
53+ */
54+ @Select("select query, query_id debug_query_id, unique_sql_id unique_query_id, datname db_name, usename user_name,"
55+ + " application_name , query_start start_time, client_addr || ':' || client_port socket , sessionid "
56+ + "from pg_stat_activity where query_id = #{id} limit 1")
57+ Map<String, Object> currentDetail(String id);
58+ 
59+ /**
60+ * statistical info list
61+ *
62+ * @param id sqlId
63+ * @return statistical info
64+ */
65+ @Select("select query, debug_query_id, unique_query_id, db_name, schema_name, substring(start_time, 0, 20) "
66+ + "start_time, substring(finish_time, 0, 20) finish_time, user_name, application_name, client_addr || ':' "
67+ + "|| client_port socket, n_returned_rows, n_tuples_fetched, n_tuples_returned, n_tuples_inserted,"
68+ + " n_tuples_updated, n_tuples_deleted, lock_count, lock_wait_count, lock_max_count, (case when "
69+ + "n_blocks_fetched = 0 then '-' else substring((n_blocks_hit / n_blocks_fetched)* 100, 0, 6)|| '%' end) "
70+ + "as blocks_hit_rate, " + "json_extract_path_text(net_send_info::json,'size') net_send_info_size, "
71+ + "json_extract_path_text(net_recv_info::json,'size') net_recv_info_size, "
72+ + "json_extract_path_text(net_stream_send_info::json,'size') net_stream_send_info_size, "
73+ + "json_extract_path_text(net_stream_recv_info::json,'size') net_stream_recv_info_size, "
74+ + "json_extract_path_text(net_send_info::json,'n_calls') net_send_info_calls, "
75+ + "json_extract_path_text(net_recv_info::json,'n_calls') net_recv_info_calls, "
76+ + "json_extract_path_text(net_stream_send_info::json,'n_calls') net_stream_send_info_calls, "
77+ + "json_extract_path_text(net_stream_recv_info::json,'n_calls') net_stream_recv_info_calls, "
78+ + "json_extract_path_text(net_send_info::json,'time') net_send_info_time, "
79+ + "json_extract_path_text(net_recv_info::json,'time') net_recv_info_time, "
80+ + "json_extract_path_text(net_stream_send_info::json,'time') net_stream_send_info_time, "
81+ + "json_extract_path_text(net_stream_recv_info::json,'time') net_stream_recv_info_time,"
82+ + " n_soft_parse, n_hard_parse, db_time/1000 db_time, "
83+ + "cpu_time/1000 cpu_time, (db_time-cpu_time)/1000 wait_time, lock_time/1000 lock_time, "
84+ + "lock_wait_time/1000 lock_wait_time, execution_time/1000 execution_time, parse_time/1000 parse_time,"
85+ + " plan_time/1000 plan_time,rewrite_time/1000 rewrite_time, pl_execution_time/ 1000 pl_execution_time, "
86+ + "pl_compilation_time/1000 pl_compilation_time, data_io_time/1000 data_io_time"
87+ + " from dbe_perf.statement_history where debug_query_id = #{id} limit 1")
88+ Map<String, Object> historyDetail(String id);
89+ 
90+ /**
91+ * current query plan
92+ *
93+ * @param id sqlId
94+ * @return query plan string
95+ */
96+ @Select("select query_plan from gs_wlm_session_statistics where queryid = #{id}")
97+ String currentPlan(String id);
98+ 
99+ /**
100+ * history query plan for sql
101+ *
102+ * @param id sqlId
103+ * @return query plan string
104+ */
105+ @Select("select query_plan from dbe_perf.statement_history where debug_query_id=#{id}")
106+ String historyPlan(String id);
107+ 
108+ /**
109+ * history sqlId to sql
110+ *
111+ * @param id sqlId
112+ * @return sql string
113+ */
114+ @Select("select query from dbe_perf.statement_history where debug_query_id=#{id}")
115+ String sql(String id);
116+ 
117+ /**
118+ * advise for sql
119+ *
120+ * @param sql sql string
121+ * @return advises
122+ */
123+ default List<IndexAdvice> advise(String sql) {
124+ try {
125+ return defAdvise(sql.replace(StrUtil.LF, CommonConstants.BLANK).replace("'", "''"));
126+ } catch (PSQLException e) {
127+ return Collections.emptyList();
128+ }
129+ }
130+ 
131+ /**
132+ * advice for sql from db
133+ *
134+ * @param sql formatted sql string, "{@code '}" to "{@code ''}" and {@code \n}
135+ * to "{@code }"
136+ * @return advise map
137+ * @throws PSQLException when sql error
138+ */
139+ @Select("select * from gs_index_advise('${sql}')")
140+ List<IndexAdvice> defAdvise(String sql) throws PSQLException;
141+ 
142+ /**
143+ * current wait event
144+ *
145+ * @param id sqlId
146+ * @return wait event
147+ */
148+ @Select("select * from ( select query_id, sessionid, sample_time, wait_status, event, lockmode, "
149+ + "locktag_decode(locktag) as locktag, block_sessionid lo from dbe_perf.LOCAL_ACTIVE_SESSION "
150+ + "where query_id = #{id} union all "
151+ + "select query_id, sessionid, sample_time, wait_status, event, lockmode, locktag_decode(locktag), "
152+ + "block_sessionid lo from GS_ASP where query_id = #{id} ) order by sample_time")
153+ List<Map<String, Object>> currentWaitEvent(String id);
154+ 
155+ /**
156+ * history sql wait event
157+ *
158+ * @param id sqlId
159+ * @return wait event json
160+ */
161+ @Select("select statement_detail_decode(details,'plaintext',false) w from dbe_perf.statement_history "
162+ + "where debug_query_id=#{id}")
12 String waitEvent(String id);163 String waitEvent(String id);
164+ 
165+ /**
166+ * table meta data
167+ *
168+ * @param relname table relname
169+ * @return meta data
170+ */
171+ @Select("select schemaname, t1.relname, pg_relation_size(relid) object_size, relkind object_type, n_live_tup, "
172+ + "n_dead_tup, case when n_live_tup + n_dead_tup = 0 then '0.00%' else round(n_dead_tup * 100 /(n_dead_tup "
173+ + "+ n_live_tup), 2)|| '%' end dead_tup_ratio, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze "
174+ + "from pg_catalog.pg_stat_all_tables t1 left join pg_catalog.pg_class t2 on t1.relid = t2.oid "
175+ + "where t1.relname =#{relname} limit 1")
176+ Map<String, Object> tableMetaData(String relname);
177+ 
178+ /**
179+ * table structure
180+ *
181+ * @param relname table relname
182+ * @return table structure
183+ */
184+ @Select("select a.attnum, a.attname, t.typname, a.attlen, a.attnotnull, b.description from pg_catalog.pg_class c, "
185+ + "pg_catalog.pg_attribute a left outer join pg_catalog.pg_description b on a.attrelid = b.objoid and "
186+ + "a.attnum = b.objsubid, pg_catalog.pg_type t where c.relname = #{relname} and a.attnum>0 "
187+ + "and a.attrelid = c.oid and a.atttypid = t.oid limit 1")
188+ List<Map<String, Object>> tableStructure(String relname);
189+ 
190+ /**
191+ * index info for table
192+ *
193+ * @param relname table relname
194+ * @return index info
195+ */
196+ @Select("select a.attnum, a.attname, t.typname, a.attlen, a.attnotnull, b.description from pg_catalog.pg_class c, "
197+ + "pg_catalog.pg_attribute a left outer join pg_catalog.pg_description b on a.attrelid = b.objoid and "
198+ + "a.attnum = b.objsubid, pg_catalog.pg_type t where c.relname = #{relname} and a.attnum>0 and a.attrelid ="
199+ + " c.oid and a.atttypid = t.oid limit 1")
200+ List<Map<String, Object>> indexInfo(String relname);
13}201}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/DictionaryConfig.java+0-40
@@ -1,40 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model;
6- 
7-import com.baomidou.mybatisplus.annotation.TableField;
8-import com.baomidou.mybatisplus.annotation.TableId;
9-import com.baomidou.mybatisplus.annotation.TableName;
10- 
11-import lombok.AllArgsConstructor;
12-import lombok.Data;
13-import lombok.NoArgsConstructor;
14- 
15-@Data
16-@TableName("dictionary_config")
17-@NoArgsConstructor
18-@AllArgsConstructor
19-public class DictionaryConfig {
20- @TableId
21- String id;
22- @TableField("nodeId")
23- String nodeId;
24- String key;
25- String value;
26- 
27- public String getId() {
28- if ("0-0".equals(id))
29- return this.id;
30- return this.id = nodeId + "-" + key;
31- }
32- 
33- public DictionaryConfig(String nodeId, String key, String value) {
34- super();
35- this.nodeId = nodeId;
36- this.key = key;
37- this.value = value;
38- this.id = nodeId + "-" + key;
39- }
40-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/ExecutionPlan.java+184-7
@@ -5,10 +5,19 @@
5package com.nctigba.observability.instance.model;5package com.nctigba.observability.instance.model;
6 6 
7import java.util.ArrayList;7import java.util.ArrayList;
8+import java.util.Arrays;
9+import java.util.HashSet;
10+import java.util.LinkedList;
8import java.util.List;11import java.util.List;
12+import java.util.Set;
9import java.util.UUID;13import java.util.UUID;
10 14 
15+import org.opengauss.admin.common.exception.CustomException;
16+ 
17+import com.nctigba.observability.instance.constants.CommonConstants;
18+ 
11import lombok.Data;19import lombok.Data;
20+import lombok.NoArgsConstructor;
12 21 
13/**22/**
14 * <p>23 * <p>
@@ -19,9 +28,10 @@ import lombok.Data;
19 * @since 2022/10/08 12:3928 * @since 2022/10/08 12:39
20 */29 */
21@Data30@Data
31+@NoArgsConstructor
22public class ExecutionPlan {32public class ExecutionPlan {
23 // uuid;33 // uuid;
24- String id;34+ String id = UUID.randomUUID().toString();
25 // operation35 // operation
26 String nodeType;36 String nodeType;
27 // object37 // object
@@ -31,16 +41,183 @@ public class ExecutionPlan {
31 // total cost41 // total cost
32 Double totalCost;42 Double totalCost;
33 // rows43 // rows
34- Integer planRows;44+ Integer planRows = 0;
35 // width45 // width
36- Integer planWidth;46+ Integer planWidth = 0;
37 // condition47 // condition
38 String joinType;48 String joinType;
39 // sub nodes49 // sub nodes
40- List<ExecutionPlan> children;50+ List<ExecutionPlan> children = new ArrayList<>();
41 51 
42- public ExecutionPlan() {52+ /**
43- this.id = UUID.randomUUID().toString();53+ * ExecutionPlan
44- this.children = new ArrayList<>();54+ *
55+ * @param executionPlan executionPlan
56+ */
57+ public ExecutionPlan(String executionPlan) {
58+ var splitLines = Arrays.asList(executionPlan.split("\n"));
59+ 
60+ LinkedList<String> plan = new LinkedList<>(splitLines);
61+ // remove non-operation or non-condition lines
62+ for (String line : splitLines) {
63+ if ((line.contains("cost=") && !line.contains("Result")) || line.contains(CommonConstants.HASH_COND)) {
64+ continue;
65+ }
66+ plan.remove(line);
67+ }
68+ if (plan.size() == 0) {
69+ throw new CustomException("failResolveExecutionPlan");
70+ }
71+ processExecutionPlanString(plan.get(0));
72+ processExecutionPlan(plan.subList(1, plan.size()), 0, this, this.getChildren());
73+ }
74+ 
75+ /**
76+ * generate execution plan tree shape json object
77+ *
78+ * @param lines lines of execution plan and condition
79+ * @param previousIndent last level indent length
80+ * @param plan last level plan
81+ * @param children last level plan children
82+ */
83+ private void processExecutionPlan(List<String> lines, int previousIndent, ExecutionPlan plan,
84+ List<ExecutionPlan> children) {
85+ for (int i = 0; i < lines.size(); i++) {
86+ String line = lines.get(i);
87+ // process condition eg. Hash Cond: (s.datid = d.oid)
88+ if (line.contains(CommonConstants.HASH_COND)) {
89+ String[] split = line.split(": ");
90+ plan.setJoinType(split[1]);
91+ continue;
92+ }
93+ // skip processed lines
94+ if (line.isEmpty()) {
95+ continue;
96+ }
97+ String[] indentPlanSplit = line.split("->");
98+ int currentIndent = indentPlanSplit[0].length();
99+ // when current indent greater than previous indent, add new children node and
100+ // go into next tree level
101+ if (currentIndent > previousIndent) {
102+ lines.set(i, "");
103+ ExecutionPlan subPlan = processExecutionPlanString(line);
104+ children.add(subPlan);
105+ processExecutionPlan(lines.subList(i + 1, lines.size()), currentIndent, subPlan, subPlan.getChildren());
106+ }
107+ // when current indent less than or equals previous indent, return to last level
108+ if (currentIndent <= previousIndent) {
109+ return;
110+ }
111+ }
112+ }
113+ 
114+ /**
115+ * parse execution plan line string into object<br>
116+ * e.g.
117+ * {@code Function Scan on pg_show_all_settings a (cost=0.00..12.50 rows=5 p-time=0 p-rows=0 width=32)}
118+ *
119+ * @param line execution plan line string
120+ * @return execution plan line object
121+ */
122+ private ExecutionPlan processExecutionPlanString(String line) {
123+ if (line.contains(CommonConstants.HASH_COND)) {
124+ return new ExecutionPlan();
125+ }
126+ ExecutionPlan plan = new ExecutionPlan();
127+ // check if first line
128+ String planString;
129+ if (line.contains("->")) {
130+ String[] indentPlanSplit = line.split("->");
131+ planString = indentPlanSplit[1];
132+ } else {
133+ planString = line;
134+ }
135+ String[] operationParameterSplit = planString.split("\\(");
136+ // set operation name and alias name
137+ String operation = operationParameterSplit[0];
138+ if (operation.contains(" on ")) {
139+ String[] split = operation.split(" on ");
140+ plan.setNodeType(split[0].trim());
141+ // only show object name, remove alias name
142+ String objectName = split[1];
143+ String aliasName;
144+ if (objectName.contains(CommonConstants.BLANK)) {
145+ String[] aliasSplit = objectName.split(CommonConstants.BLANK);
146+ aliasName = aliasSplit[0];
147+ } else {
148+ aliasName = objectName;
149+ }
150+ plan.setAlias(aliasName);
151+ } else {
152+ plan.setNodeType(operation.trim());
153+ }
154+ // set parameters
155+ String parameters = operationParameterSplit[1].replace(")", "");
156+ String[] parametersSplit = parameters.split(CommonConstants.BLANK);
157+ for (String parameterSplit : parametersSplit) {
158+ String[] split = parameterSplit.split("=");
159+ switch (split[0].trim()) {
160+ case "cost":
161+ // set start cost and total cost
162+ String[] startCostTotalCostSplit = split[1].split("\\.\\.");
163+ plan.setStartupCost(Double.parseDouble(startCostTotalCostSplit[0]));
164+ plan.setTotalCost(Double.parseDouble(startCostTotalCostSplit[1]));
165+ break;
166+ case "rows":
167+ // set plan rows
168+ int rowsNum = Integer.parseInt(split[1]);
169+ plan.setPlanRows(rowsNum);
170+ break;
171+ case "width":
172+ // set plan width
173+ int widthNum = Integer.parseInt(split[1]);
174+ plan.setPlanWidth(widthNum);
175+ break;
176+ default:
177+ }
178+ }
179+ return plan;
180+ }
181+ 
182+ /**
183+ * totalPlanRows
184+ *
185+ * @return int
186+ */
187+ public int totalPlanRows() {
188+ int rows = planRows;
189+ for (ExecutionPlan executionPlan : children) {
190+ rows += executionPlan.totalPlanRows();
191+ }
192+ return rows;
193+ }
194+ 
195+ /**
196+ * totalPlanWidth
197+ *
198+ * @return int
199+ */
200+ public int totalPlanWidth() {
201+ int width = planWidth;
202+ for (ExecutionPlan executionPlan : children) {
203+ width += executionPlan.totalPlanWidth();
204+ }
205+ return width;
206+ }
207+ 
208+ /**
209+ * allAlias
210+ *
211+ * @return Set
212+ */
213+ public Set<String> allAlias() {
214+ Set<String> set = new HashSet<>();
215+ if (alias != null) {
216+ set.add(alias);
217+ }
218+ for (ExecutionPlan executionPlan : children) {
219+ set.addAll(executionPlan.allAlias());
220+ }
221+ return set;
45 }222 }
46}223}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/IndexAdvice.java+8-0
@@ -20,4 +20,12 @@ public class IndexAdvice {
20 String table;20 String table;
21 String column;21 String column;
22 String indexType;22 String indexType;
23+ 
24+ public void setIndexType(String indexType) {
25+ this.indexType = indexType;
26+ }
27+ 
28+ public void setIndextype(String indexType) {
29+ this.indexType = indexType;
30+ }
23}31}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/InstanceInfo.java+0-22
@@ -1,22 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model;
6- 
7-import java.util.List;
8- 
9-import lombok.Data;
10- 
11-@Data
12-public class InstanceInfo {
13- private String name;
14- 
15- private String type;
16- 
17- private String dbVersion;
18- 
19- private String remark;
20- 
21- private List<InstanceInfo> nodeList;
22-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/InstanceNodeInfo.java+0-30
@@ -1,30 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model;
6- 
7-import lombok.Data;
8- 
9-@Data
10-public class InstanceNodeInfo {
11- 
12- private String id;
13- 
14- private String instanceId;
15- 
16- private String serverId;
17- 
18- private String ip;
19- 
20- private int port;
21- 
22- private String dbUser;
23- 
24- private String dbUserPassword;
25- 
26- private String dbName;
27- 
28- private String dbType;
29- 
30-}
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/ParamQuery.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/ParamQuery.java+1-1
@@ -2,7 +2,7 @@
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4 4 
5-package com.nctigba.observability.instance.model.param;5+package com.nctigba.observability.instance.model;
6 6 
7import lombok.Data;7import lombok.Data;
8import lombok.experimental.Accessors;8import lombok.experimental.Accessors;
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/PartitionDataResp.java+0-18
@@ -1,18 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model;
6- 
7-import lombok.Data;
8- 
9-@Data
10-public class PartitionDataResp {
11- 
12- String partStrategy;
13- String partKey;
14- String relPages;
15- String relTuples;
16- String relallVisible;
17- String interval;
18-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/monitoring/MonitoringMetric.java+0-17
@@ -1,17 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model.monitoring;
6- 
7-import com.alibaba.fastjson.JSONArray;
8-import com.alibaba.fastjson.JSONObject;
9- 
10-import lombok.Data;
11- 
12-@Data
13-public class MonitoringMetric {
14- private JSONObject metric;
15- private JSONArray value;
16- private JSONArray values;
17-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/monitoring/MonitoringParam.java+0-34
@@ -1,34 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.model.monitoring;
6- 
7-import lombok.Data;
8- 
9-@Data
10-public class MonitoringParam {
11- private String id;
12- 
13- private String query;
14- 
15- private String start;
16- 
17- private String end;
18- 
19- private String step;
20- 
21- private String time;
22- 
23- private String legendName;
24- 
25- private String field;
26- 
27- private String filter;
28- 
29- private String order;
30- 
31- private String type;
32- 
33- private String monitoringType;
34-}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/AspService.java+35-0
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import com.nctigba.observability.instance.dto.asp.AnalysisDto;
8+import com.nctigba.observability.instance.dto.asp.AspCountReq;
9+ 
10+import java.util.List;
11+import java.util.Map;
12+ 
13+/**
14+ * AspService
15+ *
16+ * @author liupengfei
17+ * @since 2023/8/11
18+ */
19+public interface AspService {
20+ /**
21+ * count
22+ *
23+ * @param req AspCountReq
24+ * @return Map<String, List<Object>>
25+ */
26+ Map<String, List<Object>> count(AspCountReq req);
27+ 
28+ /**
29+ * analysis
30+ *
31+ * @param req AspCountReq
32+ * @return List<AnalysisDto>
33+ */
34+ List<AnalysisDto> analysis(AspCountReq req);
35+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterManager.java+54-74
@@ -4,20 +4,17 @@
4 4 
5package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
6 6 
7-import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;7+import java.sql.Connection;
8-import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;8+import java.sql.DriverManager;
9-import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;9+import java.sql.SQLException;
10-import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;10+import java.util.Collections;
11-import com.gitee.starblues.bootstrap.annotation.AutowiredType;11+import java.util.List;
12-import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;12+import java.util.Properties;
13-import com.nctigba.common.web.exception.InstanceException;13+ 
14-import com.nctigba.observability.instance.constants.CommonConstants;14+import javax.sql.DataSource;
15-import com.nctigba.observability.instance.model.InstanceNodeInfo;15+ 
16-import lombok.Data;
17-import lombok.EqualsAndHashCode;
18-import lombok.NoArgsConstructor;
19-import lombok.extern.slf4j.Slf4j;
20import org.apache.commons.lang3.StringUtils;16import org.apache.commons.lang3.StringUtils;
17+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
21import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;18import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
22import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;19import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
23import org.opengauss.admin.common.exception.CustomException;20import org.opengauss.admin.common.exception.CustomException;
@@ -29,21 +26,26 @@ import org.springframework.beans.factory.annotation.Autowired;
29import org.springframework.stereotype.Service;26import org.springframework.stereotype.Service;
30import org.springframework.util.CollectionUtils;27import org.springframework.util.CollectionUtils;
31 28 
32-import javax.sql.DataSource;29+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
33-import java.sql.Connection;30+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
34-import java.sql.DriverManager;31+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
35-import java.sql.SQLException;32+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
36-import java.util.Collections;33+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
37-import java.util.List;34+import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
38-import java.util.Properties;35+import com.nctigba.observability.instance.constants.CommonConstants;
36+ 
37+import lombok.Data;
38+import lombok.EqualsAndHashCode;
39+import lombok.NoArgsConstructor;
40+import lombok.extern.slf4j.Slf4j;
39 41 
40@Service42@Service
41@Slf4j43@Slf4j
42public class ClusterManager {44public class ClusterManager {
43 @Autowired45 @Autowired
44- private DataSource dataSource;46+ DataSource dataSource;
45 @Autowired47 @Autowired
46- private DefaultDataSourceCreator dataSourceCreator;48+ DefaultDataSourceCreator dataSourceCreator;
47 49 
48 @Autowired(required = false)50 @Autowired(required = false)
49 @AutowiredType(Type.MAIN_PLUGIN)51 @AutowiredType(Type.MAIN_PLUGIN)
@@ -55,6 +57,25 @@ public class ClusterManager {
55 @AutowiredType(Type.MAIN_PLUGIN)57 @AutowiredType(Type.MAIN_PLUGIN)
56 private IOpsClusterService opsClusterService;58 private IOpsClusterService opsClusterService;
57 59 
60+ public OpsClusterEntity getClusterByNodeId(String nodeId) {
61+ List<OpsClusterVO> opsClusterVOList = getAllOpsCluster();
62+ if (CollectionUtils.isEmpty(opsClusterVOList)) {
63+ throw new CustomException(CommonConstants.NODE_NOT_FOUND);
64+ }
65+ for (OpsClusterVO cluster : opsClusterVOList) {
66+ List<OpsClusterNodeVO> nodes = cluster.getClusterNodes();
67+ if (CollectionUtils.isEmpty(nodes)) {
68+ continue;
69+ }
70+ for (OpsClusterNodeVO clusterNode : nodes) {
71+ if (nodeId.equals(clusterNode.getNodeId())) {
72+ return opsClusterService.getById(cluster.getClusterId());
73+ }
74+ }
75+ }
76+ throw new CustomException(CommonConstants.NODE_NOT_FOUND);
77+ }
78+ 
58 public Connection getConnectionByClusterHost(String clusterId, String hostId) {79 public Connection getConnectionByClusterHost(String clusterId, String hostId) {
59 var clusterEntity = opsClusterService.getById(clusterId);80 var clusterEntity = opsClusterService.getById(clusterId);
60 var hostEntity = hostFacade.getById(hostId);81 var hostEntity = hostFacade.getById(hostId);
@@ -66,11 +87,20 @@ public class ClusterManager {
66 try {87 try {
67 return DriverManager.getConnection(sourceURL, info);88 return DriverManager.getConnection(sourceURL, info);
68 } catch (SQLException e) {89 } catch (SQLException e) {
69- e.printStackTrace();90+ throw new CustomException("connection fail", e);
70- throw new RuntimeException("connection fail");
71 }91 }
72 }92 }
73 93 
94+ /**
95+ * Set the current data source and manually clear it
96+ *
97+ * @see com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder#push(String)
98+ * @see com.nctigba.observability.instance.service.ClusterManager#pool()
99+ */
100+ public void setCurrentDatasource(String nodeId) {
101+ setCurrentDatasource(nodeId, null);
102+ }
103+ 
74 /**104 /**
75 * Set the current data source and manually clear it105 * Set the current data source and manually clear it
76 *106 *
@@ -126,26 +156,6 @@ public class ClusterManager {
126 BeanUtils.copyProperties(opsClusterNodeVO, this);156 BeanUtils.copyProperties(opsClusterNodeVO, this);
127 this.version = version;157 this.version = version;
128 }158 }
129- 
130- public Connection connection() throws SQLException {
131- var conn = DriverManager.getConnection(
132- CommonConstants.JDBC_OPENGAUSS + getPublicIp() + ":" + getDbPort() + "/" + getDbName(), getDbUser(),
133- getDbUserPassword());
134- try (var preparedStatement = conn.prepareStatement("select 1");
135- var rs = preparedStatement.executeQuery();) {
136- return conn;
137- } catch (Exception e) {
138- log.error("test connection fail:{}", e.getMessage());
139- throw e;
140- }
141- }
142- }
143- 
144- /**
145- * Directly obtain the connection of the specified node
146- */
147- public Connection getConnectionByNodeId(String nodeId) throws SQLException {
148- return getOpsNodeById(nodeId).connection();
149 }159 }
150 160 
151 /**161 /**
@@ -167,34 +177,4 @@ public class ClusterManager {
167 }177 }
168 throw new CustomException(CommonConstants.NODE_NOT_FOUND);178 throw new CustomException(CommonConstants.NODE_NOT_FOUND);
169 }179 }
170- 
171- /**
172- * get openGauss jdbc-connection by NodeInfo
173- *
174- * @param nodeInfo nodeInfo
175- * @return jdbc Connection
176- */
177- public Connection getConnectionByNodeInfo(InstanceNodeInfo nodeInfo) {
178- try {
179- Connection connection;
180- if (!(dataSource instanceof DynamicRoutingDataSource)) {
181- throw new InstanceException("dataSource is not type of DynamicRoutingDataSource");
182- }
183- var dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
184- if (dynamicRoutingDataSource.getDataSources().containsKey(nodeInfo.getId())) {
185- connection = dynamicRoutingDataSource.getDataSource(nodeInfo.getId()).getConnection();
186- } else {
187- DataSource newDataSource = dataSourceCreator.createDataSource(new DataSourceProperty()
188- .setDriverClassName("org.opengauss.Driver")
189- .setUrl(CommonConstants.JDBC_OPENGAUSS + nodeInfo.getIp() + ":" + nodeInfo.getPort() + "/"
190- + nodeInfo.getDbName())
191- .setUsername(nodeInfo.getDbUser()).setPassword(nodeInfo.getDbUserPassword()));
192- dynamicRoutingDataSource.addDataSource(nodeInfo.getId(), newDataSource);
193- connection = newDataSource.getConnection();
194- }
195- return connection;
196- } catch (SQLException e) {
197- throw new InstanceException(e.getMessage(), e);
198- }
199- }
200}180}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterOpsService.java+70-0
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import java.util.List;
8+import java.util.Map;
9+ 
10+import com.alibaba.fastjson.JSONObject;
11+import com.nctigba.observability.instance.dto.cluster.ClusterStateDto;
12+import com.nctigba.observability.instance.dto.cluster.ClustersDto;
13+import com.nctigba.observability.instance.dto.cluster.NodeRelationDto;
14+import com.nctigba.observability.instance.dto.cluster.SyncSituationDto;
15+ 
16+/**
17+ * ClusterOpsService
18+ *
19+ * @author liupengfei
20+ * @since 2023/8/11
21+ */
22+public interface ClusterOpsService {
23+ /**
24+ * listClusters
25+ *
26+ * @return ClustersDto
27+ */
28+ List<ClustersDto> listClusters();
29+ 
30+ /**
31+ * nodes
32+ *
33+ * @param clusterId String
34+ * @return List<JSONObject>
35+ */
36+ List<JSONObject> nodes(String clusterId);
37+ 
38+ /**
39+ * relation
40+ *
41+ * @param clusterId String
42+ * @return List<NodeRelationDto>
43+ */
44+ List<NodeRelationDto> relation(String clusterId);
45+ 
46+ /**
47+ * allClusterState
48+ *
49+ * @return ClusterStateDto
50+ */
51+ List<ClusterStateDto> allClusterState();
52+ 
53+ /**
54+ * allStandbyNodes
55+ *
56+ * @return SyncSituationDto
57+ */
58+ List<SyncSituationDto> allStandbyNodes();
59+ 
60+ /**
61+ * clusterMetrics
62+ *
63+ * @param clusterId String
64+ * @param start Long
65+ * @param end Long
66+ * @param step Integer
67+ * @return Map<String, Object>
68+ */
69+ Map<String, Object> clusterMetrics(String clusterId, Long start, Long end, Integer step);
70+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ExporterInstallService.java+4-3
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
5 6 
6import java.io.File;7import java.io.File;
@@ -16,6 +17,7 @@ import java.util.LinkedHashSet;
16import java.util.Map;17import java.util.Map;
17 18 
18import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;19import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
20+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
19import org.opengauss.admin.common.core.domain.model.ops.WsSession;21import org.opengauss.admin.common.core.domain.model.ops.WsSession;
20import org.springframework.beans.factory.annotation.Autowired;22import org.springframework.beans.factory.annotation.Autowired;
21import org.springframework.core.io.ResourceLoader;23import org.springframework.core.io.ResourceLoader;
@@ -26,7 +28,6 @@ import com.nctigba.observability.instance.constants.CommonConstants;
26import com.nctigba.observability.instance.entity.NctigbaEnv;28import com.nctigba.observability.instance.entity.NctigbaEnv;
27import com.nctigba.observability.instance.entity.NctigbaEnv.envType;29import com.nctigba.observability.instance.entity.NctigbaEnv.envType;
28import com.nctigba.observability.instance.service.AbstractInstaller.Step.status;30import com.nctigba.observability.instance.service.AbstractInstaller.Step.status;
29-import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
30import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig;31import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig;
31import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig.job;32import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig.job;
32import com.nctigba.observability.instance.util.Download;33import com.nctigba.observability.instance.util.Download;
@@ -236,7 +237,7 @@ public class ExporterInstallService extends AbstractInstaller {
236 }237 }
237 }238 }
238 239 
239- private void uninstallExporter(WsSession wsSession, ArrayList<Step> steps, int curr, OpsClusterNodeVOSub node) {240+ private void uninstallExporter(WsSession wsSession, ArrayList<Step> steps, int curr, OpsClusterNodeVO node) {
240 var exporterList = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery()241 var exporterList = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery()
241 .eq(NctigbaEnv::getType, envType.EXPORTER).eq(NctigbaEnv::getHostid, node.getHostId()));242 .eq(NctigbaEnv::getType, envType.EXPORTER).eq(NctigbaEnv::getHostid, node.getHostId()));
242 var exporterenv = exporterList.isEmpty() ? null : exporterList.get(0);243 var exporterenv = exporterList.isEmpty() ? null : exporterList.get(0);
@@ -285,7 +286,7 @@ public class ExporterInstallService extends AbstractInstaller {
285 }286 }
286 287 
287 private void uninstallNodeAndGaussExporter(WsSession wsSession, ArrayList<Step> steps, int curr,288 private void uninstallNodeAndGaussExporter(WsSession wsSession, ArrayList<Step> steps, int curr,
288- OpsClusterNodeVOSub node) throws IOException {289+ OpsClusterNodeVO node) throws IOException {
289 var nodeenvList = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery()290 var nodeenvList = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery()
290 .eq(NctigbaEnv::getType, envType.NODE_EXPORTER).eq(NctigbaEnv::getHostid, node.getHostId()));291 .eq(NctigbaEnv::getType, envType.NODE_EXPORTER).eq(NctigbaEnv::getHostid, node.getHostId()));
291 var nodeenv = nodeenvList.isEmpty() ? null : nodeenvList.get(0);292 var nodeenv = nodeenvList.isEmpty() ? null : nodeenvList.get(0);
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/MetricsService.java+10-7
@@ -23,7 +23,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
23import com.gitee.starblues.bootstrap.annotation.AutowiredType;23import com.gitee.starblues.bootstrap.annotation.AutowiredType;
24import com.nctigba.observability.instance.constants.MetricsLine;24import com.nctigba.observability.instance.constants.MetricsLine;
25import com.nctigba.observability.instance.constants.MetricsValue;25import com.nctigba.observability.instance.constants.MetricsValue;
26-import com.nctigba.observability.instance.constants.MonitoringConstants;
27import com.nctigba.observability.instance.entity.NctigbaEnv;26import com.nctigba.observability.instance.entity.NctigbaEnv;
28import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;27import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
29import com.nctigba.observability.instance.service.MetricsService.PrometheusResult.PromData.MonitoringMetric;28import com.nctigba.observability.instance.service.MetricsService.PrometheusResult.PromData.MonitoringMetric;
@@ -39,6 +38,11 @@ import lombok.extern.log4j.Log4j2;
39@Service38@Service
40@Log4j239@Log4j2
41public class MetricsService {40public class MetricsService {
41+ // prometheus query ?query={query}
42+ private static final String PROMETHEUS_QUERY_POINT = "/api/v1/query";
43+ 
44+ // prometheus range query ?query={query}&start={start}&end={end}&step={step}
45+ private static final String PROMETHEUS_QUERY_RANGE = "/api/v1/query_range";
42 private static final String TIME = "time";46 private static final String TIME = "time";
43 private static final Map<String, String> PROM = new HashMap<>();47 private static final Map<String, String> PROM = new HashMap<>();
44 private static final String DEFAULT = "DEFAULT";48 private static final String DEFAULT = "DEFAULT";
@@ -85,8 +89,7 @@ public class MetricsService {
85 throw new NullPointerException("query null");89 throw new NullPointerException("query null");
86 }90 }
87 log.info("promQL:{}, time:{}", query, time);91 log.info("promQL:{}, time:{}", query, time);
88- var prometheusResult = query(MonitoringConstants.PROMETHEUS_QUERY_POINT, query,92+ var prometheusResult = query(PROMETHEUS_QUERY_POINT, query, Map.of("query", query, "time", time));
89- Map.of("query", query, "time", time));
90 return prometheusResult.getData().getResult();93 return prometheusResult.getData().getResult();
91 }94 }
92 95 
@@ -104,12 +107,12 @@ public class MetricsService {
104 return Collections.emptyList();107 return Collections.emptyList();
105 }108 }
106 log.info("promQL:{}, start:{}, end:{}, step:{}", query, start, end, step);109 log.info("promQL:{}, start:{}, end:{}, step:{}", query, start, end, step);
107- var prometheusResult = query(MonitoringConstants.PROMETHEUS_QUERY_RANGE, query,110+ var prometheusResult = query(PROMETHEUS_QUERY_RANGE, query,
108 Map.of("query", query, "start", start, "end", end, "step", step));111 Map.of("query", query, "start", start, "end", end, "step", step));
109 return prometheusResult.getData().getResult();112 return prometheusResult.getData().getResult();
110 }113 }
111 114 
112- public HashMap<String, Object> listBatch(Enum<?>[] metricsArr, String nodeId, Long start, Long end, Integer step) {115+ public Map<String, Object> listBatch(Enum<?>[] metricsArr, String nodeId, Long start, Long end, Integer step) {
113 var result = new HashMap<String, Object>();116 var result = new HashMap<String, Object>();
114 var node = clusterManager.getOpsNodeById(nodeId);117 var node = clusterManager.getOpsNodeById(nodeId);
115 List<Long> timeline = new ArrayList<>();118 List<Long> timeline = new ArrayList<>();
@@ -164,7 +167,7 @@ public class MetricsService {
164 var map = new HashMap<String, Object>();167 var map = new HashMap<String, Object>();
165 for (var monitoringMetric : metric) {168 for (var monitoringMetric : metric) {
166 if (template == null)169 if (template == null)
167- throw new NullPointerException(promQl);170+ return metric.get(0).getValue().get(1);
168 String key = StrUtil.format(template, monitoringMetric.getMetric());171 String key = StrUtil.format(template, monitoringMetric.getMetric());
169 map.put(key, monitoringMetric.getValue().get(1));172 map.put(key, monitoringMetric.getValue().get(1));
170 }173 }
@@ -181,7 +184,7 @@ public class MetricsService {
181 var map = new HashMap<String, Object>();184 var map = new HashMap<String, Object>();
182 for (var monitoringMetric : metric) {185 for (var monitoringMetric : metric) {
183 if (template == null) {186 if (template == null) {
184- throw new NullPointerException(promQl);187+ return ListUtil.collect(metric.get(0).getValues(), timeline);
185 }188 }
186 String key = StrUtil.format(template, monitoringMetric.getMetric());189 String key = StrUtil.format(template, monitoringMetric.getMetric());
187 var lineNumber = ListUtil.collect(monitoringMetric.getValues(), timeline);190 var lineNumber = ListUtil.collect(monitoringMetric.getValues(), timeline);
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/MonitoringService.java+0-19
@@ -1,19 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.service;
5- 
6-import java.util.List;
7-import java.util.Map;
8- 
9-import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;
10-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
11- 
12-public interface MonitoringService {
13- 
14- Map<String, Object> getPointMonitoringData(MonitoringParam monitoringParam);
15- 
16- List<MonitoringMetric> getCurrentMonitoringData(MonitoringParam monitoringParam);
17- 
18- List<Object> getRangeMonitoringData(MonitoringParam monitoringParam);
19-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/OpsWdrService.java+44-8
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
5 6 
6import java.text.MessageFormat;7import java.text.MessageFormat;
@@ -10,10 +11,10 @@ import java.util.HashMap;
10import java.util.List;11import java.util.List;
11import java.util.Map;12import java.util.Map;
12import java.util.Objects;13import java.util.Objects;
14+import java.util.stream.Collectors;
13 15 
14import javax.servlet.http.HttpServletResponse;16import javax.servlet.http.HttpServletResponse;
15 17 
16-import com.nctigba.observability.instance.constants.CommonConstants;
17import org.apache.commons.lang3.StringUtils;18import org.apache.commons.lang3.StringUtils;
18import org.opengauss.admin.common.constant.ops.SshCommandConstants;19import org.opengauss.admin.common.constant.ops.SshCommandConstants;
19import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;20import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
@@ -39,16 +40,19 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
39import com.gitee.starblues.bootstrap.annotation.AutowiredType;40import com.gitee.starblues.bootstrap.annotation.AutowiredType;
40import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;41import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
41import com.jcraft.jsch.Session;42import com.jcraft.jsch.Session;
43+import com.nctigba.observability.instance.constants.CommonConstants;
42import com.nctigba.observability.instance.entity.OpsWdrEntity;44import com.nctigba.observability.instance.entity.OpsWdrEntity;
43import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;45import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
44import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrTypeEnum;46import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrTypeEnum;
47+import com.nctigba.observability.instance.entity.Snapshot;
45import com.nctigba.observability.instance.mapper.OpsWdrMapper;48import com.nctigba.observability.instance.mapper.OpsWdrMapper;
49+import com.nctigba.observability.instance.mapper.SnapshotMapper;
46import com.nctigba.observability.instance.model.WdrGeneratorBody;50import com.nctigba.observability.instance.model.WdrGeneratorBody;
47-import com.nctigba.observability.instance.model.WdrSnapshotVO;
48import com.nctigba.observability.instance.service.provider.ClusterOpsProviderManager;51import com.nctigba.observability.instance.service.provider.ClusterOpsProviderManager;
49import com.nctigba.observability.instance.util.JschUtil;52import com.nctigba.observability.instance.util.JschUtil;
50 53 
51import cn.hutool.core.collection.CollUtil;54import cn.hutool.core.collection.CollUtil;
55+import cn.hutool.core.date.DateUtil;
52import cn.hutool.core.util.StrUtil;56import cn.hutool.core.util.StrUtil;
53import lombok.extern.slf4j.Slf4j;57import lombok.extern.slf4j.Slf4j;
54 58 
@@ -80,8 +84,13 @@ public class OpsWdrService extends ServiceImpl<OpsWdrMapper, OpsWdrEntity> {
80 @Autowired84 @Autowired
81 @AutowiredType(Type.PLUGIN_MAIN)85 @AutowiredType(Type.PLUGIN_MAIN)
82 protected EncryptionUtils encryptionUtils;86 protected EncryptionUtils encryptionUtils;
87+ @Autowired
88+ private SnapshotMapper snapshotMapper;
83 89 
84- @SuppressWarnings({ "unchecked", "rawtypes" })90+ @SuppressWarnings({
91+ "unchecked",
92+ "rawtypes"
93+ })
85 public Page<OpsWdrEntity> listWdr(Page page, String clusterId, WdrScopeEnum wdrScope, WdrTypeEnum wdrType,94 public Page<OpsWdrEntity> listWdr(Page page, String clusterId, WdrScopeEnum wdrScope, WdrTypeEnum wdrType,
86 String hostId, Date start, Date end) {95 String hostId, Date start, Date end) {
87 var wrapper = Wrappers.lambdaQuery(OpsWdrEntity.class).eq(OpsWdrEntity::getClusterId, clusterId)96 var wrapper = Wrappers.lambdaQuery(OpsWdrEntity.class).eq(OpsWdrEntity::getClusterId, clusterId)
@@ -122,7 +131,34 @@ public class OpsWdrService extends ServiceImpl<OpsWdrMapper, OpsWdrEntity> {
122 }131 }
123 }132 }
124 133 
125- @SuppressWarnings({ "rawtypes", "unchecked" })134+ /**
135+ * findSnapshot
136+ *
137+ * @param id id
138+ * @param start start
139+ * @param end end
140+ * @return Map
141+ */
142+ public Map<String, Object> findSnapshot(String id, Date start, Date end) {
143+ Map<String, Object> map = new HashMap<>();
144+ Long startSnapshot = snapshotMapper.getIdByTime(id, Snapshot::getStartTs, DateUtil.offsetHour(start, -1),
145+ start);
146+ map.put("start", startSnapshot == 0 ? null : startSnapshot);
147+ Long endSnapshot = snapshotMapper.getIdByTime(id, Snapshot::getEndTs, end, DateUtil.offsetHour(end, 1));
148+ map.put("end", endSnapshot == 0 ? null : endSnapshot);
149+ if (startSnapshot == null || endSnapshot == null) {
150+ return map;
151+ }
152+ var listWdr = getBaseMapper().selectList(Wrappers.<OpsWdrEntity>lambdaQuery()
153+ .eq(OpsWdrEntity::getStartSnapshotId, startSnapshot).eq(OpsWdrEntity::getEndSnapshotId, endSnapshot));
154+ map.put("wdrId", listWdr.stream().map(OpsWdrEntity::getWdrId).collect(Collectors.toList()));
155+ return map;
156+ }
157+ 
158+ @SuppressWarnings({
159+ "rawtypes",
160+ "unchecked"
161+ })
126 public Page listSnapshot(Page page, String clusterId, String hostId) {162 public Page listSnapshot(Page page, String clusterId, String hostId) {
127 var connection = clusterManager.getConnectionByClusterHost(clusterId, hostId);163 var connection = clusterManager.getConnectionByClusterHost(clusterId, hostId);
128 String sqlCount = "select count(*) from snapshot.snapshot";164 String sqlCount = "select count(*) from snapshot.snapshot";
@@ -140,10 +176,10 @@ public class OpsWdrService extends ServiceImpl<OpsWdrMapper, OpsWdrEntity> {
140 var res = new ArrayList<>();176 var res = new ArrayList<>();
141 try (var statement = connection.createStatement(); var rs = statement.executeQuery(sql);) {177 try (var statement = connection.createStatement(); var rs = statement.executeQuery(sql);) {
142 while (rs.next()) {178 while (rs.next()) {
143- var vo = new WdrSnapshotVO();179+ var vo = new HashMap<>();
144- vo.setSnapshotId(rs.getInt("snapshot_id"));180+ vo.put("snapshotId", rs.getInt("snapshot_id"));
145- vo.setStartTs(rs.getDate("start_ts"));181+ vo.put("startTs", DateUtil.formatDateTime(rs.getDate("start_ts")));
146- vo.setEndTs(rs.getDate("end_ts"));182+ vo.put("endTs", DateUtil.formatDateTime(rs.getDate("end_ts")));
147 res.add(vo);183 res.add(vo);
148 }184 }
149 } catch (Exception e) {185 } catch (Exception e) {
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ParamInfoService.java+22-21
@@ -1,24 +1,27 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
5 6 
6import java.util.ArrayList;7import java.util.ArrayList;
7import java.util.List;8import java.util.List;
9+import java.util.Map;
8 10 
9import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;11import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
10import org.springframework.beans.factory.annotation.Autowired;12import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.stereotype.Service;13import org.springframework.stereotype.Service;
12 14 
13import com.gitee.starblues.bootstrap.annotation.AutowiredType;15import com.gitee.starblues.bootstrap.annotation.AutowiredType;
14-import com.nctigba.common.web.exception.InstanceException;
15import com.nctigba.observability.instance.constants.CommonConstants;16import com.nctigba.observability.instance.constants.CommonConstants;
16import com.nctigba.observability.instance.dto.param.ParamInfoDTO;17import com.nctigba.observability.instance.dto.param.ParamInfoDTO;
17-import com.nctigba.observability.instance.entity.ParamInfo.type;18+import com.nctigba.observability.instance.entity.ParamInfo.ParamType;
18import com.nctigba.observability.instance.entity.ParamValueInfo;19import com.nctigba.observability.instance.entity.ParamValueInfo;
20+import com.nctigba.observability.instance.exception.InstanceException;
21+import com.nctigba.observability.instance.mapper.DbConfigMapper;
19import com.nctigba.observability.instance.mapper.ParamInfoMapper;22import com.nctigba.observability.instance.mapper.ParamInfoMapper;
20import com.nctigba.observability.instance.mapper.ParamValueInfoMapper;23import com.nctigba.observability.instance.mapper.ParamValueInfoMapper;
21-import com.nctigba.observability.instance.model.param.ParamQuery;24+import com.nctigba.observability.instance.model.ParamQuery;
22import com.nctigba.observability.instance.util.MessageSourceUtil;25import com.nctigba.observability.instance.util.MessageSourceUtil;
23import com.nctigba.observability.instance.util.SshSession;26import com.nctigba.observability.instance.util.SshSession;
24 27 
@@ -29,12 +32,14 @@ import lombok.extern.slf4j.Slf4j;
29@Service32@Service
30public class ParamInfoService {33public class ParamInfoService {
31 @Autowired34 @Autowired
32- private ClusterManager opsFacade;35+ private ClusterManager clusterManager;
33 @Autowired36 @Autowired
34 @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)37 @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
35- protected EncryptionUtils encryptionUtils;38+ private EncryptionUtils encryptionUtils;
36 @Autowired39 @Autowired
37- protected ParamValueInfoMapper paramValueInfoMapper;40+ private ParamValueInfoMapper paramValueInfoMapper;
41+ @Autowired
42+ private DbConfigMapper dbConfigMapper;
38 43 
39 public List<ParamInfoDTO> getParamInfo(ParamQuery paramQuery) {44 public List<ParamInfoDTO> getParamInfo(ParamQuery paramQuery) {
40 if ("1".equals(paramQuery.getIsRefresh())) {45 if ("1".equals(paramQuery.getIsRefresh())) {
@@ -48,33 +53,29 @@ public class ParamInfoService {
48 return paramValueInfoMapper.query(paramQuery.getNodeId());53 return paramValueInfoMapper.query(paramQuery.getNodeId());
49 }54 }
50 55 
51- private static final String DBSETTINGS = "select name,setting from pg_settings";
52- 
53 private void updateDatabaseParamInfo(ParamQuery paramQuery) {56 private void updateDatabaseParamInfo(ParamQuery paramQuery) {
54 try {57 try {
55- var conn = opsFacade.getConnectionByNodeId(paramQuery.getNodeId());58+ clusterManager.setCurrentDatasource(paramQuery.getNodeId(), null);
56- var stmt = conn.createStatement();
57- var rs = stmt.executeQuery(DBSETTINGS);
58 var list = new ArrayList<ParamValueInfo>();59 var list = new ArrayList<ParamValueInfo>();
59- while (rs.next()) {60+ for (Map<String, String> setting : dbConfigMapper.settings()) {
60- String name = rs.getString(1);61+ var paramInfo = ParamInfoMapper.getParamInfo(ParamType.DB, setting.get("name"));
61- String value = rs.getString(2);
62- var paramInfo = ParamInfoMapper.getParamInfo(type.DB, name);
63 if (paramInfo == null)62 if (paramInfo == null)
64 continue;63 continue;
65- list.add(new ParamValueInfo(paramInfo.getId(), paramQuery.getNodeId(), value));64+ list.add(new ParamValueInfo(paramInfo.getId(), paramQuery.getNodeId(), setting.get("setting")));
66 }65 }
67- update(list, type.DB);66+ update(list, ParamType.DB);
68 } catch (Exception e) {67 } catch (Exception e) {
69 log.info(e.getMessage(), e);68 log.info(e.getMessage(), e);
70 throw new InstanceException(MessageSourceUtil.get("connect.database.tip"), e);69 throw new InstanceException(MessageSourceUtil.get("connect.database.tip"), e);
70+ } finally {
71+ clusterManager.pool();
71 }72 }
72 73 
73 }74 }
74 75 
75 private void updateOsParamInfo(ParamQuery paramQuery) {76 private void updateOsParamInfo(ParamQuery paramQuery) {
76 try {77 try {
77- var node = opsFacade.getOpsNodeById(paramQuery.getNodeId());78+ var node = clusterManager.getOpsNodeById(paramQuery.getNodeId());
78 if (node == null)79 if (node == null)
79 throw new InstanceException(MessageSourceUtil.get("node.tip"));80 throw new InstanceException(MessageSourceUtil.get("node.tip"));
80 var ssh = SshSession.connect(node.getPublicIp(), node.getHostPort(), "root",81 var ssh = SshSession.connect(node.getPublicIp(), node.getHostPort(), "root",
@@ -84,19 +85,19 @@ public class ParamInfoService {
84 for (int n = 0; n < values.length; n++) {85 for (int n = 0; n < values.length; n++) {
85 String name = values[n].substring(0, values[n].lastIndexOf(CommonConstants.EQUAL_SYMBOL)).trim();86 String name = values[n].substring(0, values[n].lastIndexOf(CommonConstants.EQUAL_SYMBOL)).trim();
86 String paramData = values[n].substring(values[n].indexOf(CommonConstants.EQUAL_SYMBOL) + 1).trim();87 String paramData = values[n].substring(values[n].indexOf(CommonConstants.EQUAL_SYMBOL) + 1).trim();
87- var paramInfo = ParamInfoMapper.getParamInfo(type.OS, name);88+ var paramInfo = ParamInfoMapper.getParamInfo(ParamType.OS, name);
88 if (paramInfo == null)89 if (paramInfo == null)
89 continue;90 continue;
90 list.add(new ParamValueInfo(paramInfo.getId(), paramQuery.getNodeId(), paramData));91 list.add(new ParamValueInfo(paramInfo.getId(), paramQuery.getNodeId(), paramData));
91 }92 }
92- update(list, type.OS);93+ update(list, ParamType.OS);
93 } catch (Exception e) {94 } catch (Exception e) {
94 log.info(e.getMessage(), e);95 log.info(e.getMessage(), e);
95 throw new InstanceException(MessageSourceUtil.get("password.tip"), e);96 throw new InstanceException(MessageSourceUtil.get("password.tip"), e);
96 }97 }
97 }98 }
98 99 
99- private void update(ArrayList<ParamValueInfo> list, type t) {100+ private void update(ArrayList<ParamValueInfo> list, ParamType t) {
100 var ids = ParamInfoMapper.getIds(t);101 var ids = ParamInfoMapper.getIds(t);
101 ParamValueInfoMapper.delBySids(ids);102 ParamValueInfoMapper.delBySids(ids);
102 ParamValueInfoMapper.insertBatch(list);103 ParamValueInfoMapper.insertBatch(list);
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/SessionService.java+114-10
@@ -1,28 +1,132 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
5 6 
7+import java.util.ArrayList;
8+import java.util.HashMap;
6import java.util.List;9import java.util.List;
7import java.util.Map;10import java.util.Map;
11+import java.util.Set;
12+import java.util.stream.Collectors;
13+ 
14+import org.apache.commons.lang3.StringUtils;
15+import org.springframework.stereotype.Service;
8 16 
9import com.alibaba.fastjson.JSONObject;17import com.alibaba.fastjson.JSONObject;
18+import com.nctigba.observability.instance.aop.Ds;
10import com.nctigba.observability.instance.dto.session.DetailStatisticDto;19import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
20+import com.nctigba.observability.instance.exception.InstanceException;
21+import com.nctigba.observability.instance.mapper.SessionMapper;
11 22 
12-public interface SessionService {23+import lombok.RequiredArgsConstructor;
13- JSONObject detailGeneral(String id, String sessionid);
14 24 
15- List<DetailStatisticDto> detailStatistic(String id, String sessionid);25+@Service
26+@RequiredArgsConstructor
27+public class SessionService {
28+ private final SessionMapper sessionMapper;
16 29 
17- List<JSONObject> detailWaiting(String id, String sessionid);30+ @Ds
31+ public Map<String, Object> detailGeneral(String id, String sessionid) {
32+ Map<String, Object> res = new JSONObject();
33+ List<Map<String, Object>> generalMesList = sessionMapper.generalMesList(sessionid);
34+ if (generalMesList.size() == 0) {
35+ throw new InstanceException("session.detail.general.message");
36+ }
37+ res.putAll(generalMesList.get(0));
38+ if (sessionMapper.sessionIsWaiting(sessionid) != 0) {
39+ return res;
40+ }
41+ List<Map<String, Object>> blockList = sessionMapper.blockList(sessionid);
42+ if (blockList.size() != 0) {
43+ res.putAll(blockList.get(0));
44+ }
45+ return res;
46+ }
18 47 
19- List<JSONObject> detailBlockTree(String id, String sessionid);48+ @Ds
49+ public List<DetailStatisticDto> detailStatistic(String id, String sessionid) {
50+ ArrayList<DetailStatisticDto> list = new ArrayList<>();
51+ list.addAll(
52+ sessionMapper.statistic(sessionid).stream()
53+ .map(ob -> new DetailStatisticDto(DetailStatisticDto.Type.STATUS,
54+ ob.get("stat_name").toString(), ob.get("value").toString()))
55+ .collect(Collectors.toList()));
56+ list.addAll(
57+ sessionMapper.runtime(sessionid).stream()
58+ .map(ob -> new DetailStatisticDto(DetailStatisticDto.Type.RUNTIME,
59+ ob.get("statname").toString(), ob.get("value").toString()))
60+ .collect(Collectors.toList()));
61+ return list;
62+ }
20 63 
21- JSONObject simpleStatistic(String id);64+ @Ds
65+ public List<Map<String, Object>> detailWaiting(String id, String sessionid) {
66+ return sessionMapper.detailWaiting(sessionid);
67+ }
22 68 
23- List<JSONObject> longTxc(String id);69+ @Ds
70+ public List<Map<String, Object>> detailBlockTree(String id, String sessionid) {
71+ List<Map<String, Object>> queryList = sessionMapper.blockTree();
72+ if (StringUtils.isNotEmpty(sessionid)) {
73+ Set<String> treeIdSet = queryList.stream().filter(obj -> obj.get("pathid").toString().contains(sessionid))
74+ .map(obj -> obj.get("tree_id").toString()).collect(Collectors.toSet());
75+ queryList = queryList.stream().filter(obj -> treeIdSet.contains(obj.get("tree_id").toString()))
76+ .collect(Collectors.toList());
77+ }
78+ return toTreeData(queryList);
79+ }
24 80 
25- Map<String, List<JSONObject>> blockAndLongTxc(String id);81+ @Ds
82+ public Map<String, Object> simpleStatistic(String id) {
83+ return sessionMapper.simpleStatistic();
84+ }
26 85 
27- Map<String, Object> detail(String id, String sessionid);86+ @Ds
28-}87+ public List<Map<String, Object>> longTxc(String id) {
88+ return sessionMapper.longTxc();
89+ }
90+ 
91+ @Ds
92+ public HashMap<String, List<Map<String, Object>>> blockAndLongTxc(String id) {
93+ HashMap<String, List<Map<String, Object>>> res = new HashMap<>();
94+ res.put("blockTree", detailBlockTree(id, null));
95+ res.put("longTxc", longTxc(id));
96+ return res;
97+ }
98+ 
99+ @Ds
100+ public Map<String, Object> detail(String id, String sessionid) {
101+ Map<String, Object> resMap = new HashMap<>();
102+ resMap.put("general", detailGeneral(id, sessionid));
103+ resMap.put("statistic", detailStatistic(id, sessionid));
104+ resMap.put("blockTree", detailBlockTree(id, sessionid));
105+ resMap.put("waiting", detailWaiting(id, sessionid));
106+ return resMap;
107+ }
108+ 
109+ @SuppressWarnings({
110+ "unchecked",
111+ "rawtypes"
112+ })
113+ private List<Map<String, Object>> toTreeData(List<Map<String, Object>> list) {
114+ // Classify by parentid
115+ Map<Long, Map<String, Object>> map = new HashMap<>();
116+ List<Map<String, Object>> result = new ArrayList<>();
117+ for (Map<String, Object> object : list) {
118+ object.put("children", new ArrayList<>());
119+ map.put(Long.valueOf(object.get("id").toString()), object);
120+ Long parentId = Long.valueOf(object.get("parentid").toString());
121+ if (parentId == 0) {
122+ result.add(object);
123+ continue;
124+ }
125+ Object children = map.get(parentId).get("children");
126+ if (children instanceof List) {
127+ ((List) children).add(object);
128+ }
129+ }
130+ return result;
131+ }
132+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/TopSQLService.java+185-125
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.service;5package com.nctigba.observability.instance.service;
5 6 
6import java.time.Duration;7import java.time.Duration;
@@ -8,168 +9,227 @@ import java.time.LocalDateTime;
8import java.time.format.DateTimeFormatter;9import java.time.format.DateTimeFormatter;
9import java.time.format.DateTimeParseException;10import java.time.format.DateTimeParseException;
10import java.util.ArrayList;11import java.util.ArrayList;
11-import java.util.Collections;12+import java.util.HashMap;
13+import java.util.LinkedList;
12import java.util.List;14import java.util.List;
13import java.util.Map;15import java.util.Map;
16+import java.util.Set;
14 17 
15-import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;18+import org.apache.commons.lang3.StringUtils;
19+import org.opengauss.admin.common.exception.CustomException;
16import org.springframework.stereotype.Service;20import org.springframework.stereotype.Service;
17 21 
18-import com.alibaba.fastjson.JSONObject;22+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
19-import com.nctigba.observability.instance.constants.DatabaseType;23+import com.nctigba.observability.instance.aop.Ds;
20-import com.nctigba.observability.instance.dto.topsql.TopSQLInfoReq;
21import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;24import com.nctigba.observability.instance.dto.topsql.TopSQLListReq;
22-import com.nctigba.observability.instance.dto.topsql.TopSQLNowReq;25+import com.nctigba.observability.instance.entity.PgSettings;
23-import com.nctigba.observability.instance.factory.TopSQLHandlerFactory;26+import com.nctigba.observability.instance.mapper.PgSettingMapper;
24-import com.nctigba.observability.instance.handler.topsql.TopSQLHandler;
25import com.nctigba.observability.instance.mapper.TopSqlMapper;27import com.nctigba.observability.instance.mapper.TopSqlMapper;
26-import com.nctigba.observability.instance.model.InstanceNodeInfo;28+import com.nctigba.observability.instance.model.ExecutionPlan;
27-import com.nctigba.observability.instance.service.TopSQLService.waitEvent.event;29+import com.nctigba.observability.instance.model.IndexAdvice;
30+import com.nctigba.observability.instance.service.TopSQLService.WaitEvent.Event;
28 31 
32+import cn.hutool.core.collection.CollUtil;
29import cn.hutool.core.util.StrUtil;33import cn.hutool.core.util.StrUtil;
30import lombok.Data;34import lombok.Data;
31import lombok.Generated;35import lombok.Generated;
32import lombok.RequiredArgsConstructor;36import lombok.RequiredArgsConstructor;
33-import lombok.extern.slf4j.Slf4j;
34 37 
35/**38/**
36 * <p>39 * <p>
37 * TopSQL services40 * TopSQL services
38 * </p>41 * </p>
39 *42 *
40- * @author zhanggr@vastdata.com.cn
41 * @since 2022/9/15 15:4643 * @since 2022/9/15 15:46
42 */44 */
43@Service45@Service
44@RequiredArgsConstructor46@RequiredArgsConstructor
45-@Slf4j
46public class TopSQLService {47public class TopSQLService {
47- private final TopSQLHandlerFactory topSQLHandlerFactory;48+ private final PgSettingMapper pgSettingMapper;
48- private final ClusterManager clusterManager;
49 private final TopSqlMapper topSqlMapper;49 private final TopSqlMapper topSqlMapper;
50 50 
51- public boolean testConnection(String nodeId) {51+ /**
52- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());52+ * fetch TopSQL list from database
53- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(nodeId);53+ *
54- try {54+ * @param topSQLListReq instance node id
55- return handler.getConnection(instanceNodeInfo) != null;55+ * @return TopSQL list
56- } catch (Exception e) {56+ */
57- log.error("test connection exception: {}", e.getMessage());57+ @Ds("id")
58- return false;58+ public List<Map<String, Object>> topSQLList(TopSQLListReq topSQLListReq) {
59- }59+ topSqlListPreCheck();
60- }60+ return topSqlMapper.historyTopsqlList(topSQLListReq);
61- 
62- public List<JSONObject> getTopSQLList(TopSQLListReq topSQLListReq) {
63- // get handler
64- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
65- // query node info
66- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLListReq.getId());
67- // query TopSQL list
68- return handler.getTopSQLList(instanceNodeInfo, topSQLListReq);
69- }
70- 
71- public List<JSONObject> getTopSQLNow(TopSQLNowReq topSQLNowReq) {
72- // get handler
73- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
74- // query node info
75- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLNowReq.getId());
76- // query TopSQL list
77- return handler.getTopSQLNow(instanceNodeInfo, topSQLNowReq);
78- }
79- 
80- public JSONObject getStatisticalInfo(TopSQLInfoReq topSQLDetailReq) {
81- // get handler
82- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
83- // query node info
84- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLDetailReq.getId());
85- // query TopSQL statistical information
86- return handler.getStatisticalInfo(instanceNodeInfo, topSQLDetailReq.getSqlId());
87- }
88- 
89- public JSONObject getExecutionPlan(TopSQLInfoReq topSQLPlanReq, String type) {
90- // get handler
91- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
92- // query node info
93- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLPlanReq.getId());
94- // query TopSQL statistical information
95- return handler.getExecutionPlan(instanceNodeInfo, topSQLPlanReq.getSqlId(), type);
96- }
97- 
98- public List<JSONObject> getPartitionList(TopSQLInfoReq topSQLPartitionReq) {
99- // get handler
100- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
101- // query node info
102- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLPartitionReq.getId());
103- // query TopSQL statistical information
104- return handler.getPartitionList(instanceNodeInfo, topSQLPartitionReq.getSqlId());
105- }
106- 
107- public List<String> getIndexAdvice(TopSQLInfoReq topSQLIndexReq) {
108- // get handler
109- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
110- // query node info
111- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLIndexReq.getId());
112- // query TopSQL statistical information
113- return handler.getIndexAdvice(instanceNodeInfo, topSQLIndexReq.getSqlId());
114- }
115- 
116- public JSONObject getObjectInfo(TopSQLInfoReq topSQLObjectReq) {
117- // get handler
118- TopSQLHandler handler = topSQLHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType());
119- // query node info
120- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(topSQLObjectReq.getId());
121- // query TopSQL statistical information
122- return handler.getObjectInfo(instanceNodeInfo, topSQLObjectReq.getSqlId());
123 }61 }
124 62 
125 /**63 /**
126- * Query instance node information64+ * fetch TopSQL now list from database
127 *65 *
128- * @param nodeId instance node id66+ * @param id instance node id
129- * @return Instance node information67+ * @return TopSQL list
130 */68 */
131- public InstanceNodeInfo queryNodeInfo(String nodeId) {69+ @Ds
132- OpsClusterNodeVO opsClusterNode = clusterManager.getOpsNodeById(nodeId);70+ public List<Map<String, Object>> topSQLNow(String id) {
133- InstanceNodeInfo instanceNodeInfo = new InstanceNodeInfo();71+ return topSqlMapper.currentTopsqlList();
134- instanceNodeInfo.setId(opsClusterNode.getNodeId());
135- instanceNodeInfo.setIp(opsClusterNode.getPublicIp());
136- instanceNodeInfo.setPort(opsClusterNode.getDbPort());
137- instanceNodeInfo.setDbName(opsClusterNode.getDbName());
138- instanceNodeInfo.setDbUser(opsClusterNode.getDbUser());
139- instanceNodeInfo.setDbUserPassword(opsClusterNode.getDbUserPassword());
140- instanceNodeInfo.setDbType(DatabaseType.DEFAULT.getDbType());
141- return instanceNodeInfo;
142 }72 }
143 73 
74+ /**
75+ * fetch TopSQL statistical information from database, page index
76+ *
77+ * @param nodeId instance node id
78+ * @param sqlId TopSQL debug query id
79+ * @return TopSQL statistical information
80+ */
81+ @Ds
82+ public Map<String, Object> detail(String nodeId, String sqlId) {
83+ var detail = topSqlMapper.currentDetail(sqlId);
84+ if (CollUtil.isNotEmpty(detail)) {
85+ return detail;
86+ }
87+ return topSqlMapper.historyDetail(sqlId);
88+ }
89+ 
90+ /**
91+ * fetch TopSQL execution plan from database
92+ *
93+ * @param nodeId instance node id
94+ * @param sqlId TopSQL debug query id
95+ * @return TopSQL execution plan
96+ */
97+ @Ds
98+ public ExecutionPlan executionPlan(String nodeId, String sqlId) {
99+ String plan = topSqlMapper.currentPlan(sqlId);
100+ if (StrUtil.isNotBlank(plan)) {
101+ return new ExecutionPlan(plan);
102+ }
103+ // pre-check track_stmt_stat_leve full sql level at least L1
104+ var settings = pgSettingMapper
105+ .selectOne(Wrappers.<PgSettings>lambdaQuery().eq(PgSettings::getName, "track_stmt_stat_level"));
106+ var setting = settings.getSetting();
107+ if (StringUtils.startsWith(setting, "OFF") || StringUtils.startsWith(setting, "L0")) {
108+ throw new CustomException("failGetExecutionPlan");
109+ }
110+ // get prepared statement
111+ plan = topSqlMapper.historyPlan(sqlId);
112+ // get base execution plan object
113+ return new ExecutionPlan(plan);
114+ }
115+ 
116+ /**
117+ * fetch TopSQL object information from database
118+ *
119+ * @param nodeId instance node id
120+ * @param sqlId TopSQL debug query id
121+ * @return TopSQL object information
122+ */
123+ @Ds
124+ public Map<String, Object> objectInfo(String nodeId, String sqlId) {
125+ // init objectNameList via get execution plan
126+ var plan = executionPlan(nodeId, sqlId);
127+ Set<String> curObjectNameList = plan.allAlias();
128+ List<String> modifyObjectNameList = new LinkedList<>(curObjectNameList);
129+ Map<String, Object> tableMetadata = new HashMap<>();
130+ Map<String, Object> tableStructure = new HashMap<>();
131+ Map<String, Object> tableIndex = new HashMap<>();
132+ for (String name : curObjectNameList) {
133+ try {
134+ tableMetadata.put(name, topSqlMapper.tableMetaData(name));
135+ tableStructure.put(name, topSqlMapper.tableStructure(name));
136+ tableIndex.put(name, topSqlMapper.indexInfo(name));
137+ } catch (Exception e) {
138+ modifyObjectNameList.remove(name);
139+ }
140+ }
141+ return Map.of("object_name_list", modifyObjectNameList, "table_metadata", tableMetadata, "table_structure",
142+ tableStructure, "table_index", tableIndex);
143+ }
144+ 
145+ /**
146+ * fetch TopSQL index advice from database
147+ *
148+ * @param nodeId instance node id
149+ * @param sqlId TopSQL debug query id
150+ * @return TopSQL index advice
151+ */
152+ @Ds
153+ public List<String> indexAdvice(String nodeId, String sqlId) {
154+ List<String> results = new ArrayList<>();
155+ var queryText = topSqlMapper.sql(sqlId);
156+ var advises = topSqlMapper.advise(queryText);
157+ String indexTemplate = "建议为%s模式下的%t表的%c列创建索引";
158+ String multiColumnIndexTemplate = "建议为%s模式下的%t表的%c创建复合索引";
159+ if (advises.isEmpty()) {
160+ results.add("No index suggestions");
161+ return results;
162+ }
163+ // process index advice for every returned line
164+ for (IndexAdvice advice : advises) {
165+ String column = advice.getColumn();
166+ if (StringUtils.isNotEmpty(column)) {
167+ String result = column.contains(",") ? multiColumnIndexTemplate : indexTemplate;
168+ result = result.replace("%s", advice.getSchema());
169+ result = result.replace("%t", advice.getTable());
170+ result = result.replace("%c", advice.getColumn());
171+ results.add(result);
172+ }
173+ }
174+ return results;
175+ }
176+ 
177+ @Ds
144 public List<Map<String, Object>> waitEvent(String nodeId, String sqlId) {178 public List<Map<String, Object>> waitEvent(String nodeId, String sqlId) {
145 List<Map<String, Object>> list = new ArrayList<>();179 List<Map<String, Object>> list = new ArrayList<>();
146- try {180+ String table = topSqlMapper.waitEvent(sqlId);
147- clusterManager.setCurrentDatasource(nodeId, null);181+ if (StrUtil.isBlank(table)) {
148- String table = topSqlMapper.waitEvent(sqlId);182+ return topSqlMapper.currentWaitEvent(sqlId);
149- if (StrUtil.isBlank(table)) {183+ }
150- return Collections.emptyList();184+ String[] lines = table.split(",");
185+ WaitEvent pre = null;
186+ for (String string : lines) {
187+ var curr = new WaitEvent(string);
188+ if (pre != null && pre.getE() == Event.LOCK_START && curr.getE() == Event.LOCK_END) {
189+ list.add(Map.of("starttime", pre.getTimeStr(), "lockType", pre.getLockType(), "waittime",
190+ (Duration.between(pre.getTime(), curr.getTime()).toNanos() / 1000)));
191+ pre = null;
192+ continue;
151 }193 }
152- String[] lines = table.split(",");194+ pre = curr;
153- waitEvent pre = null;
154- for (String string : lines) {
155- var curr = new waitEvent(string);
156- if (pre != null && pre.getE() == event.LOCK_START && curr.getE() == event.LOCK_END) {
157- list.add(Map.of("starttime", pre.getTimeStr(), "lockType", pre.getLockType(), "waittime",
158- (Duration.between(pre.getTime(), curr.getTime()).toNanos() / 1000)));
159- pre = null;
160- continue;
161- }
162- pre = curr;
163- }
164- } finally {
165- clusterManager.pool();
166 }195 }
167 return list;196 return list;
168 }197 }
169 198 
199+ /**
200+ * pre-check top sql list job
201+ *
202+ * true when not log; false when can search log
203+ */
204+ private void topSqlListPreCheck() {
205+ var list = pgSettingMapper.selectList(Wrappers.<PgSettings>lambdaQuery().in(PgSettings::getName,
206+ "enable_stmt_track", "enable_resource_track", "track_stmt_stat_level'"));
207+ for (PgSettings pgSettings : list) {
208+ switch (pgSettings.getName()) {
209+ case "enable_resource_track":
210+ case "enable_stmt_track":
211+ if ("off".equals(pgSettings.getSetting())) {
212+ throw new CustomException("top sql pre check fail", 602);
213+ }
214+ break;
215+ case "track_stmt_stat_level":
216+ var setting = pgSettings.getSetting();
217+ if (StringUtils.isEmpty(setting) || !setting.contains(",")) {
218+ throw new CustomException("top sql pre check fail", 602);
219+ }
220+ String[] settingArr = setting.split(",");
221+ if ("off".equalsIgnoreCase(settingArr[0])) {
222+ throw new CustomException("top sql pre check fail", 602);
223+ }
224+ break;
225+ default:
226+ }
227+ }
228+ }
229+ 
170 @Data230 @Data
171 @Generated231 @Generated
172- public static class waitEvent {232+ public static class WaitEvent {
173 private static final DateTimeFormatter[] FORMATTERS = {233 private static final DateTimeFormatter[] FORMATTERS = {
174 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSx"),234 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSx"),
175 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSx"),235 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSx"),
@@ -179,16 +239,16 @@ public class TopSQLService {
179 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.Sx")239 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.Sx")
180 };240 };
181 private String index;241 private String index;
182- private event e;242+ private Event e;
183 private LocalDateTime time;243 private LocalDateTime time;
184 private String timeStr;244 private String timeStr;
185 private String id;245 private String id;
186 private String lockType;246 private String lockType;
187 247 
188- public waitEvent(String string) {248+ public WaitEvent(String string) {
189 String[] str = string.split("'\\s+'");249 String[] str = string.split("'\\s+'");
190 this.index = str[0].replaceAll("'", "");250 this.index = str[0].replaceAll("'", "");
191- this.e = event.valueOf(str[1].replaceAll("'", ""));251+ this.e = Event.valueOf(str[1].replaceAll("'", ""));
192 for (int i = 0; i < FORMATTERS.length; i++) {252 for (int i = 0; i < FORMATTERS.length; i++) {
193 try {253 try {
194 this.time = LocalDateTime.parse(str[2].replaceAll("'", ""), FORMATTERS[i]);254 this.time = LocalDateTime.parse(str[2].replaceAll("'", ""), FORMATTERS[i]);
@@ -204,7 +264,7 @@ public class TopSQLService {
204 this.lockType = str[4].replaceAll("'", "");264 this.lockType = str[4].replaceAll("'", "");
205 }265 }
206 266 
207- public enum event {267+ public enum Event {
208 LOCK_START,268 LOCK_START,
209 LOCK_END,269 LOCK_END,
210 LOCK_RELEASE270 LOCK_RELEASE
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/AspServiceImpl.java+53-0
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service.impl;
6+ 
7+import java.util.ArrayList;
8+import java.util.HashMap;
9+import java.util.List;
10+import java.util.Map;
11+ 
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.stereotype.Service;
14+ 
15+import com.nctigba.observability.instance.aop.Ds;
16+import com.nctigba.observability.instance.dto.asp.AnalysisDto;
17+import com.nctigba.observability.instance.dto.asp.AspCountReq;
18+import com.nctigba.observability.instance.mapper.AspMapper;
19+import com.nctigba.observability.instance.service.AspService;
20+ 
21+/**
22+ * AspServiceImpl
23+ *
24+ * @author liupengfei
25+ * @since 2023/8/11
26+ */
27+@Service
28+public class AspServiceImpl implements AspService {
29+ @Autowired
30+ private AspMapper aspMapper;
31+ 
32+ @Override
33+ @Ds("id")
34+ public Map<String, List<Object>> count(AspCountReq req) {
35+ List<Map<String, Object>> searchRes = aspMapper.count(req);
36+ ArrayList<Object> time = new ArrayList<>();
37+ ArrayList<Object> count = new ArrayList<>();
38+ Map<String, List<Object>> res = new HashMap<>();
39+ for (Map<String, Object> map : searchRes) {
40+ time.add(map.get("sample_time"));
41+ count.add(map.get("session_count"));
42+ }
43+ res.put("sessionCount", count);
44+ res.put("sampleTime", time);
45+ return res;
46+ }
47+ 
48+ @Override
49+ @Ds("id")
50+ public List<AnalysisDto> analysis(AspCountReq req) {
51+ return aspMapper.analysis(req);
52+ }
53+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/ClusterOpsServiceImpl.java+645-0
@@ -0,0 +1,645 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service.impl;
6+ 
7+import cn.hutool.cache.CacheUtil;
8+import cn.hutool.cache.impl.TimedCache;
9+import cn.hutool.core.bean.BeanUtil;
10+import cn.hutool.core.lang.Pair;
11+import cn.hutool.core.map.MapUtil;
12+import cn.hutool.core.thread.ThreadUtil;
13+import cn.hutool.core.util.StrUtil;
14+import com.alibaba.fastjson.JSONObject;
15+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
16+import com.nctigba.observability.instance.constants.MetricsLine;
17+import com.nctigba.observability.instance.dto.cluster.ClusterHealthState;
18+import com.nctigba.observability.instance.dto.cluster.ClusterStateDto;
19+import com.nctigba.observability.instance.dto.cluster.ClustersDto;
20+import com.nctigba.observability.instance.dto.cluster.NodeAndCompDto;
21+import com.nctigba.observability.instance.dto.cluster.NodeRelationDto;
22+import com.nctigba.observability.instance.dto.cluster.SyncSituation;
23+import com.nctigba.observability.instance.dto.cluster.SyncSituationDto;
24+import com.nctigba.observability.instance.exception.InstanceException;
25+import com.nctigba.observability.instance.mapper.ClustersMapper;
26+import com.nctigba.observability.instance.service.ClusterManager;
27+import com.nctigba.observability.instance.service.ClusterOpsService;
28+import com.nctigba.observability.instance.service.MetricsService;
29+import com.nctigba.observability.instance.util.SshSession;
30+import lombok.extern.slf4j.Slf4j;
31+import org.jetbrains.annotations.NotNull;
32+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
33+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
34+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
35+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
36+import org.opengauss.admin.common.enums.ops.DeployTypeEnum;
37+import org.opengauss.admin.system.plugin.facade.HostFacade;
38+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
39+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
40+import org.springframework.beans.factory.annotation.Autowired;
41+import org.springframework.stereotype.Service;
42+ 
43+import java.io.IOException;
44+import java.time.Instant;
45+import java.time.LocalDateTime;
46+import java.time.LocalTime;
47+import java.time.ZoneId;
48+import java.time.ZoneOffset;
49+import java.util.ArrayList;
50+import java.util.Collection;
51+import java.util.Collections;
52+import java.util.Date;
53+import java.util.HashMap;
54+import java.util.HashSet;
55+import java.util.List;
56+import java.util.Map;
57+import java.util.Optional;
58+import java.util.Set;
59+import java.util.concurrent.ConcurrentHashMap;
60+import java.util.concurrent.CopyOnWriteArrayList;
61+import java.util.concurrent.CountDownLatch;
62+import java.util.concurrent.ExecutionException;
63+import java.util.concurrent.Future;
64+import java.util.concurrent.TimeUnit;
65+import java.util.concurrent.TimeoutException;
66+import java.util.stream.Collectors;
67+ 
68+import static com.nctigba.observability.instance.dto.cluster.NodeRelationDto.getDefaultRelationDto;
69+import static com.nctigba.observability.instance.dto.cluster.NodeRelationDto.setNodeState;
70+import static com.nctigba.observability.instance.dto.cluster.SyncSituationDto.getDefaultSituationDto;
71+ 
72+/**
73+ * ClusterOpsServiceImpl
74+ *
75+ * @author liupengfei
76+ * @since 2023/8/11
77+ */
78+@Service
79+@Slf4j
80+public class ClusterOpsServiceImpl implements ClusterOpsService {
81+ private static final MetricsLine[] CLUSTER_METRICS = {
82+ MetricsLine.CPU,
83+ MetricsLine.MEMORY,
84+ MetricsLine.IOPS_R_TOTAL,
85+ MetricsLine.NETWORK_IN_TOTAL,
86+ MetricsLine.NETWORK_OUT_TOTAL,
87+ MetricsLine.INSTANCE_QPS,
88+ MetricsLine.INSTANCE_DB_RESPONSETIME_P80,
89+ MetricsLine.INSTANCE_DB_RESPONSETIME_P95,
90+ MetricsLine.CLUSTER_PRIMARY_WAL_SEND_PRESSURE,
91+ MetricsLine.CLUSTER_PRIMARY_WAL_WRITE_PER_SEC,
92+ MetricsLine.CLUSTER_WAL_RECEIVED_DELAY,
93+ MetricsLine.CLUSTER_WAL_WRITE_DELAY,
94+ MetricsLine.CLUSTER_WAL_REPLAY_DELAY
95+ };
96+ private static final long CACHE_TIMEOUT = 15000L;
97+ private static final long CACHE_CLEAN_INTERVAL = 60000L;
98+ private static final TimedCache<String, ClusterHealthState> CLUSTER_STATE_CACHE;
99+ 
100+ static {
101+ CLUSTER_STATE_CACHE = CacheUtil.newTimedCache(CACHE_TIMEOUT);
102+ CLUSTER_STATE_CACHE.schedulePrune(CACHE_CLEAN_INTERVAL);
103+ }
104+ 
105+ @Autowired
106+ private MetricsService metricsService;
107+ @Autowired
108+ private ClusterManager clusterManager;
109+ @Autowired
110+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
111+ private HostFacade hostFacade;
112+ @Autowired
113+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
114+ private HostUserFacade hostUserFacade;
115+ @Autowired
116+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
117+ private EncryptionUtils encryptionUtils;
118+ @Autowired
119+ private ClustersMapper clustersMapper;
120+ 
121+ @Override
122+ public List<ClustersDto> listClusters() {
123+ List<ClustersDto> list = new ArrayList<>();
124+ List<OpsClusterVO> clusters = clusterManager.getAllOpsCluster();
125+ for (OpsClusterVO cluster : clusters) {
126+ if (!DeployTypeEnum.CLUSTER.equals(cluster.getDeployType())) {
127+ continue;
128+ }
129+ ClustersDto clustersDto = ClustersDto.of(cluster);
130+ list.add(clustersDto);
131+ }
132+ return list;
133+ }
134+ 
135+ @Override
136+ public List<JSONObject> nodes(String clusterId) {
137+ OpsClusterVO cluster = getOpsClusterVOById(clusterId);
138+ ClusterHealthState stateCache = getClusterHealthState(clusterId, cluster);
139+ List<OpsClusterNodeVO> clusterNodes = cluster.getClusterNodes();
140+ List<SyncSituationDto> nodeSyncSituationDtoList = new CopyOnWriteArrayList<>();
141+ 
142+ // get standby nodes
143+ getClusterStandbyNodes(nodeSyncSituationDtoList, cluster);
144+ 
145+ // check
146+ checkPrimaryNode(cluster, stateCache, nodeSyncSituationDtoList);
147+ 
148+ // get cm、om state
149+ HashMap<String, Map<String, String>> cmAndOmState = getCmAndOmState(clusterNodes);
150+ return nodeSyncSituationDtoList.stream().map(syn -> {
151+ NodeAndCompDto com = new NodeAndCompDto();
152+ com.setCmAgentState(
153+ cmAndOmState.get("cmAgent").getOrDefault(syn.getHostIp(), NodeAndCompDto.BinState.STOP.getCode()));
154+ com.setCmServerState(
155+ cmAndOmState.get("cmServer").getOrDefault(syn.getHostIp(), NodeAndCompDto.BinState.STOP.getCode()));
156+ com.setOmMonitorState(cmAndOmState.get("omMonitor").getOrDefault(syn.getHostIp(),
157+ NodeAndCompDto.BinState.STOP.getCode()));
158+ JSONObject jsonObject = new JSONObject();
159+ jsonObject.putAll(BeanUtil.beanToMap(syn));
160+ jsonObject.putAll(BeanUtil.beanToMap(com));
161+ jsonObject.remove("walSyncState");
162+ return jsonObject;
163+ }).collect(Collectors.toList());
164+ }
165+ 
166+ private HashMap<String, Map<String, String>> getCmAndOmState(List<OpsClusterNodeVO> clusterNodes) {
167+ ConcurrentHashMap<String, String> cmServer = new ConcurrentHashMap<>();
168+ ConcurrentHashMap<String, String> cmAgent = new ConcurrentHashMap<>();
169+ ConcurrentHashMap<String, String> omMonitor = new ConcurrentHashMap<>();
170+ CountDownLatch countDownLatch = ThreadUtil.newCountDownLatch(clusterNodes.size());
171+ for (OpsClusterNodeVO clusterNode : clusterNodes) {
172+ ThreadUtil.execute(() -> {
173+ try {
174+ execute(cmServer, cmAgent, omMonitor, clusterNode);
175+ } finally {
176+ countDownLatch.countDown();
177+ }
178+ });
179+ }
180+ HashMap<String, Map<String, String>> map = new HashMap<>();
181+ map.put("cmServer", cmServer);
182+ map.put("cmAgent", cmAgent);
183+ map.put("omMonitor", omMonitor);
184+ return map;
185+ }
186+ 
187+ private void execute(Map<String, String> cmServer, Map<String, String> cmAgent, Map<String, String> omMonitor,
188+ OpsClusterNodeVO clusterNode) {
189+ SshSession sshSession = null;
190+ try {
191+ sshSession = getSshSession(clusterNode);
192+ } catch (IOException e) {
193+ log.error(e.getMessage(), e);
194+ }
195+ if (sshSession == null) {
196+ cmServer.put(clusterNode.getPublicIp(), "Unknown");
197+ cmAgent.put(clusterNode.getPublicIp(), "Unknown");
198+ omMonitor.put(clusterNode.getPublicIp(), "Unknown");
199+ return;
200+ }
201+ String executeRes = null;
202+ try {
203+ executeRes = sshSession.execute("ps -aux | grep \"cm_agent\\|cm_server\\|om_monitor\" | grep -v grep");
204+ } catch (IOException e) {
205+ log.error(e.getMessage(), e);
206+ }
207+ if (executeRes == null) {
208+ cmServer.put(clusterNode.getPublicIp(), "Unknown");
209+ cmAgent.put(clusterNode.getPublicIp(), "Unknown");
210+ omMonitor.put(clusterNode.getPublicIp(), "Unknown");
211+ return;
212+ }
213+ String[] binMessage = executeRes.split(StrUtil.LF);
214+ for (String bin : binMessage) {
215+ String[] s = bin.replaceAll(" +", " ").split(" ");
216+ if (bin.contains("cm_agent")) {
217+ cmAgent.put(clusterNode.getPublicIp(), s[7]);
218+ continue;
219+ }
220+ if (bin.contains("cm_server")) {
221+ cmServer.put(clusterNode.getPublicIp(), s[7]);
222+ continue;
223+ }
224+ if (bin.contains("om_monitor")) {
225+ omMonitor.put(clusterNode.getPublicIp(), s[7]);
226+ }
227+ }
228+ }
229+ 
230+ private static void checkPrimaryNode(OpsClusterVO cluster, ClusterHealthState stateCache,
231+ List<SyncSituationDto> retList) {
232+ if (retList.size() == cluster.getClusterNodes().size()) {
233+ return;
234+ }
235+ Set<String> standbySet = retList.stream().map(SyncSituationDto::getHostIp).collect(Collectors.toSet());
236+ for (OpsClusterNodeVO clusterNode : cluster.getClusterNodes()) {
237+ if (!standbySet.contains(clusterNode.getPublicIp())) {
238+ SyncSituationDto dto = new SyncSituationDto();
239+ dto.setClusterId(cluster.getClusterId());
240+ dto.setNodeId(clusterNode.getNodeId());
241+ dto.setHostIp(clusterNode.getPublicIp());
242+ dto.setNodeName(stateCache.getNodeName().get(clusterNode.getPublicIp()));
243+ dto.setLocalAddr(clusterNode.getPublicIp() + ":" + clusterNode.getDbPort());
244+ dto.setRole(stateCache.getNodeRole().getOrDefault(clusterNode.getPublicIp(), "Unknown"));
245+ dto.setNodeState(stateCache.getNodeState().getOrDefault(clusterNode.getPublicIp(), "Unknown"));
246+ retList.add(0, dto);
247+ }
248+ }
249+ }
250+ 
251+ @NotNull
252+ private OpsClusterVO getOpsClusterVOById(String clusterId) {
253+ Optional<OpsClusterVO> clusterVOOptional = clusterManager.getAllOpsCluster().stream()
254+ .filter(clusterVO -> clusterVO.getClusterId().equals(clusterId)).findFirst();
255+ if (clusterVOOptional.isEmpty()) {
256+ throw new InstanceException("The cluster does not exist");
257+ }
258+ return clusterVOOptional.get();
259+ }
260+ 
261+ @Override
262+ public List<NodeRelationDto> relation(String clusterId) {
263+ OpsClusterVO cluster = getOpsClusterVOById(clusterId);
264+ ClusterHealthState stateCache = getClusterHealthState(clusterId, cluster);
265+ List<OpsClusterNodeVO> clusterNodes = cluster.getClusterNodes();
266+ ArrayList<NodeRelationDto> retList = new ArrayList<>();
267+ HashSet<String> allAliveNodeIpSet = new HashSet<>();
268+ for (OpsClusterNodeVO node : clusterNodes) {
269+ if ("Primary".equalsIgnoreCase(stateCache.getNodeRole().get(node.getPublicIp()))) {
270+ List<NodeRelationDto> standbyList = clustersMapper.relation(node.getNodeId());
271+ standbyList.forEach(standby -> setNodeState(standby, stateCache));
272+ NodeRelationDto primary = getDefaultRelationDto(stateCache, node, standbyList);
273+ retList.add(primary);
274+ allAliveNodeIpSet
275+ .addAll(standbyList.stream().map(NodeRelationDto::getHostIp).collect(Collectors.toList()));
276+ allAliveNodeIpSet.add(primary.getHostIp());
277+ }
278+ }
279+ 
280+ // check
281+ if (allAliveNodeIpSet.size() != clusterNodes.size()) {
282+ for (OpsClusterNodeVO node : clusterNodes) {
283+ if (!allAliveNodeIpSet.contains(node.getPublicIp())) {
284+ retList.add(getDefaultRelationDto(stateCache, node, new ArrayList<>()));
285+ }
286+ }
287+ }
288+ return retList;
289+ }
290+ 
291+ @Override
292+ public List<ClusterStateDto> allClusterState() {
293+ ArrayList<ClusterStateDto> list = new ArrayList<>();
294+ List<OpsClusterVO> clusters = clusterManager.getAllOpsCluster().stream()
295+ .filter(cluster -> DeployTypeEnum.CLUSTER.equals(cluster.getDeployType())).collect(Collectors.toList());
296+ refreshAllClusterState(clusters);
297+ clusters.forEach(cluster -> {
298+ ClusterHealthState stateCache = CLUSTER_STATE_CACHE.get(cluster.getClusterId(), false);
299+ list.add(ClusterStateDto.of(cluster, stateCache));
300+ });
301+ return list;
302+ }
303+ 
304+ private void refreshAllClusterState(List<OpsClusterVO> clusters) {
305+ CountDownLatch countDown = ThreadUtil.newCountDownLatch(clusters.size());
306+ for (OpsClusterVO cluster : clusters) {
307+ if (!DeployTypeEnum.CLUSTER.equals(cluster.getDeployType())) {
308+ continue;
309+ }
310+ ThreadUtil.execute(() -> {
311+ try {
312+ if (!CLUSTER_STATE_CACHE.containsKey(cluster.getClusterId())) {
313+ refreshHealthStateCache(cluster);
314+ }
315+ } finally {
316+ countDown.countDown();
317+ }
318+ });
319+ }
320+ try {
321+ countDown.await(10, TimeUnit.SECONDS);
322+ } catch (InterruptedException e) {
323+ throw new InstanceException(e.getMessage(), e);
324+ }
325+ }
326+ 
327+ private ClusterHealthState getClusterHealthState(String clusterId, OpsClusterVO cluster) {
328+ if (!CLUSTER_STATE_CACHE.containsKey(cluster.getClusterId())) {
329+ refreshHealthStateCache(cluster);
330+ }
331+ return CLUSTER_STATE_CACHE.get(clusterId, false);
332+ }
333+ 
334+ @Override
335+ public List<SyncSituationDto> allStandbyNodes() {
336+ List<OpsClusterVO> clusters = clusterManager.getAllOpsCluster().stream()
337+ .filter(cluster -> DeployTypeEnum.CLUSTER.equals(cluster.getDeployType())).collect(Collectors.toList());
338+ refreshAllClusterState(clusters);
339+ CopyOnWriteArrayList<SyncSituationDto> retList = new CopyOnWriteArrayList<>();
340+ CountDownLatch countDownLatch = ThreadUtil.newCountDownLatch(clusters.size());
341+ for (OpsClusterVO cluster : clusters) {
342+ ThreadUtil.execute(() -> {
343+ try {
344+ getClusterStandbyNodes(retList, cluster);
345+ } finally {
346+ countDownLatch.countDown();
347+ }
348+ });
349+ }
350+ try {
351+ countDownLatch.await(10, TimeUnit.SECONDS);
352+ } catch (InterruptedException e) {
353+ throw new InstanceException(e.getMessage(), e);
354+ }
355+ return retList;
356+ }
357+ 
358+ @Override
359+ public Map<String, Object> clusterMetrics(String clusterId, Long start, Long end, Integer step) {
360+ HashMap<String, Object> metricsNodes = new HashMap<>();
361+ OpsClusterVO opsClusterVOById = getOpsClusterVOById(clusterId);
362+ List<OpsClusterNodeVO> clusterNodes = opsClusterVOById.getClusterNodes();
363+ List<Pair<String, Future<Map<String, Object>>>> futureList = clusterNodes.stream()
364+ .map(node -> new Pair<>(node.getNodeId(),
365+ ThreadUtil.execAsync(
366+ () -> metricsService.listBatch(CLUSTER_METRICS, node.getNodeId(), start, end, step))))
367+ .collect(Collectors.toList());
368+ queryPrimaryWalTotalIncrease(end, metricsNodes, clusterNodes);
369+ for (Pair<String, Future<Map<String, Object>>> future : futureList) {
370+ Map<String, Object> nodeRes;
371+ try {
372+ nodeRes = future.getValue().get(5, TimeUnit.SECONDS);
373+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
374+ log.error(e.getMessage(), e);
375+ continue;
376+ }
377+ metricsNodes.put("time", nodeRes.get("time"));
378+ nodeRes.remove("time");
379+ getMetricsNodes(metricsNodes, future, nodeRes);
380+ }
381+ return metricsNodes;
382+ }
383+ 
384+ private void queryPrimaryWalTotalIncrease(Long end, HashMap<String, Object> metricsNodes,
385+ List<OpsClusterNodeVO> clusterNodes) {
386+ LocalDateTime currentDate = LocalDateTime.ofInstant(Instant.ofEpochSecond(end), ZoneId.systemDefault());
387+ Long startOfTheDay = currentDate.toLocalDate().atStartOfDay().minusSeconds(1)
388+ .toEpochSecond(ZoneOffset.of("+8"));
389+ Long fiveDaysAgo = LocalDateTime.of(currentDate.toLocalDate().minusDays(5), LocalTime.of(23, 59, 59))
390+ .toEpochSecond(ZoneOffset.of("+8"));
391+ List<Pair<String, Future<Map<String, Object>>>> historyPrimaryWalWriteTotalFutureList = clusterNodes.stream()
392+ .map(node -> new Pair<>(node.getNodeId(),
393+ ThreadUtil.execAsync(() -> metricsService.listBatch(new MetricsLine[]{
394+ MetricsLine.CLUSTER_PRIMARY_WAL_WRITE_TOTAL
395+ }, node.getNodeId(), fiveDaysAgo, startOfTheDay, 60 * 60 * 24)))).collect(Collectors.toList());
396+ List<Pair<String, Future<Map<String, Object>>>> todayPrimaryWalWriteTotalFutureList = clusterNodes.stream()
397+ .map(node -> new Pair<>(node.getNodeId(),
398+ ThreadUtil.execAsync(() -> metricsService.listBatch(new MetricsLine[]{
399+ MetricsLine.CLUSTER_PRIMARY_WAL_WRITE_TOTAL
400+ }, node.getNodeId(), startOfTheDay, end, (int) (end - startOfTheDay)))))
401+ .collect(Collectors.toList());
402+ List<Date> historyTimeLine = new ArrayList<>();
403+ getHistoryResult(metricsNodes, historyPrimaryWalWriteTotalFutureList, historyTimeLine);
404+ getTodayResult(metricsNodes, todayPrimaryWalWriteTotalFutureList, historyTimeLine);
405+ Collections.sort(historyTimeLine);
406+ metricsNodes.put("CLUSTER_PRIMARY_WAL_WRITE_TOTAL_TIME", historyTimeLine);
407+ }
408+ 
409+ @SuppressWarnings("unchecked")
410+ private static void getTodayResult(HashMap<String, Object> metricsNodes,
411+ List<Pair<String, Future<Map<String, Object>>>> futureList, List<Date> time) {
412+ List<Date> todayTimeLine = new ArrayList<>();
413+ for (Pair<String, Future<Map<String, Object>>> futurePair : futureList) {
414+ Map<String, Object> today = null;
415+ try {
416+ today = futurePair.getValue().get(5, TimeUnit.SECONDS);
417+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
418+ log.error(e.getMessage(), e);
419+ continue;
420+ }
421+ todayTimeLine = (List<Date>) today.get("time");
422+ for (Date aLong : todayTimeLine) {
423+ if (!time.contains(aLong)) {
424+ time.add(aLong);
425+ }
426+ }
427+ log.info("...........{}", today.entrySet());
428+ for (Map.Entry<String, Object> nodeMetrics : today.entrySet()) {
429+ if (!metricsNodes.containsKey(nodeMetrics.getKey())) {
430+ metricsNodes.put(nodeMetrics.getKey(), MapUtil.of(futurePair.getKey(), nodeMetrics.getValue()));
431+ continue;
432+ }
433+ Map<String, Object> metricsNodesValue = (Map<String, Object>) metricsNodes.get(nodeMetrics.getKey());
434+ List<Object> valueList = (List<Object>) metricsNodesValue.get(futurePair.getKey());
435+ valueList.addAll((Collection<?>) nodeMetrics.getValue());
436+ }
437+ }
438+ }
439+ 
440+ @SuppressWarnings("unchecked")
441+ private static void getHistoryResult(HashMap<String, Object> metricsNodes,
442+ List<Pair<String, Future<Map<String, Object>>>> futureList, List<Date> time) {
443+ for (Pair<String, Future<Map<String, Object>>> futurePair : futureList) {
444+ Map<String, Object> history = null;
445+ try {
446+ history = futurePair.getValue().get(5, TimeUnit.SECONDS);
447+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
448+ log.error(e.getMessage(), e);
449+ continue;
450+ }
451+ List<Date> timeLine = (List<Date>) history.get("time");
452+ for (Date aLong : timeLine) {
453+ if (!time.contains(aLong)) {
454+ time.add(aLong);
455+ }
456+ }
457+ getMetricsNodes(metricsNodes, futurePair, history);
458+ }
459+ }
460+ 
461+ private static void getMetricsNodes(HashMap<String, Object> metricsNodes,
462+ Pair<String, Future<Map<String, Object>>> future, Map<String, Object> nodeRes) {
463+ for (Map.Entry<String, Object> nodeMetrics : nodeRes.entrySet()) {
464+ if (!metricsNodes.containsKey(nodeMetrics.getKey())) {
465+ metricsNodes.put(nodeMetrics.getKey(), MapUtil.of(future.getKey(), nodeMetrics.getValue()));
466+ continue;
467+ }
468+ Map<String, Object> metricsNodesValue = (Map<String, Object>) metricsNodes.get(nodeMetrics.getKey());
469+ metricsNodesValue.put(future.getKey(), nodeMetrics.getValue());
470+ }
471+ }
472+ 
473+ private void getClusterStandbyNodes(List<SyncSituationDto> retList, OpsClusterVO cluster) {
474+ ClusterHealthState stateCache = CLUSTER_STATE_CACHE.get(cluster.getClusterId(), false);
475+ List<OpsClusterNodeVO> clusterNodes = cluster.getClusterNodes();
476+ List<SyncSituationDto> standbyList = new ArrayList<>();
477+ for (OpsClusterNodeVO clusterNode : clusterNodes) {
478+ if ("Primary".equalsIgnoreCase(stateCache.getNodeRole().get(clusterNode.getPublicIp()))) {
479+ List<SyncSituation> syncSituations = clustersMapper.getSyncSituation(clusterNode.getNodeId());
480+ syncSituations.forEach(s -> {
481+ SyncSituationDto standby = getDefaultSituationDto(s, clusterNodes, cluster.getClusterId());
482+ standby.setNodeName(stateCache.getNodeName().get(standby.getHostIp()));
483+ standby.setRole(stateCache.getNodeRole().get(standby.getHostIp()));
484+ standby.setNodeState(stateCache.getNodeState().get(standby.getHostIp()));
485+ standby.setPrimaryAddr(clusterNode.getPublicIp() + ":" + clusterNode.getDbPort());
486+ standby.setLocalAddr(standby.getHostIp() + ":" + clusterNode.getDbPort());
487+ standbyList.add(standby);
488+ });
489+ break;
490+ }
491+ }
492+ checkStandbyNode(cluster, stateCache, standbyList);
493+ retList.addAll(standbyList);
494+ }
495+ 
496+ private static void checkStandbyNode(OpsClusterVO cluster, ClusterHealthState stateCache,
497+ List<SyncSituationDto> standbyList) {
498+ // check, the cluster might not have a primary node (pending...) or disconnect
499+ ArrayList<String> allNodeIp = new ArrayList<>();
500+ for (Map.Entry<String, String> entry : stateCache.getNodeRole().entrySet()) {
501+ if (!"Primary".equals(entry.getValue())) {
502+ allNodeIp.add(entry.getKey());
503+ }
504+ }
505+ if (standbyList.isEmpty()) {
506+ for (OpsClusterNodeVO clusterNode : cluster.getClusterNodes()) {
507+ SyncSituationDto dto = new SyncSituationDto();
508+ dto.setClusterId(cluster.getClusterId());
509+ dto.setNodeId(clusterNode.getNodeId());
510+ dto.setHostIp(clusterNode.getPublicIp());
511+ dto.setNodeName(clusterNode.getHostname());
512+ dto.setLocalAddr(clusterNode.getPublicIp() + ":" + clusterNode.getHostPort());
513+ dto.setRole(stateCache.getNodeRole().getOrDefault(clusterNode.getPublicIp(), "Unknown"));
514+ dto.setNodeState("Unknown");
515+ standbyList.add(dto);
516+ }
517+ return;
518+ }
519+ if (standbyList.size() != allNodeIp.size()) {
520+ Set<String> standbyIps = standbyList.stream().map(SyncSituationDto::getHostIp).collect(Collectors.toSet());
521+ for (String ip : allNodeIp) {
522+ if (!standbyIps.contains(ip)) {
523+ SyncSituationDto dto = new SyncSituationDto();
524+ dto.setClusterId(cluster.getClusterId());
525+ dto.setNodeId(cluster.getClusterNodes().stream().filter(node -> node.getPublicIp().equals(ip))
526+ .findFirst().get().getNodeId());
527+ dto.setNodeName(stateCache.getNodeName().get(ip));
528+ dto.setLocalAddr(ip + ":" + cluster.getClusterNodes().get(0).getDbPort());
529+ dto.setRole(stateCache.getNodeRole().get(ip));
530+ dto.setNodeState(stateCache.getNodeState().get(ip));
531+ standbyList.add(dto);
532+ }
533+ }
534+ }
535+ }
536+ 
537+ private void refreshHealthStateCache(OpsClusterVO cluster) {
538+ List<OpsClusterNodeVO> clusterNodes = cluster.getClusterNodes();
539+ SshSession sshSession = null;
540+ 
541+ // Select a host that you can connect to
542+ for (OpsClusterNodeVO clusterNode : clusterNodes) {
543+ try {
544+ sshSession = getSshSession(clusterNode);
545+ break;
546+ } catch (IOException e) {
547+ log.error(e.getMessage(), e);
548+ }
549+ }
550+ if (sshSession == null) {
551+ log.error("Failed to connect cluster[{}]", cluster.getClusterId());
552+ CLUSTER_STATE_CACHE.put(cluster.getClusterId(), new ClusterHealthState());
553+ return;
554+ }
555+ ClusterHealthState healthState = getHealthState(sshSession, cluster.getEnvPath());
556+ CLUSTER_STATE_CACHE.put(cluster.getClusterId(), healthState);
557+ }
558+ 
559+ private SshSession getSshSession(OpsClusterNodeVO clusterNode) throws IOException {
560+ OpsHostEntity hostEntity = hostFacade.getById(clusterNode.getHostId());
561+ OpsHostUserEntity userEntity = hostUserFacade.listHostUserByHostId(clusterNode.getHostId()).stream()
562+ .filter(p -> p.getUsername().equals(clusterNode.getInstallUserName())).findFirst().orElseThrow(
563+ () -> new InstanceException("The node information corresponding to the host is not found"));
564+ return SshSession.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(),
565+ encryptionUtils.decrypt(userEntity.getPassword()));
566+ }
567+ 
568+ private ClusterHealthState getHealthState(SshSession sshSession, String envPath) {
569+ String command = "source " + envPath + " && gs_om -t status --detail";
570+ String result = null;
571+ try {
572+ result = sshSession.execute(command);
573+ } catch (IOException e) {
574+ log.error(e.getMessage(), e);
575+ }
576+ if (result == null) {
577+ return new ClusterHealthState();
578+ }
579+ ClusterHealthState res = new ClusterHealthState();
580+ Map<String, String> cmState = new HashMap<>(1);
581+ int cmIndex = result.indexOf("CMServer State");
582+ if (cmIndex > 0) {
583+ cmState(result, cmState, cmIndex);
584+ }
585+ res.setCmState(cmState);
586+ int clusterStateIndex = result.indexOf("cluster_state");
587+ String clusterState = null;
588+ if (clusterStateIndex > 0) {
589+ int splitIndex = result.indexOf(":", clusterStateIndex);
590+ int lineEndIndex = result.indexOf(StrUtil.LF, clusterStateIndex);
591+ clusterState = result.substring(splitIndex + 1, lineEndIndex).trim();
592+ }
593+ res.setClusterState(clusterState);
594+ int datanodeStateIndex = result.indexOf("Datanode State");
595+ if (datanodeStateIndex > 0) {
596+ nodeState(result, datanodeStateIndex, res);
597+ }
598+ return res;
599+ }
600+ 
601+ private static void cmState(String result, Map<String, String> cmState, int cmIndex) {
602+ int splitIndex = result.indexOf("------------------", cmIndex);
603+ String dataNodeStateStr = result.substring(splitIndex);
604+ String[] dataNode = dataNodeStateStr.split(StrUtil.LF);
605+ for (String s : dataNode) {
606+ String[] s1 = s.replaceAll(" +", " ").split(" ");
607+ if (s1.length == 6) {
608+ cmState.put(s1[2], s1[5].trim());
609+ }
610+ }
611+ }
612+ 
613+ private static void nodeState(String result, int datanodeStateIndex, ClusterHealthState res) {
614+ Map<String, String> nodeState = new HashMap<>(1);
615+ Map<String, String> nodeRole = new HashMap<>(1);
616+ Map<String, String> nodeName = new HashMap<>(1);
617+ int splitIndex = result.indexOf("------------------", datanodeStateIndex);
618+ String dataNodeStateStr = result.substring(splitIndex);
619+ String[] dataNode = dataNodeStateStr.split(StrUtil.LF);
620+ for (String s : dataNode) {
621+ String[] s1 = s.replaceAll(" +", " ").split(" ");
622+ String state = "";
623+ String role = "";
624+ if (s1.length == 1) {
625+ continue;
626+ }
627+ if (s1.length >= 9) {
628+ for (int i = 8; i < s1.length; i++) {
629+ state += (s1[i] + " ");
630+ }
631+ role = s1[7];
632+ }
633+ if (s1.length == 8) {
634+ state = s1[7];
635+ role = s1[6];
636+ }
637+ nodeState.put(s1[2], state.trim());
638+ nodeRole.put(s1[2], role);
639+ nodeName.put(s1[2], s1[1]);
640+ }
641+ res.setNodeState(nodeState);
642+ res.setNodeRole(nodeRole);
643+ res.setNodeName(nodeName);
644+ }
645+}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/MonitoringServiceImpl.java+0-65
@@ -1,65 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.service.impl;
5- 
6-import java.util.HashMap;
7-import java.util.List;
8-import java.util.Map;
9- 
10-import org.opengauss.admin.common.exception.CustomException;
11-import org.springframework.beans.factory.annotation.Autowired;
12-import org.springframework.stereotype.Service;
13- 
14-import com.alibaba.fastjson.JSONArray;
15-import com.alibaba.fastjson.JSONObject;
16-import com.nctigba.observability.instance.constants.MonitoringResultType;
17-import com.nctigba.observability.instance.factory.MonitoringHandlerFactory;
18-import com.nctigba.observability.instance.handler.monitoring.MonitoringHandler;
19-import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;
20-import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
21-import com.nctigba.observability.instance.service.MonitoringService;
22- 
23-@Service
24-public class MonitoringServiceImpl implements MonitoringService {
25- @Autowired
26- private MonitoringHandlerFactory monitoringHandlerFactory;
27- 
28- @Override
29- public Map<String, Object> getPointMonitoringData(MonitoringParam param) {
30- MonitoringHandler monitoringHandler = monitoringHandlerFactory.getInstance(param.getMonitoringType());
31- List<MonitoringMetric> monitoringMetricList = monitoringHandler.pointQuery(param.getQuery(), param.getTime());
32- Map<String, Object> map = new HashMap<>();
33- for (MonitoringMetric monitoringMetric : monitoringMetricList) {
34- JSONObject metric = monitoringMetric.getMetric();
35- JSONArray value = monitoringMetric.getValue();
36- if (metric.containsKey("__name__")) {
37- metric.put("value", value.size() == 2 ? value.get(1) : null);
38- map.put(metric.getString("__name__"), metric);
39- } else {
40- map.put("value", value.size() == 2 ? value.get(1) : null);
41- }
42- }
43- return map;
44- }
45- 
46- @Override
47- public List<MonitoringMetric> getCurrentMonitoringData(MonitoringParam param) {
48- MonitoringHandler monitoringHandler = monitoringHandlerFactory.getInstance(param.getMonitoringType());
49- return monitoringHandler.pointQuery(param.getQuery(), param.getTime());
50- }
51- 
52- @Override
53- public List<Object> getRangeMonitoringData(MonitoringParam param) {
54- MonitoringHandler monitoringHandler = monitoringHandlerFactory.getInstance(param.getMonitoringType());
55- List<MonitoringMetric> monitoringMetricList = monitoringHandler.rangeQuery(param.getQuery(), param.getStart(),
56- param.getEnd(), param.getStep());
57- if (MonitoringResultType.TABLE.name().equalsIgnoreCase(param.getType())) {
58- return monitoringHandler.metricToTable(monitoringMetricList, param);
59- } else if (MonitoringResultType.LINE.name().equalsIgnoreCase(param.getType())) {
60- return monitoringHandler.metricToLine(monitoringMetricList, param);
61- } else {
62- throw new CustomException("Unsupported data format:" + param.getType(), 400);
63- }
64- }
65-}
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/SessionServiceImpl.java+0-236
@@ -1,236 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance.service.impl;
5- 
6-import java.sql.Connection;
7-import java.util.HashMap;
8-import java.util.List;
9-import java.util.Map;
10-import java.util.concurrent.Callable;
11-import java.util.concurrent.ExecutionException;
12-import java.util.concurrent.Future;
13- 
14-import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
15-import org.opengauss.admin.common.exception.CustomException;
16-import org.springframework.stereotype.Service;
17- 
18-import com.alibaba.fastjson.JSONObject;
19-import com.nctigba.common.web.exception.InstanceException;
20-import com.nctigba.observability.instance.constants.DatabaseType;
21-import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
22-import com.nctigba.observability.instance.factory.SessionHandlerFactory;
23-import com.nctigba.observability.instance.handler.session.SessionHandler;
24-import com.nctigba.observability.instance.model.InstanceNodeInfo;
25-import com.nctigba.observability.instance.service.ClusterManager;
26-import com.nctigba.observability.instance.service.SessionService;
27- 
28-import cn.hutool.core.thread.ThreadUtil;
29-import lombok.RequiredArgsConstructor;
30-import lombok.extern.slf4j.Slf4j;
31- 
32-@Service
33-@RequiredArgsConstructor
34-@Slf4j
35-public class SessionServiceImpl implements SessionService {
36- 
37- private final SessionHandlerFactory sessionHandlerFactory;
38- private final ClusterManager opsFacade;
39- 
40- @Override
41- public JSONObject detailGeneral(String id, String sessionid) {
42- // query node info
43- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
44- // get handler
45- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
46- Connection connection = handler.getConnection(instanceNodeInfo);
47- JSONObject general;
48- try {
49- // query TopSQL list
50- general = handler.detailGeneral(connection, sessionid);
51- } catch (Exception e) {
52- throw new CustomException("", e);
53- } finally {
54- handler.close(connection);
55- }
56- return general;
57- }
58- 
59- @Override
60- public List<DetailStatisticDto> detailStatistic(String id, String sessionid) {
61- // query node info
62- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
63- // get handler
64- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
65- Connection connection = handler.getConnection(instanceNodeInfo);
66- List<DetailStatisticDto> statistic;
67- try {
68- // query TopSQL list
69- statistic = handler.detailStatistic(connection, sessionid);
70- } catch (Exception e) {
71- throw new CustomException("", e);
72- } finally {
73- handler.close(connection);
74- }
75- return statistic;
76- }
77- 
78- @Override
79- public List<JSONObject> detailWaiting(String id, String sessionid) {
80- // query node info
81- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
82- // get handler
83- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
84- Connection connection = handler.getConnection(instanceNodeInfo);
85- List<JSONObject> detailWaiting;
86- try {
87- // query list
88- detailWaiting = handler.detailWaiting(connection, sessionid);
89- } catch (Exception e) {
90- throw new CustomException("", e);
91- } finally {
92- handler.close(connection);
93- }
94- return detailWaiting;
95- }
96- 
97- @Override
98- public List<JSONObject> detailBlockTree(String id, String sessionid) {
99- // query node info
100- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
101- // get handler
102- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
103- Connection connection = handler.getConnection(instanceNodeInfo);
104- List<JSONObject> detailBlockTree;
105- try {
106- // query list
107- detailBlockTree = handler.detailBlockTree(connection, sessionid);
108- } catch (Exception e) {
109- throw new CustomException("", e);
110- } finally {
111- handler.close(connection);
112- }
113- return detailBlockTree;
114- }
115- 
116- @Override
117- public JSONObject simpleStatistic(String id) {
118- // query node info
119- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
120- // get handler
121- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
122- Connection connection = handler.getConnection(instanceNodeInfo);
123- JSONObject simpleStatistic;
124- try {
125- // query list
126- simpleStatistic = handler.simpleStatistic(connection);
127- } catch (Exception e) {
128- throw new CustomException("", e);
129- } finally {
130- handler.close(connection);
131- }
132- return simpleStatistic;
133- }
134- 
135- @Override
136- public List<JSONObject> longTxc(String id) {
137- // query node info
138- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
139- // get handler
140- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
141- Connection connection = handler.getConnection(instanceNodeInfo);
142- List<JSONObject> longTxc;
143- try {
144- // query list
145- longTxc = handler.longTxc(connection);
146- } catch (Exception e) {
147- throw new CustomException("", e);
148- } finally {
149- handler.close(connection);
150- }
151- return longTxc;
152- }
153- 
154- @Override
155- public HashMap<String, List<JSONObject>> blockAndLongTxc(String id) {
156- // query node info
157- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
158- // get handler
159- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
160- Connection connection = handler.getConnection(instanceNodeInfo);
161- Future<List<JSONObject>> blockFuture = ThreadUtil.execAsync(() -> handler.detailBlockTree(connection, null));
162- Future<List<JSONObject>> longTxcFuture = ThreadUtil.execAsync(() -> handler.longTxc(connection));
163- List<JSONObject> blockTree;
164- List<JSONObject> longTxc;
165- try {
166- blockTree = blockFuture.get();
167- longTxc = longTxcFuture.get();
168- } catch (Exception e) {
169- log.error("", e);
170- throw new CustomException("", e);
171- } finally {
172- handler.close(connection);
173- }
174- HashMap<String, List<JSONObject>> res = new HashMap<>();
175- res.put("blockTree", blockTree);
176- res.put("longTxc", longTxc);
177- return res;
178- }
179- 
180- @SuppressWarnings({
181- "rawtypes",
182- "unchecked"
183- })
184- @Override
185- public Map<String, Object> detail(String id, String sessionid) {
186- // query node info
187- InstanceNodeInfo instanceNodeInfo = queryNodeInfo(id);
188- // get handler
189- SessionHandler handler = sessionHandlerFactory.getInstance(instanceNodeInfo.getDbType());
190- Connection connection = handler.getConnection(instanceNodeInfo);
191- Map<String, Callable> callables = new HashMap<>();
192- callables.put("general", () -> handler.detailGeneral(connection, sessionid));
193- callables.put("statistic", () -> handler.detailStatistic(connection, sessionid));
194- callables.put("blockTree", () -> handler.detailBlockTree(connection, sessionid));
195- callables.put("waiting", () -> handler.detailWaiting(connection, sessionid));
196- Map<String, Future> futures = new HashMap<>();
197- for (Map.Entry<String, Callable> callable : callables.entrySet()) {
198- futures.put(callable.getKey(), ThreadUtil.execAsync(callable.getValue()));
199- }
200- HashMap<String, Object> resMap = new HashMap<>();
201- try {
202- for (Map.Entry<String, Future> future : futures.entrySet()) {
203- resMap.put(future.getKey(), future.getValue().get());
204- }
205- } catch (ExecutionException | InterruptedException e) {
206- if (e.getCause() instanceof InstanceException) {
207- InstanceException instanceException = (InstanceException) e.getCause();
208- log.error(instanceException.getMessage(), e);
209- throw instanceException;
210- }
211- throw new InstanceException(e.getMessage(), e);
212- } finally {
213- handler.close(connection);
214- }
215- return resMap;
216- }
217- 
218- /**
219- * Query instance node information
220- *
221- * @param nodeId instance node id
222- * @return Instance node information
223- */
224- public InstanceNodeInfo queryNodeInfo(String nodeId) {
225- OpsClusterNodeVO opsClusterNode = opsFacade.getOpsNodeById(nodeId);
226- InstanceNodeInfo instanceNodeInfo = new InstanceNodeInfo();
227- instanceNodeInfo.setId(opsClusterNode.getNodeId());
228- instanceNodeInfo.setIp(opsClusterNode.getPublicIp());
229- instanceNodeInfo.setPort(opsClusterNode.getDbPort());
230- instanceNodeInfo.setDbName(opsClusterNode.getDbName());
231- instanceNodeInfo.setDbUser(opsClusterNode.getDbUser());
232- instanceNodeInfo.setDbUserPassword(opsClusterNode.getDbUserPassword());
233- instanceNodeInfo.setDbType(DatabaseType.DEFAULT.getDbType());
234- return instanceNodeInfo;
235- }
236-}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/MessageSourceUtil.java+1-1
@@ -69,7 +69,7 @@ public class MessageSourceUtil {
69 }69 }
70 70 
71 public static String getMsg(String key) {71 public static String getMsg(String key) {
72- return messageSource.getMessage(key, null, key, getRequestLocale());72+ return messageSource.getMessage(key, null, null, getRequestLocale());
73 }73 }
74 74 
75 public static String getMsg(String key, Object... objs) {75 public static String getMsg(String key, Object... objs) {
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/SshSession.java+25-0
@@ -1,6 +1,7 @@
1/*1/*
2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.2 * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3 */3 */
4+ 
4package com.nctigba.observability.instance.util;5package com.nctigba.observability.instance.util;
5 6 
6import java.io.ByteArrayInputStream;7import java.io.ByteArrayInputStream;
@@ -156,6 +157,30 @@ public class SshSession implements AutoCloseable {
156 return new SshSession(host, port, username, password);157 return new SshSession(host, port, username, password);
157 }158 }
158 159 
160+ /**
161+ * session is Open
162+ *
163+ * @return boolean
164+ */
165+ public boolean isConnected() {
166+ return session.isOpen();
167+ }
168+ 
169+ /**
170+ * get session through sessionPool
171+ *
172+ * @param host host
173+ * @param port port
174+ * @param username username
175+ * @param password password
176+ * @return SshSession SshSession
177+ * @throws IOException IOException
178+ */
179+ public static SshSession getSession(String host, Integer port, String username, String password)
180+ throws IOException {
181+ return SshSessionPool.INSTANCE.getSession(host, port, username, password);
182+ }
183+ 
159 @Override184 @Override
160 public void close() throws IOException {185 public void close() throws IOException {
161 session.close();186 session.close();
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/SshSessionPool.java+111-0
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.util;
6+ 
7+import static com.nctigba.observability.instance.util.SshSession.connect;
8+ 
9+import java.io.IOException;
10+import java.util.Collection;
11+import java.util.Iterator;
12+import java.util.Map;
13+import java.util.concurrent.ConcurrentHashMap;
14+ 
15+import org.opengauss.admin.common.exception.CustomException;
16+ 
17+import cn.hutool.core.util.StrUtil;
18+ 
19+/**
20+ * SshSessionPool
21+ *
22+ * @author liupengfei
23+ * @since 2023/8/11
24+ */
25+public enum SshSessionPool {
26+ INSTANCE;
27+ 
28+ private Map<String, SshSession> sessionPool = new ConcurrentHashMap<>();
29+ private static final Object LOCK = new Object();
30+ 
31+ /**
32+ * get a session by key
33+ *
34+ * @param key key
35+ * @return SshSession
36+ */
37+ public SshSession get(String key) {
38+ return sessionPool.get(key);
39+ }
40+ 
41+ /**
42+ * put a session
43+ *
44+ * @param key key
45+ * @param session SshSession
46+ */
47+ public void put(String key, SshSession session) {
48+ this.sessionPool.put(key, session);
49+ }
50+ 
51+ /**
52+ * get an SSH session and reuse a previously used session.
53+ *
54+ * @param sshHost host
55+ * @param sshPort port
56+ * @param sshUser username
57+ * @param sshPass password
58+ * @return an SSH session
59+ * @throws IOException IOException
60+ */
61+ public SshSession getSession(String sshHost, int sshPort, String sshUser, String sshPass) throws IOException {
62+ final String key = StrUtil.format("{}@{}:{}", sshUser, sshHost, sshPort);
63+ SshSession session = get(key);
64+ if (session == null || !session.isConnected()) {
65+ synchronized (LOCK) {
66+ session = get(key);
67+ if (session == null || !session.isConnected()) {
68+ session = connect(sshHost, sshPort, sshUser, sshPass);
69+ put(key, session);
70+ }
71+ }
72+ }
73+ return session;
74+ }
75+ 
76+ /**
77+ * remove a session
78+ *
79+ * @param session session
80+ */
81+ public void remove(SshSession session) {
82+ if (session != null) {
83+ final Iterator<Map.Entry<String, SshSession>> iterator = this.sessionPool.entrySet().iterator();
84+ Map.Entry<String, SshSession> entry;
85+ while (iterator.hasNext()) {
86+ entry = iterator.next();
87+ if (session.equals(entry.getValue())) {
88+ iterator.remove();
89+ break;
90+ }
91+ }
92+ }
93+ }
94+ 
95+ /**
96+ * close and clear all session
97+ */
98+ public void closeAll() {
99+ Collection<SshSession> sessions = sessionPool.values();
100+ for (SshSession session : sessions) {
101+ if (session.isConnected()) {
102+ try {
103+ session.close();
104+ } catch (IOException e) {
105+ throw new CustomException(null, e);
106+ }
107+ }
108+ }
109+ sessionPool.clear();
110+ }
111+}
Mplugins/observability-instance/src/main/resources/messages.properties+41-0
@@ -184,3 +184,44 @@ promuninstall.step4 = \u505C\u6B62prometheus
184promuninstall.step5 = \u5378\u8F7D\u5B8C\u6210184promuninstall.step5 = \u5378\u8F7D\u5B8C\u6210
185session.detail.general.message=The session id does not exist, probably the session has finished executing and closed.185session.detail.general.message=The session id does not exist, probably the session has finished executing and closed.
186session.detail.block.message=The number of rows returned by querying block session through sessionid should be equal to 1, but return {0} 186session.detail.block.message=The number of rows returned by querying block session through sessionid should be equal to 1, but return {0}
187+cluster.state.value.Normal=\u53EF\u7528(\u6709\u5197\u4F59\u5907\u4EFD)
188+cluster.state.value.Unavailable=\u4E0D\u53EF\u7528
189+cluster.state.value.Degraded=\u53EF\u7528(\u5B58\u5728\u6545\u969C\u8282\u70B9)
190+cluster.state.value.Unknown=\u72B6\u6001\u672A\u77E5
191+cluster.node.role.Primary=\u4E3B\u5B9E\u4F8B
192+cluster.node.role.Standby=\u5907\u5B9E\u4F8B
193+cluster.node.role.Cascade=Cascade
194+cluster.node.role.Pending=\u4EF2\u88C1\u9636\u6BB5
195+cluster.node.role.Unknown=\u72B6\u6001\u672A\u77E5
196+cluster.node.role.Down=\u5B95\u673A
197+cluster.node.role.Abnormal=\u5F02\u5E38
198+cluster.node.role.Manually\ stopped=\u624B\u52A8\u505C\u6B62
199+cluster.node.state.Normal=\u6B63\u5E38
200+cluster.node.state.Need\ repair=\u9700\u8981\u4FEE\u590D
201+cluster.node.state.Starting=\u542F\u52A8\u4E2D
202+cluster.node.state.Wait\ promoting=\u7B49\u5F85\u5347\u7EA7
203+cluster.node.state.Promoting=\u6B63\u5728\u5347\u7EA7
204+cluster.node.state.Demoting=\u964D\u7EA7\u4E2D
205+cluster.node.state.Building=\u91CD\u5EFA
206+cluster.node.state.Catchup=\u8FFD\u8D76
207+cluster.node.state.Coredump=\u5D29\u6E83
208+cluster.node.state.Unknown=\u672A\u77E5
209+cluster.arch={0}\u4E3B{1}\u5907
210+cluster.state.desc.Normal=
211+cluster.state.desc.Unavailable=
212+cluster.state.desc.Degraded=
213+cluster.state.desc.Unknown=
214+cluster.node.sync.Async=\u5F02\u6B65\u590D\u5236
215+cluster.node.sync.Sync=\u540C\u6B65\u590D\u5236
216+cluster.node.sync.Potential=\u5F02\u6B65\u590D\u5236\uFF08\u6F5C\u5728\u540C\u6B65\u5E93\uFF09
217+cluster.node.syncState.Streaming=\u4E00\u81F4
218+cluster.node.syncState.Catchup=\u8FFD\u8D76
219+OS.bin.state.TASK_UNINTERRUPTIBLE=\u7761\u7720
220+OS.bin.state.TASK_RUNNING=\u6B63\u5E38
221+OS.bin.state.TASK_INTERRUPTIBLE=\u7761\u7720
222+OS.bin.state.TASK_STOPPED=\u6682\u505C
223+OS.bin.state.TASK_TRACED=\u88AB\u8DDF\u8E2A
224+OS.bin.state.EXIT_ZOMBIE=\u50F5\u6B7B
225+OS.bin.state.EXIT_DEAD=\u9000\u51FA
226+OS.bin.state.UNKNOWN=\u672A\u77E5
227+OS.bin.state.STOP=\u4E2D\u65AD
Mplugins/observability-instance/src/main/resources/messages_en_US.properties+41-0
@@ -187,3 +187,44 @@ promuninstall.step4 = Stop Prometheus
187promuninstall.step5 = Uninstall complete187promuninstall.step5 = Uninstall complete
188session.detail.general.message=The session id does not exist, probably the session has finished executing and closed.188session.detail.general.message=The session id does not exist, probably the session has finished executing and closed.
189session.detail.block.message=The number of rows returned by querying block session through sessionid should be equal to 1, but return {0} 189session.detail.block.message=The number of rows returned by querying block session through sessionid should be equal to 1, but return {0}
190+cluster.state.value.Normal=Normal
191+cluster.state.value.Unavailable=Unavailable
192+cluster.state.value.Degraded=Degraded
193+cluster.state.value.Unknown=Unknown
194+cluster.node.role.Primary=Primary
195+cluster.node.role.Standby=Standby
196+cluster.node.role.Cascade=Cascade
197+cluster.node.role.Pending=Pending
198+cluster.node.role.Unknown=Unknown
199+cluster.node.role.Down=Down
200+cluster.node.role.Abnormal=Abnormal
201+cluster.node.role.Manually\ stopped=Manually stopped
202+cluster.node.state.Normal=Normal
203+cluster.node.state.Need\ repair=Need repair
204+cluster.node.state.Starting=Starting
205+cluster.node.state.Wait\ promoting=Wait promoting
206+cluster.node.state.Promoting=Promoting
207+cluster.node.state.Demoting=Demoting
208+cluster.node.state.Building=Building
209+cluster.node.state.Catchup=Catchup
210+cluster.node.state.Coredump=Coredump
211+cluster.node.state.Unknown=Unknown
212+cluster.arch={0} primary {1} standby
213+cluster.state.desc.Normal=All database nodes in the cluster are healthy
214+cluster.state.desc.Unavailable=The cluster is unavailable
215+cluster.state.desc.Degraded=The cluster is available, but there are failed database nodes
216+cluster.state.desc.Unknown=The cluster cannot be connected and the status is unknown
217+cluster.node.sync.Async=Async
218+cluster.node.sync.Sync=Sync
219+cluster.node.sync.Potential=Potential
220+cluster.node.syncState.Streaming=Streaming
221+cluster.node.syncState.Catchup=catchup
222+OS.bin.state.TASK_UNINTERRUPTIBLE=TASK_UNINTERRUPTIBLE
223+OS.bin.state.TASK_RUNNING=TASK_RUNNING
224+OS.bin.state.TASK_INTERRUPTIBLE=TASK_INTERRUPTIBLE
225+OS.bin.state.TASK_STOPPED=TASK_STOPPED
226+OS.bin.state.TASK_TRACED=TASK_TRACED
227+OS.bin.state.EXIT_ZOMBIE=EXIT_ZOMBIE
228+OS.bin.state.EXIT_DEAD=EXIT_DEAD
229+OS.bin.state.UNKNOWN=unknown
230+OS.bin.state.STOP=stop
Mplugins/observability-instance/src/main/resources/messages_zh_CN.properties+41-0
@@ -214,3 +214,44 @@ promuninstall.step4 = \u505C\u6B62prometheus
214promuninstall.step5 = \u5378\u8F7D\u5B8C\u6210214promuninstall.step5 = \u5378\u8F7D\u5B8C\u6210
215session.detail.general.message=\u4F1A\u8BDDID\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u4F1A\u8BDD\u5DF2\u6267\u884C\u5B8C\u6BD5\u5E76\u5173\u95ED\u3002215session.detail.general.message=\u4F1A\u8BDDID\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u4F1A\u8BDD\u5DF2\u6267\u884C\u5B8C\u6BD5\u5E76\u5173\u95ED\u3002
216session.detail.block.message=\u6839\u636Esessionid\u67E5\u8BE2\u7684\u963B\u585E\u4F1A\u8BDD\u4FE1\u606F\u5E94\u4E3A1\u884C\uFF0C\u4F46\u7ED3\u679C\u4E3A{0}\u884C216session.detail.block.message=\u6839\u636Esessionid\u67E5\u8BE2\u7684\u963B\u585E\u4F1A\u8BDD\u4FE1\u606F\u5E94\u4E3A1\u884C\uFF0C\u4F46\u7ED3\u679C\u4E3A{0}\u884C
217+cluster.state.value.Normal=\u53EF\u7528(\u6709\u5197\u4F59\u5907\u4EFD)
218+cluster.state.value.Unavailable=\u4E0D\u53EF\u7528
219+cluster.state.value.Degraded=\u53EF\u7528(\u5B58\u5728\u6545\u969C\u8282\u70B9)
220+cluster.state.value.Unknown=\u72B6\u6001\u672A\u77E5
221+cluster.node.role.Primary=\u4E3B\u5B9E\u4F8B
222+cluster.node.role.Standby=\u5907\u5B9E\u4F8B
223+cluster.node.role.Cascade=\u7EA7\u8054\u5907\u5B9E\u4F8B
224+cluster.node.role.Pending=\u4EF2\u88C1\u9636\u6BB5
225+cluster.node.role.Unknown=\u72B6\u6001\u672A\u77E5
226+cluster.node.role.Down=\u5B95\u673A
227+cluster.node.role.Abnormal=\u5F02\u5E38
228+cluster.node.role.Manually\ stopped=\u624B\u52A8\u505C\u6B62
229+cluster.node.state.Normal=\u6B63\u5E38
230+cluster.node.state.Need\ repair=\u9700\u8981\u4FEE\u590D
231+cluster.node.state.Starting=\u542F\u52A8\u4E2D
232+cluster.node.state.Wait\ promoting=\u7B49\u5F85\u5347\u7EA7
233+cluster.node.state.Promoting=\u6B63\u5728\u5347\u7EA7
234+cluster.node.state.Demoting=\u964D\u7EA7\u4E2D
235+cluster.node.state.Building=\u91CD\u5EFA
236+cluster.node.state.Catchup=\u8FFD\u8D76
237+cluster.node.state.Coredump=\u5D29\u6E83
238+cluster.node.state.Unknown=\u672A\u77E5
239+cluster.arch={0}\u4E3B{1}\u5907
240+cluster.state.desc.Normal=\u96C6\u7FA4\u4E2D\u6240\u6709\u6570\u636E\u5E93\u8282\u70B9\u8FD0\u884C\u6B63\u5E38
241+cluster.state.desc.Unavailable=\u96C6\u7FA4\u4E0D\u53EF\u7528
242+cluster.state.desc.Degraded=\u96C6\u7FA4\u53EF\u7528\uFF0C\u4F46\u5B58\u5728\u6545\u969C\u7684\u6570\u636E\u5E93\u8282\u70B9
243+cluster.state.desc.Unknown=\u96C6\u7FA4\u65E0\u6CD5\u8FDE\u63A5\uFF0C\u72B6\u6001\u672A\u77E5
244+cluster.node.sync.Async=\u5F02\u6B65\u590D\u5236
245+cluster.node.sync.Sync=\u540C\u6B65\u590D\u5236
246+cluster.node.sync.Potential=\u5F02\u6B65\u590D\u5236\uFF08\u6F5C\u5728\u540C\u6B65\u5E93\uFF09
247+cluster.node.syncState.Streaming=\u4E00\u81F4
248+cluster.node.syncState.Catchup=\u8FFD\u8D76
249+OS.bin.state.TASK_UNINTERRUPTIBLE=\u7761\u7720
250+OS.bin.state.TASK_RUNNING=\u6B63\u5E38
251+OS.bin.state.TASK_INTERRUPTIBLE=\u7761\u7720
252+OS.bin.state.TASK_STOPPED=\u6682\u505C
253+OS.bin.state.TASK_TRACED=\u88AB\u8DDF\u8E2A
254+OS.bin.state.EXIT_ZOMBIE=\u50F5\u6B7B
255+OS.bin.state.EXIT_DEAD=\u9000\u51FA
256+OS.bin.state.UNKNOWN=\u672A\u77E5
257+OS.bin.state.STOP=\u4E2D\u65AD
Dplugins/observability-instance/src/test/java/com/nctigba/observability/instance/ObservabilityPluginApplicationTests.java+0-23
@@ -1,23 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4-package com.nctigba.observability.instance;
5- 
6-import org.junit.jupiter.api.Test;
7-import org.springframework.boot.test.context.SpringBootTest;
8- 
9-//@SpringBootTest
10-class ObservabilityPluginApplicationTests {
11-// @Autowired
12-// SlowLogMapper slowLogMapper;
13- // @Test
14- void contextLoads() {
15-// List a = slowLogMapper.slowLogtTotalList("postgres","2022-09-19 15:10:12.080 +0800","2022-09-19 15:10:12.080 +0800");
16-// System.out.println(slowLogMapper.slowLogtTotalList("postgres",
17-// "2022-09-19 15:10:12.080 +0800",
18-// "2022-09-19 15:10:12.080 +0800"));
19-// System.out.println(slowLogMapper.slowLogtTrendList1());
20- 
21- }
22- 
23-}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/config/ParamInfoInitConfigTest.java+73-0
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.config;
6+ 
7+import static org.mockito.ArgumentMatchers.anyString;
8+import static org.mockito.Mockito.mock;
9+import static org.mockito.Mockito.mockStatic;
10+import static org.mockito.Mockito.when;
11+ 
12+import java.io.File;
13+import java.lang.reflect.Field;
14+import java.sql.Connection;
15+import java.util.Map;
16+ 
17+import org.junit.jupiter.api.Test;
18+import org.mockito.MockedStatic;
19+ 
20+import cn.hutool.core.io.FileUtil;
21+ 
22+/**
23+ * ParamInfoInitConfigTest
24+ *
25+ * @since 2023717
26+ */
27+class ParamInfoInitConfigTest {
28+ @Test
29+ void testGetCon() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
30+ deleteFile();
31+ try (MockedStatic<FileUtil> mockedStatic = mockStatic(FileUtil.class)) {
32+ File fileMock = mock(File.class);
33+ when(fileMock.exists()).thenReturn(false);
34+ when(fileMock.getParentFile()).thenReturn(fileMock);
35+ mockedStatic.when(() -> FileUtil.file(anyString())).thenReturn(fileMock);
36+ ParamInfoInitConfig.getCon(ParamInfoInitConfig.PARAMINFO);
37+ 
38+ deleteFile();
39+ clearCache();
40+ ParamInfoInitConfig.getCon(ParamInfoInitConfig.PARAMINFO);
41+ 
42+ when(fileMock.exists()).thenReturn(true);
43+ deleteFile();
44+ clearCache();
45+ ParamInfoInitConfig.getCon(ParamInfoInitConfig.PARAMINFO);
46+ }
47+ }
48+ 
49+ private static void deleteFile() {
50+ File info = new File("data" + File.separatorChar + "paramInfo.db");
51+ if (info.exists()) {
52+ info.delete();
53+ }
54+ File valueInfo = new File("data" + File.separatorChar + "paramValueInfo.db");
55+ if (valueInfo.exists()) {
56+ valueInfo.delete();
57+ }
58+ File nul = new File("null");
59+ if (nul.exists()) {
60+ nul.delete();
61+ }
62+ }
63+ 
64+ private static void clearCache()
65+ throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
66+ Class<ParamInfoInitConfig> clazz = ParamInfoInitConfig.class;
67+ Field field = clazz.getDeclaredField("map");
68+ field.setAccessible(true);
69+ @SuppressWarnings("unchecked")
70+ var map = (Map<String, Connection>) field.get(null);
71+ map.clear();
72+ }
73+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/constants/CommonConstantsTest.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.constants;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertThrows;
8+ 
9+import java.lang.reflect.InvocationTargetException;
10+ 
11+import org.junit.jupiter.api.Test;
12+ 
13+/**
14+ * CommonConstantsTest
15+ *
16+ * @since 2023717
17+ */
18+class CommonConstantsTest {
19+ @Test
20+ void test() {
21+ assertThrows(InvocationTargetException.class, () -> {
22+ var cons = CommonConstants.class.getDeclaredConstructor();
23+ cons.setAccessible(true);
24+ cons.newInstance();
25+ });
26+ }
27+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/controller/IndexControllerTest.java+95-0
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.controller;
6+ 
7+import static org.mockito.ArgumentMatchers.anyInt;
8+import static org.mockito.ArgumentMatchers.anyString;
9+import static org.mockito.Mockito.mockStatic;
10+import static org.mockito.Mockito.when;
11+ 
12+import java.util.List;
13+import java.util.Map;
14+ 
15+import org.junit.jupiter.api.BeforeEach;
16+import org.junit.jupiter.api.Test;
17+import org.junit.jupiter.api.extension.ExtendWith;
18+import org.mockito.InjectMocks;
19+import org.mockito.Mock;
20+import org.mockito.MockedStatic;
21+import org.mockito.junit.jupiter.MockitoExtension;
22+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
23+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
24+import org.opengauss.admin.system.plugin.facade.HostFacade;
25+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
26+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
27+ 
28+import com.alibaba.fastjson.JSONObject;
29+import com.nctigba.observability.instance.mapper.DbConfigMapper;
30+import com.nctigba.observability.instance.service.ClusterManager;
31+import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
32+import com.nctigba.observability.instance.service.MetricsService;
33+import com.nctigba.observability.instance.service.SessionService;
34+import com.nctigba.observability.instance.service.TopSQLService;
35+import com.nctigba.observability.instance.util.SshSession;
36+ 
37+/**
38+ * IndexControllerTest.java
39+ *
40+ * @since 2023717
41+ */
42+@ExtendWith(MockitoExtension.class)
43+class IndexControllerTest {
44+ @InjectMocks
45+ private IndexController indexController;
46+ @Mock
47+ private MetricsService metricsService;
48+ @Mock
49+ private TopSQLService topSQLService;
50+ @Mock
51+ private SessionService sessionService;
52+ @Mock
53+ private SshSession sshSession;
54+ @Mock
55+ private DbConfigMapper configMapper;
56+ @Mock
57+ private ClusterManager clusterManager;
58+ @Mock
59+ private HostFacade hostFacade;
60+ @Mock
61+ private HostUserFacade hostUserFacade;
62+ @Mock
63+ private EncryptionUtils encryptionUtils;
64+ 
65+ @BeforeEach
66+ void setup() {
67+ when(sessionService.simpleStatistic(anyString())).thenReturn(new JSONObject());
68+ OpsClusterNodeVOSub opsClusterNodeVOSub = new OpsClusterNodeVOSub();
69+ opsClusterNodeVOSub.setHostId("id");
70+ opsClusterNodeVOSub.setInstallUserName("name");
71+ when(clusterManager.getOpsNodeById(anyString())).thenReturn(opsClusterNodeVOSub);
72+ when(configMapper.env()).thenReturn(Map.of("datapath", "", "log_directory", ""));
73+ var host = new OpsHostEntity();
74+ host.setHostId("");
75+ host.setPublicIp("");
76+ host.setPort(11);
77+ when(hostFacade.getById(anyString())).thenReturn(host);
78+ OpsHostUserEntity e1 = new OpsHostUserEntity();
79+ e1.setUsername("name");
80+ e1.setPassword("");
81+ when(hostUserFacade.listHostUserByHostId(anyString())).thenReturn(List.of(e1));
82+ when(encryptionUtils.decrypt(anyString())).thenReturn("pwd");
83+ }
84+ 
85+ @Test
86+ void test() {
87+ indexController.mainMetrics("id", null, null, null);
88+ indexController.topSQLNow("id");
89+ try (MockedStatic<SshSession> mockStatic = mockStatic(SshSession.class);) {
90+ mockStatic.when(() -> SshSession.connect(anyString(), anyInt(), anyString(), anyString()))
91+ .thenReturn(sshSession);
92+ indexController.nodeInfo("id");
93+ }
94+ }
95+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/controller/PageControllerTest.java+73-0
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.controller;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.Mockito.when;
9+ 
10+import java.util.HashMap;
11+import java.util.List;
12+import java.util.Map;
13+ 
14+import org.junit.jupiter.api.BeforeEach;
15+import org.junit.jupiter.api.Test;
16+import org.junit.jupiter.api.extension.ExtendWith;
17+import org.mockito.InjectMocks;
18+import org.mockito.Mock;
19+import org.mockito.junit.jupiter.MockitoExtension;
20+import org.springframework.context.MessageSource;
21+ 
22+import com.nctigba.observability.instance.constants.MetricsLine;
23+import com.nctigba.observability.instance.constants.MetricsValue;
24+import com.nctigba.observability.instance.mapper.DbConfigMapper;
25+import com.nctigba.observability.instance.service.ClusterManager;
26+import com.nctigba.observability.instance.service.MetricsService;
27+import com.nctigba.observability.instance.util.Language;
28+ 
29+/**
30+ * PageControllerTest.java
31+ *
32+ * @since 2023717
33+ */
34+@ExtendWith(MockitoExtension.class)
35+class PageControllerTest {
36+ @InjectMocks
37+ private PageController controller;
38+ @Mock
39+ private MetricsService metricsService;
40+ @Mock
41+ private DbConfigMapper dbConfigMapper;
42+ @Mock
43+ private ClusterManager clusterManager;
44+ @Mock
45+ private MessageSource messageSource;
46+ @Mock
47+ private Language language;
48+ 
49+ @BeforeEach
50+ void setUp() throws Exception {
51+ HashMap<String, Object> result = new HashMap<>();
52+ result.put("IO_TPS", Map.of("device", "test"));
53+ result.put("NETWORK_TX", Map.of("device", "test"));
54+ result.put(MetricsValue.MEM_TOTAL.name(), "1");
55+ result.put(MetricsLine.MEMORY_DB_USED.name(), List.of(1, 2, 3));
56+ when(metricsService.listBatch(any(), any(), any(), any(), any())).thenReturn(result);
57+ when(messageSource.getMessage(any(), any(), any(), any())).thenReturn("any");
58+ var config = new HashMap<String, Object>();
59+ config.put("memorytype", "test");
60+ config.put("name", "test");
61+ when(dbConfigMapper.memoryConfig()).thenReturn(List.of(config));
62+ when(dbConfigMapper.memoryNodeDetail()).thenReturn(List.of(config));
63+ }
64+ 
65+ @Test
66+ void test() {
67+ controller.memory("id", null, null, null);
68+ controller.io("id", null, null, null);
69+ controller.network("id", null, null, null);
70+ controller.instance("id", null, null, null);
71+ controller.waitEvent("id", null, null, null);
72+ }
73+}
Dplugins/observability-instance/src/test/java/com/nctigba/observability/instance/controller/SessionControllerTest.java+0-63
@@ -1,63 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.controller;
6- 
7-import static org.junit.jupiter.api.Assertions.assertNotNull;
8-import static org.mockito.ArgumentMatchers.any;
9-import static org.mockito.Mockito.times;
10-import static org.mockito.Mockito.verify;
11-import static org.mockito.Mockito.when;
12- 
13-import java.util.HashMap;
14-import java.util.List;
15- 
16-import org.junit.jupiter.api.Test;
17-import org.junit.jupiter.api.extension.ExtendWith;
18-import org.mockito.InjectMocks;
19-import org.mockito.Mock;
20-import org.mockito.junit.jupiter.MockitoExtension;
21-import org.opengauss.admin.common.core.domain.AjaxResult;
22- 
23-import com.alibaba.fastjson.JSONObject;
24-import com.nctigba.observability.instance.service.MetricsService;
25-import com.nctigba.observability.instance.service.impl.MonitoringServiceImpl;
26-import com.nctigba.observability.instance.service.impl.SessionServiceImpl;
27- 
28-/**
29- * SessionServiceImplTest.java
30- *
31- * @author liupengfei
32- * @since 2023/6/30
33- */
34-@ExtendWith(MockitoExtension.class)
35-class SessionControllerTest {
36- @InjectMocks
37- SessionController sessionController;
38- @Mock
39- SessionServiceImpl sessionService;
40- @Mock
41- MetricsService metricsService;
42- @Mock
43- MonitoringServiceImpl monitoringService;
44- 
45- @Test
46- public void sessionStatistic() {
47- String id = "123";
48- Long start = 111L;
49- Long end = 222L;
50- Integer step = 15;
51- when(monitoringService.getRangeMonitoringData(any())).thenReturn(List.of("waitEven"));
52- JSONObject jsonObject = new JSONObject();
53- jsonObject.put("simple", "val");
54- when(sessionService.simpleStatistic(id)).thenReturn(jsonObject);
55- HashMap<String, Object> metric = new HashMap<>();
56- metric.put("metric", "val");
57- when(metricsService.listBatch(any(), any(), any(), any(), any())).thenReturn(metric);
58- 
59- var appResult = sessionController.sessionStatistic(id, start, end, step);
60- assertNotNull(appResult.getOrDefault(AjaxResult.DATA_TAG, null));
61- verify(sessionService, times(1)).simpleStatistic(any());
62- }
63-}
Dplugins/observability-instance/src/test/java/com/nctigba/observability/instance/handler/session/OpenGaussSessionHandlerTest.java+0-183
@@ -1,183 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.handler.session;
6- 
7-import com.alibaba.fastjson.JSONObject;
8-import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
9-import com.nctigba.observability.instance.model.InstanceNodeInfo;
10-import com.nctigba.observability.instance.service.ClusterManager;
11-import org.junit.jupiter.api.BeforeEach;
12-import org.junit.jupiter.api.Test;
13-import org.junit.jupiter.api.extension.ExtendWith;
14-import org.mockito.InjectMocks;
15-import org.mockito.Mock;
16-import org.mockito.junit.jupiter.MockitoExtension;
17-import org.mockito.junit.jupiter.MockitoSettings;
18-import org.mockito.quality.Strictness;
19- 
20-import java.sql.Connection;
21-import java.sql.PreparedStatement;
22-import java.sql.ResultSet;
23-import java.sql.ResultSetMetaData;
24-import java.sql.SQLException;
25-import java.util.List;
26- 
27-import static org.junit.jupiter.api.Assertions.assertEquals;
28-import static org.junit.jupiter.api.Assertions.assertNotNull;
29-import static org.junit.jupiter.api.Assertions.assertThrows;
30-import static org.mockito.ArgumentMatchers.anyString;
31-import static org.mockito.ArgumentMatchers.eq;
32-import static org.mockito.Mockito.times;
33-import static org.mockito.Mockito.verify;
34-import static org.mockito.Mockito.when;
35- 
36-/**
37- * OpenGaussSessionHandlerTest.java
38- *
39- * @since 2023717
40- */
41-@ExtendWith(MockitoExtension.class)
42-@MockitoSettings(strictness = Strictness.LENIENT)
43-class OpenGaussSessionHandlerTest {
44- @InjectMocks
45- private OpenGaussSessionHandler sessionHandler;
46- 
47- @Mock
48- private Connection mockConnection;
49- 
50- @Mock
51- private PreparedStatement mockPreparedStatement;
52- 
53- @Mock
54- private ResultSet mockResultSet;
55- 
56- @Mock
57- private ResultSetMetaData mockResultSetMetaData;
58- 
59- @Mock
60- private ClusterManager clusterManager;
61- 
62- @BeforeEach
63- public void setUp() throws Exception {
64- when(mockConnection.prepareStatement(anyString())).thenReturn(mockPreparedStatement);
65- when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet);
66- when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet);
67- }
68- 
69- @Test
70- public void testDetailGeneral() throws SQLException {
71- default1Row3ColumnMockResult();
72- when(mockResultSet.next()).thenReturn(true, false, true, false, true, false);
73- JSONObject detailGeneral = sessionHandler.detailGeneral(mockConnection, "123");
74- assertNotNull(detailGeneral);
75- assertEquals(3, detailGeneral.size());
76- verify(mockPreparedStatement, times(3)).executeQuery();
77- }
78- 
79- private void default1Row3ColumnMockResult() throws SQLException {
80- when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData);
81- when(mockResultSetMetaData.getColumnCount()).thenReturn(3);
82- when(mockResultSetMetaData.getColumnLabel(eq(1))).thenReturn("key1");
83- when(mockResultSetMetaData.getColumnLabel(eq(2))).thenReturn("key2");
84- when(mockResultSetMetaData.getColumnLabel(eq(3))).thenReturn("count");
85- when(mockResultSet.getString(eq(1))).thenReturn("value1");
86- when(mockResultSet.getString(eq(2))).thenReturn("value2");
87- when(mockResultSet.getString(eq(3))).thenReturn("3");
88- }
89- 
90- @Test
91- public void testDetailGeneralException() throws SQLException {
92- String sessionId = "123";
93- when(mockPreparedStatement.executeQuery()).thenThrow(new SQLException("Query error"));
94- assertThrows(Exception.class, () -> {
95- sessionHandler.detailGeneral(mockConnection, sessionId);
96- });
97- }
98- 
99- @Test
100- public void testDetailStatistic() throws SQLException {
101- String sessionId = "123";
102- when(mockResultSet.next()).thenReturn(true, false, true, false);
103- when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData);
104- when(mockResultSetMetaData.getColumnCount()).thenReturn(3);
105- when(mockResultSetMetaData.getColumnLabel(eq(1))).thenReturn("stat_name");
106- when(mockResultSetMetaData.getColumnLabel(eq(2))).thenReturn("statname");
107- when(mockResultSetMetaData.getColumnLabel(eq(3))).thenReturn("value");
108- when(mockResultSet.getString(eq(1))).thenReturn("stat_name_1");
109- when(mockResultSet.getString(eq(2))).thenReturn("statname_2");
110- when(mockResultSet.getString(eq(3))).thenReturn("value");
111- List<DetailStatisticDto> statistic = sessionHandler.detailStatistic(mockConnection, sessionId);
112- assertNotNull(statistic);
113- assertEquals(2, statistic.size());
114- verify(mockPreparedStatement, times(2)).executeQuery();
115- }
116- 
117- @Test
118- public void testDetailWaiting() throws SQLException {
119- String sessionId = "123";
120- default1Row3ColumnMockResult();
121- when(mockResultSet.next()).thenReturn(true, true, true, false);
122- List<JSONObject> waiting = sessionHandler.detailWaiting(mockConnection, sessionId);
123- assertNotNull(waiting);
124- }
125- 
126- @Test
127- public void testDetailBlockTree() throws SQLException {
128- when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData);
129- when(mockResultSetMetaData.getColumnCount()).thenReturn(4);
130- when(mockResultSetMetaData.getColumnLabel(eq(1))).thenReturn("id");
131- when(mockResultSetMetaData.getColumnLabel(eq(2))).thenReturn("pathid");
132- when(mockResultSetMetaData.getColumnLabel(eq(3))).thenReturn("parentid");
133- when(mockResultSetMetaData.getColumnLabel(eq(4))).thenReturn("tree_id");
134- when(mockResultSet.getString(eq(1))).thenReturn("1").thenReturn("2").thenReturn("3");
135- when(mockResultSet.getString(eq(2))).thenReturn("/1").thenReturn("/1/2").thenReturn("/1/2/3");
136- when(mockResultSet.getString(eq(3))).thenReturn("0").thenReturn("1").thenReturn("2");
137- when(mockResultSet.getString(eq(4))).thenReturn("1");
138- when(mockResultSet.next()).thenReturn(true, true, true, false);
139- List<JSONObject> blockTree = sessionHandler.detailBlockTree(mockConnection, "1");
140- assertNotNull(blockTree);
141- assertEquals(1, blockTree.size());
142- assertEquals("1", blockTree.get(0).get("id"));
143- verify(mockPreparedStatement, times(1)).executeQuery();
144- }
145- 
146- @Test
147- public void testSimpleStatistic() throws SQLException {
148- default1Row3ColumnMockResult();
149- when(mockResultSet.next()).thenReturn(true, true, true, false);
150- JSONObject statistic = sessionHandler.simpleStatistic(mockConnection);
151- assertNotNull(statistic);
152- assertEquals(3, statistic.size());
153- verify(mockPreparedStatement, times(1)).executeQuery();
154- }
155- 
156- @Test
157- public void testLongTxc() throws SQLException {
158- default1Row3ColumnMockResult();
159- when(mockResultSet.next()).thenReturn(true, true, true, false);
160- List<JSONObject> longTxc = sessionHandler.longTxc(mockConnection);
161- assertNotNull(longTxc);
162- assertEquals(3, longTxc.get(0).size());
163- verify(mockPreparedStatement, times(1)).executeQuery();
164- }
165- 
166- @Test
167- public void testGetConnection() {
168- InstanceNodeInfo nodeInfo = new InstanceNodeInfo();
169- nodeInfo.setIp("127.0.0.0");
170- nodeInfo.setPort(8080);
171- nodeInfo.setDbName("dbNameTest");
172- when(clusterManager.getConnectionByNodeInfo(nodeInfo)).thenReturn(mockConnection);
173- Connection connection = sessionHandler.getConnection(nodeInfo);
174- assertEquals(mockConnection, connection);
175- }
176- 
177- @Test
178- public void testClose() throws SQLException {
179- sessionHandler.close(mockConnection);
180- sessionHandler.getDatabaseType();
181- verify(mockConnection, times(1)).close();
182- }
183-}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/listener/PluginListenerTest.java+62-0
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.listener;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyString;
10+import static org.mockito.Mockito.when;
11+ 
12+import org.junit.jupiter.api.Test;
13+import org.junit.jupiter.api.extension.ExtendWith;
14+import org.mockito.InjectMocks;
15+import org.mockito.Mock;
16+import org.mockito.junit.jupiter.MockitoExtension;
17+import org.opengauss.admin.common.core.vo.MenuVo;
18+import org.opengauss.admin.system.plugin.facade.MenuFacade;
19+import org.springframework.boot.context.event.ApplicationReadyEvent;
20+import org.springframework.context.ConfigurableApplicationContext;
21+import org.springframework.context.event.ContextClosedEvent;
22+ 
23+import com.gitee.starblues.spring.MainApplicationContext;
24+import com.gitee.starblues.spring.SpringBeanFactory;
25+ 
26+/**
27+ * PluginListenerTest.java
28+ *
29+ * @since 2023717
30+ */
31+@ExtendWith(MockitoExtension.class)
32+class PluginListenerTest {
33+ @InjectMocks
34+ private PluginListener pluginListener;
35+ @Mock
36+ private ApplicationReadyEvent readyEvent;
37+ @Mock
38+ private ConfigurableApplicationContext applicationContext;
39+ @Mock
40+ private MainApplicationContext mainApplicationContext;
41+ @Mock
42+ private SpringBeanFactory beanFactory;
43+ @Mock
44+ private MenuFacade menuFacade;
45+ @Mock
46+ private ContextClosedEvent closedEvent;
47+ 
48+ @SuppressWarnings("unchecked")
49+ @Test
50+ void test() {
51+ when(readyEvent.getApplicationContext()).thenReturn(applicationContext);
52+ when(applicationContext.getBean(any(Class.class))).thenReturn(mainApplicationContext);
53+ when(mainApplicationContext.getSpringBeanFactory()).thenReturn(beanFactory);
54+ when(beanFactory.getBean(MenuFacade.class)).thenReturn(menuFacade);
55+ var vo = new MenuVo();
56+ vo.setMenuId(1);
57+ when(menuFacade.savePluginMenu(anyString(), anyString(), anyString(), anyInt(), anyString())).thenReturn(vo);
58+ pluginListener.onApplicationEvent(readyEvent);
59+ when(closedEvent.getApplicationContext()).thenReturn(applicationContext);
60+ pluginListener.onApplicationEvent(closedEvent);
61+ }
62+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/mapper/ParamInfoMapperTest.java+65-0
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertThrows;
8+import static org.mockito.ArgumentMatchers.anyString;
9+import static org.mockito.Mockito.mockStatic;
10+import static org.mockito.Mockito.reset;
11+import static org.mockito.Mockito.when;
12+ 
13+import java.sql.Connection;
14+import java.sql.ResultSet;
15+import java.sql.SQLException;
16+import java.sql.Statement;
17+ 
18+import org.junit.jupiter.api.Test;
19+import org.junit.jupiter.api.extension.ExtendWith;
20+import org.mockito.InjectMocks;
21+import org.mockito.Mock;
22+import org.mockito.MockedStatic;
23+import org.mockito.junit.jupiter.MockitoExtension;
24+ 
25+import com.nctigba.observability.instance.config.ParamInfoInitConfig;
26+import com.nctigba.observability.instance.entity.ParamInfo.ParamType;
27+ 
28+/**
29+ * ParamInfoMapperTest.java
30+ *
31+ * @since 2023717
32+ */
33+@ExtendWith(MockitoExtension.class)
34+class ParamInfoMapperTest {
35+ @InjectMocks
36+ private ParamInfoMapper paramInfoMapper;
37+ @Mock
38+ private Connection connection;
39+ @Mock
40+ private Statement statement;
41+ @Mock
42+ private ResultSet resultSet;
43+ 
44+ @Test
45+ void test() throws Exception {
46+ try (MockedStatic<ParamInfoInitConfig> mockedStatic = mockStatic(ParamInfoInitConfig.class)) {
47+ when(resultSet.next()).thenReturn(true, true, false);
48+ when(resultSet.getInt(anyString())).thenReturn(1, 2, 3);
49+ when(resultSet.getString(anyString())).thenReturn("OS");
50+ when(statement.executeQuery(anyString())).thenReturn(resultSet);
51+ when(connection.createStatement()).thenReturn(statement);
52+ mockedStatic.when(() -> {
53+ ParamInfoInitConfig.getCon(anyString());
54+ }).thenReturn(connection);
55+ paramInfoMapper.afterPropertiesSet();
56+ ParamInfoMapper.getAll();
57+ ParamInfoMapper.getById(1);
58+ ParamInfoMapper.getIds(ParamType.OS);
59+ ParamInfoMapper.getParamInfo(ParamType.OS, "OS");
60+ reset(connection);
61+ when(connection.createStatement()).thenThrow(SQLException.class);
62+ assertThrows(RuntimeException.class, () -> paramInfoMapper.afterPropertiesSet());
63+ }
64+ }
65+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/mapper/ParamValueInfoMapperTest.java+77-0
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.mapper;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertThrows;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyString;
10+import static org.mockito.Mockito.mockStatic;
11+import static org.mockito.Mockito.reset;
12+import static org.mockito.Mockito.when;
13+ 
14+import java.sql.Connection;
15+import java.sql.PreparedStatement;
16+import java.sql.ResultSet;
17+import java.sql.SQLException;
18+import java.sql.Statement;
19+import java.util.List;
20+ 
21+import org.junit.jupiter.api.Test;
22+import org.junit.jupiter.api.extension.ExtendWith;
23+import org.mockito.InjectMocks;
24+import org.mockito.Mock;
25+import org.mockito.MockedStatic;
26+import org.mockito.junit.jupiter.MockitoExtension;
27+ 
28+import com.nctigba.observability.instance.config.ParamInfoInitConfig;
29+import com.nctigba.observability.instance.entity.ParamValueInfo;
30+ 
31+/**
32+ * ParamValueInfoMapperTest.java
33+ *
34+ * @since 2023717
35+ */
36+@ExtendWith(MockitoExtension.class)
37+class ParamValueInfoMapperTest {
38+ @InjectMocks
39+ private ParamValueInfoMapper paramValueInfoMapper;
40+ @Mock
41+ private Connection connection;
42+ @Mock
43+ private Statement statement;
44+ @Mock
45+ private PreparedStatement pstatement;
46+ @Mock
47+ private ResultSet resultSet;
48+ 
49+ @Test
50+ void test() throws SQLException {
51+ try (MockedStatic<ParamInfoInitConfig> mockedStatic = mockStatic(ParamInfoInitConfig.class)) {
52+ when(resultSet.next()).thenReturn(true, true, false);
53+ when(resultSet.getInt(anyInt())).thenReturn(1, 2, 3);
54+ when(resultSet.getString(anyInt())).thenReturn("OS");
55+ when(pstatement.executeQuery()).thenReturn(resultSet);
56+ when(connection.createStatement()).thenReturn(statement);
57+ when(connection.prepareStatement(anyString())).thenReturn(pstatement);
58+ mockedStatic.when(() -> ParamInfoInitConfig.getCon(anyString())).thenReturn(connection);
59+ 
60+ paramValueInfoMapper.query("");
61+ paramValueInfoMapper.refresh(null);
62+ ParamValueInfoMapper.insert(new ParamValueInfo(1, "", ""));
63+ ParamValueInfoMapper.insertBatch(List.of(new ParamValueInfo(1, "", ""), new ParamValueInfo(1, "", "")));
64+ ParamValueInfoMapper.delBySids(List.of(1, 2, 3));
65+ ParamValueInfoMapper.delBySids(List.of());
66+ ParamValueInfoMapper.selectByInstanceId("");
67+ reset(connection);
68+ when(connection.createStatement()).thenThrow(SQLException.class);
69+ when(connection.prepareStatement(anyString())).thenThrow(SQLException.class);
70+ assertThrows(RuntimeException.class, () -> ParamValueInfoMapper.selectByInstanceId(""));
71+ assertThrows(RuntimeException.class, () -> ParamValueInfoMapper.insert(new ParamValueInfo(1, "", "")));
72+ assertThrows(RuntimeException.class, () -> ParamValueInfoMapper
73+ .insertBatch(List.of(new ParamValueInfo(1, "", ""), new ParamValueInfo(1, "", ""))));
74+ assertThrows(RuntimeException.class, () -> ParamValueInfoMapper.delBySids(List.of(1, 2, 3)));
75+ }
76+ }
77+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/ClusterManagerTest.java+79-0
@@ -0,0 +1,79 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyString;
9+import static org.mockito.Mockito.mockStatic;
10+import static org.mockito.Mockito.when;
11+ 
12+import java.sql.DriverManager;
13+import java.util.List;
14+ 
15+import org.junit.jupiter.api.BeforeEach;
16+import org.junit.jupiter.api.Test;
17+import org.junit.jupiter.api.extension.ExtendWith;
18+import org.mockito.InjectMocks;
19+import org.mockito.Mock;
20+import org.mockito.MockedStatic;
21+import org.mockito.junit.jupiter.MockitoExtension;
22+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
23+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
24+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
25+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
26+import org.opengauss.admin.system.plugin.facade.HostFacade;
27+import org.opengauss.admin.system.plugin.facade.OpsFacade;
28+import org.opengauss.admin.system.service.ops.IOpsClusterService;
29+ 
30+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
31+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
32+ 
33+/**
34+ * ClusterManagerTest.java
35+ *
36+ * @since 2023717
37+ */
38+@ExtendWith(MockitoExtension.class)
39+class ClusterManagerTest {
40+ @InjectMocks
41+ private ClusterManager clusterManager;
42+ @Mock
43+ private DynamicRoutingDataSource dataSource;
44+ @Mock
45+ private DefaultDataSourceCreator dataSourceCreator;
46+ @Mock
47+ private OpsFacade opsFacade;
48+ @Mock
49+ private HostFacade hostFacade;
50+ @Mock
51+ private IOpsClusterService opsClusterService;
52+ 
53+ @BeforeEach
54+ void setup() {
55+ OpsClusterNodeVO node = new OpsClusterNodeVO();
56+ node.setNodeId("id");
57+ OpsClusterVO cluster = new OpsClusterVO();
58+ cluster.setClusterId("id");
59+ cluster.setClusterNodes(List.of(node));
60+ when(opsFacade.listCluster()).thenReturn(List.of(cluster));
61+ 
62+ when(hostFacade.getById(anyString())).thenReturn(new OpsHostEntity());
63+ OpsClusterEntity opsCluster = new OpsClusterEntity();
64+ opsCluster.setDatabaseUsername("");
65+ opsCluster.setDatabasePassword("");
66+ when(opsClusterService.getById(any())).thenReturn(opsCluster);
67+ }
68+ 
69+ @Test
70+ void test() {
71+ clusterManager.setCurrentDatasource("id", null);
72+ clusterManager.pool();
73+ clusterManager.setCurrentDatasource(null, null);
74+ try (MockedStatic<DriverManager> mockStatic = mockStatic(DriverManager.class)) {
75+ mockStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(null);
76+ clusterManager.getConnectionByClusterHost("id", "id");
77+ }
78+ }
79+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/ExporterInstallServiceTest.java+117-0
@@ -0,0 +1,117 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyMap;
10+import static org.mockito.ArgumentMatchers.anyString;
11+import static org.mockito.Mockito.mockStatic;
12+import static org.mockito.Mockito.when;
13+ 
14+import java.io.File;
15+import java.io.IOException;
16+import java.util.List;
17+ 
18+import org.junit.jupiter.api.BeforeEach;
19+import org.junit.jupiter.api.Test;
20+import org.junit.jupiter.api.extension.ExtendWith;
21+import org.mockito.InjectMocks;
22+import org.mockito.Mock;
23+import org.mockito.MockedStatic;
24+import org.mockito.junit.jupiter.MockitoExtension;
25+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
26+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
27+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
28+import org.opengauss.admin.common.utils.ops.WsUtil;
29+import org.opengauss.admin.system.plugin.facade.HostFacade;
30+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
31+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
32+import org.springframework.core.io.FileSystemResource;
33+import org.springframework.core.io.ResourceLoader;
34+ 
35+import com.nctigba.observability.instance.entity.NctigbaEnv;
36+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
37+import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
38+import com.nctigba.observability.instance.util.MessageSourceUtil;
39+import com.nctigba.observability.instance.util.SshSession;
40+ 
41+import cn.hutool.http.HttpUtil;
42+ 
43+/**
44+ * ExporterInstallServiceTest.java
45+ *
46+ * @since 2023717
47+ */
48+@ExtendWith(MockitoExtension.class)
49+class ExporterInstallServiceTest {
50+ @InjectMocks
51+ private ExporterInstallService exporterInstallService;
52+ @Mock
53+ private HostFacade hostFacade;
54+ @Mock
55+ private EncryptionUtils encryptionUtils;
56+ @Mock
57+ private HostUserFacade hostUserFacade;
58+ @Mock
59+ private NctigbaEnvMapper envMapper;
60+ @Mock
61+ private WsUtil wsUtil;
62+ @Mock
63+ private ClusterManager clusterManager;
64+ @Mock
65+ private ResourceLoader loader;
66+ @Mock
67+ private SshSession sshSession;
68+ 
69+ @BeforeEach
70+ void setup() throws IOException {
71+ OpsClusterNodeVOSub node = new OpsClusterNodeVOSub();
72+ node.setNodeId("id");
73+ node.setInstallUserName("name");
74+ node.setDbUser("name");
75+ node.setDbUserPassword("password");
76+ node.setDbPort(123);
77+ node.setHostId("id");
78+ when(clusterManager.getOpsNodeById(anyString())).thenReturn(node);
79+ 
80+ OpsHostEntity host = new OpsHostEntity();
81+ host.setHostId("id");
82+ host.setPublicIp("123");
83+ host.setPort(123);
84+ when(hostFacade.getById(any())).thenReturn(host);
85+ when(loader.getResource(anyString())).thenReturn(new FileSystemResource(File.createTempFile("tmp", "tmp")));
86+ 
87+ OpsHostUserEntity user = new OpsHostUserEntity();
88+ user.setUsername("name");
89+ user.setPassword("");
90+ when(hostUserFacade.listHostUserByHostId(anyString())).thenReturn(List.of(user));
91+ NctigbaEnv nul = null;
92+ when(envMapper.selectOne(any()))
93+ .thenReturn(new NctigbaEnv().setPath("11").setUsername("name").setHostid("host"), nul);
94+ when(encryptionUtils.decrypt(anyString())).thenReturn("");
95+ when(sshSession.test(anyString())).thenReturn(false);
96+ }
97+ 
98+ @Test
99+ void test() throws IOException {
100+ try (MockedStatic<SshSession> mockStatic = mockStatic(SshSession.class);
101+ MockedStatic<MessageSourceUtil> msg = mockStatic(MessageSourceUtil.class);
102+ MockedStatic<HttpUtil> util = mockStatic(HttpUtil.class)) {
103+ when(sshSession.execute(anyString())).thenReturn("scrape_configs:" + System.lineSeparator()
104+ + "- scheme: http" + System.lineSeparator() + "" + " job_name: prometheus" + System.lineSeparator()
105+ + " static_configs:" + System.lineSeparator() + " - targets:" + System.lineSeparator()
106+ + " - localhost:9090");
107+ mockStatic.when(() -> SshSession.connect(anyString(), anyInt(), anyString(), anyString()))
108+ .thenReturn(sshSession);
109+ msg.when(() -> MessageSourceUtil.get(anyString())).thenReturn("");
110+ util.when(() -> HttpUtil.get(anyString(), anyMap())).thenReturn(null);
111+ util.when(() -> HttpUtil.post(anyString(), anyString())).thenReturn(null);
112+ exporterInstallService.install(new WsSession(null, "id"), "id", "", 9999, 9998);
113+ 
114+ exporterInstallService.uninstall(new WsSession(null, "id"), "id");
115+ }
116+ }
117+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/MetricsServiceTest.java+105-0
@@ -0,0 +1,105 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyMap;
10+import static org.mockito.ArgumentMatchers.anyString;
11+import static org.mockito.Mockito.mockStatic;
12+import static org.mockito.Mockito.when;
13+ 
14+import java.lang.reflect.InvocationTargetException;
15+import java.util.concurrent.CountDownLatch;
16+ 
17+import org.junit.jupiter.api.BeforeEach;
18+import org.junit.jupiter.api.Test;
19+import org.junit.jupiter.api.extension.ExtendWith;
20+import org.mockito.InjectMocks;
21+import org.mockito.Mock;
22+import org.mockito.MockedStatic;
23+import org.mockito.junit.jupiter.MockitoExtension;
24+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
25+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
26+import org.opengauss.admin.system.plugin.facade.HostFacade;
27+ 
28+import com.nctigba.observability.instance.constants.MetricsLine;
29+import com.nctigba.observability.instance.constants.MetricsValue;
30+import com.nctigba.observability.instance.entity.NctigbaEnv;
31+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
32+import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
33+ 
34+import cn.hutool.core.thread.ThreadUtil;
35+import cn.hutool.http.HttpUtil;
36+ 
37+/**
38+ * MetricsServiceTest.java
39+ *
40+ * @since 2023717
41+ */
42+@ExtendWith(MockitoExtension.class)
43+class MetricsServiceTest {
44+ private static final Enum<?>[] METRICS = {
45+ MetricsLine.CPU,
46+ MetricsValue.SWAP_FREE
47+ };
48+ 
49+ @InjectMocks
50+ private MetricsService metricsService;
51+ @Mock
52+ private NctigbaEnvMapper envMapper;
53+ @Mock
54+ private HostFacade hostFacade;
55+ @Mock
56+ private ClusterManager clusterManager;
57+ 
58+ @BeforeEach
59+ void setup() {
60+ var host = new OpsHostEntity();
61+ host.setPublicIp("");
62+ when(hostFacade.getById(anyString())).thenReturn(host);
63+ 
64+ OpsClusterNodeVOSub node = new OpsClusterNodeVOSub();
65+ node.setNodeId("id");
66+ node.setInstallUserName("name");
67+ node.setDbUser("name");
68+ node.setDbUserPassword("password");
69+ node.setDbPort(123);
70+ node.setHostId("id");
71+ when(clusterManager.getOpsNodeById(anyString())).thenReturn(node);
72+ when(envMapper.selectOne(any())).thenReturn(new NctigbaEnv().setPort(1).setHostid("host"));
73+ }
74+ 
75+ @Test
76+ void test() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
77+ InvocationTargetException {
78+ try (MockedStatic<HttpUtil> http = mockStatic(HttpUtil.class);
79+ MockedStatic<ThreadUtil> thread = mockStatic(ThreadUtil.class)) {
80+ http.when(() -> HttpUtil.get(any(), anyMap())).thenAnswer(invocation -> {
81+ var url = invocation.getArgument(0);
82+ if ("http://:1/api/v1/query".equals(url)) {
83+ return "{'status':'success','data':{'resultType':'vector','result':[{'metric':{'__name__':'agent_f"
84+ + "ree_Swap_free_bytes','type':'exporter'},'value':[1689294857.390,'5823524864']}]}}";
85+ }
86+ return "{'status':'success','data':{'resultType':'matrix','result':[{'metric':{},"
87+ + "'values':[[1689294785.463,'13.90762676073958']]}]}}";
88+ });
89+ metricsService.list("1", 1, 1, 1);
90+ metricsService.value("1", 1L);
91+ when(ThreadUtil.newCountDownLatch(anyInt())).thenReturn(new CountDownLatch(0));
92+ metricsService.listBatch(METRICS, "id", 1L, 1L, 1);
93+ 
94+ var extra = metricsService.getClass().getDeclaredMethod("extracted", OpsClusterNodeVO.class, Long.class,
95+ Long.class, Integer.class, Object.class);
96+ extra.setAccessible(true);
97+ OpsClusterNodeVO vo = new OpsClusterNodeVO();
98+ vo.setNodeId("id");
99+ vo.setHostId("id");
100+ for (Enum<?> enum1 : METRICS) {
101+ extra.invoke(metricsService, vo, 1L, 1L, 1, enum1);
102+ }
103+ }
104+ }
105+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/ParamInfoServiceTest.java+81-0
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyString;
10+import static org.mockito.Mockito.mockStatic;
11+import static org.mockito.Mockito.when;
12+ 
13+import java.io.IOException;
14+import java.sql.SQLException;
15+import java.util.List;
16+import java.util.Map;
17+ 
18+import org.junit.jupiter.api.Test;
19+import org.junit.jupiter.api.extension.ExtendWith;
20+import org.mockito.InjectMocks;
21+import org.mockito.Mock;
22+import org.mockito.MockedStatic;
23+import org.mockito.junit.jupiter.MockitoExtension;
24+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
25+ 
26+import com.nctigba.observability.instance.entity.ParamInfo;
27+import com.nctigba.observability.instance.mapper.DbConfigMapper;
28+import com.nctigba.observability.instance.mapper.ParamInfoMapper;
29+import com.nctigba.observability.instance.mapper.ParamValueInfoMapper;
30+import com.nctigba.observability.instance.model.ParamQuery;
31+import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
32+import com.nctigba.observability.instance.util.MessageSourceUtil;
33+import com.nctigba.observability.instance.util.SshSession;
34+ 
35+/**
36+ * ParamInfoServiceTest.java
37+ *
38+ * @since 2023717
39+ */
40+@ExtendWith(MockitoExtension.class)
41+class ParamInfoServiceTest {
42+ @InjectMocks
43+ private ParamInfoService paramInfoService;
44+ @Mock
45+ private ClusterManager opsFacade;
46+ @Mock
47+ private EncryptionUtils encryptionUtils;
48+ @Mock
49+ private ParamValueInfoMapper paramValueInfoMapper;
50+ @Mock
51+ private SshSession sshSession;
52+ @Mock
53+ private DbConfigMapper dbConfigMapper;
54+ 
55+ @Test
56+ void test() throws IOException, SQLException {
57+ when(encryptionUtils.decrypt(anyString())).thenReturn("");
58+ OpsClusterNodeVOSub node = new OpsClusterNodeVOSub();
59+ node.setPublicIp("1");
60+ node.setHostPort(12);
61+ when(opsFacade.getOpsNodeById(anyString())).thenReturn(node);
62+ when(dbConfigMapper.settings()).thenReturn(List.of(Map.of("", "")));
63+ when(sshSession.execute(anyString())).thenReturn("net.ipv6.mld_max_msf = 64" + System.lineSeparator()
64+ + "net.ipv6.mld_qrv = 2" + System.lineSeparator()
65+ + "net.ipv6.neigh.br-728fbf973182.anycast_delay = 100" + System.lineSeparator()
66+ + "net.ipv6.neigh.br-728fbf973182.app_solicit = 0" + System.lineSeparator()
67+ + "net.ipv6.neigh.br-728fbf973182.base_reachable_time_ms = 30000" + System.lineSeparator()
68+ + "net.ipv6.neigh.br-728fbf973182.delay_first_probe_time = 5" + System.lineSeparator());
69+ try (MockedStatic<SshSession> mockStatic = mockStatic(SshSession.class);
70+ MockedStatic<ParamInfoMapper> mapper = mockStatic(ParamInfoMapper.class);
71+ MockedStatic<MessageSourceUtil> util = mockStatic(MessageSourceUtil.class)) {
72+ util.when(() -> MessageSourceUtil.get(anyString())).thenReturn("123");
73+ ParamInfo nul = null;
74+ mapper.when(() -> ParamInfoMapper.getParamInfo(any(), anyString()))
75+ .thenReturn(new ParamInfo().setId(1), nul);
76+ mockStatic.when(() -> SshSession.connect(anyString(), anyInt(), anyString(), anyString()))
77+ .thenReturn(sshSession);
78+ paramInfoService.getParamInfo(new ParamQuery().setIsRefresh("1").setPassword("2").setNodeId("3"));
79+ }
80+ }
81+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/PrometheusServiceTest.java+99-0
@@ -0,0 +1,99 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.mockito.ArgumentMatchers.any;
8+import static org.mockito.ArgumentMatchers.anyInt;
9+import static org.mockito.ArgumentMatchers.anyString;
10+import static org.mockito.Mockito.mockStatic;
11+import static org.mockito.Mockito.when;
12+ 
13+import java.io.File;
14+import java.io.IOException;
15+import java.util.List;
16+ 
17+import org.junit.jupiter.api.BeforeEach;
18+import org.junit.jupiter.api.Test;
19+import org.junit.jupiter.api.extension.ExtendWith;
20+import org.mockito.InjectMocks;
21+import org.mockito.Mock;
22+import org.mockito.MockedStatic;
23+import org.mockito.junit.jupiter.MockitoExtension;
24+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
25+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
26+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
27+import org.opengauss.admin.common.utils.ops.WsUtil;
28+import org.opengauss.admin.system.plugin.facade.HostFacade;
29+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
30+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
31+ 
32+import com.nctigba.observability.instance.entity.NctigbaEnv;
33+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
34+import com.nctigba.observability.instance.util.Download;
35+import com.nctigba.observability.instance.util.MessageSourceUtil;
36+import com.nctigba.observability.instance.util.SshSession;
37+ 
38+import cn.hutool.http.HttpUtil;
39+ 
40+/**
41+ * PrometheusServiceTest.java
42+ *
43+ * @since 2023717
44+ */
45+@ExtendWith(MockitoExtension.class)
46+class PrometheusServiceTest {
47+ @InjectMocks
48+ private PrometheusService prometheusService;
49+ @Mock
50+ private HostFacade hostFacade;
51+ @Mock
52+ private EncryptionUtils encryptionUtils;
53+ @Mock
54+ private HostUserFacade hostUserFacade;
55+ @Mock
56+ private NctigbaEnvMapper envMapper;
57+ @Mock
58+ private WsUtil wsUtil;
59+ @Mock
60+ private SshSession sshSession;
61+ 
62+ @BeforeEach
63+ void setup() throws IOException {
64+ OpsHostEntity host = new OpsHostEntity();
65+ host.setHostId("id");
66+ host.setPublicIp("123");
67+ host.setPort(123);
68+ when(hostFacade.getById(any())).thenReturn(host);
69+ when(envMapper.selectById(any()))
70+ .thenReturn(new NctigbaEnv().setHostid("id").setUsername("name").setPort(9999));
71+ 
72+ OpsHostUserEntity user = new OpsHostUserEntity();
73+ user.setUsername("name");
74+ user.setPassword("");
75+ when(hostUserFacade.listHostUserByHostId(anyString())).thenReturn(List.of(user));
76+ when(encryptionUtils.decrypt(anyString())).thenReturn("");
77+ when(sshSession.test(anyString())).thenReturn(false);
78+ }
79+ 
80+ @Test
81+ void test() throws IOException {
82+ try (MockedStatic<SshSession> mockStatic = mockStatic(SshSession.class);
83+ MockedStatic<MessageSourceUtil> msg = mockStatic(MessageSourceUtil.class);
84+ MockedStatic<HttpUtil> util = mockStatic(HttpUtil.class);
85+ MockedStatic<Download> down = mockStatic(Download.class);) {
86+ when(sshSession.execute(anyString())).thenReturn("scrape_configs:" + System.lineSeparator()
87+ + "- scheme: http" + System.lineSeparator() + " job_name: prometheus" + System.lineSeparator()
88+ + " static_configs:" + System.lineSeparator() + " - targets:" + System.lineSeparator()
89+ + " - localhost:9090");
90+ mockStatic.when(() -> SshSession.connect(anyString(), anyInt(), anyString(), anyString()))
91+ .thenReturn(sshSession);
92+ msg.when(() -> MessageSourceUtil.get(anyString())).thenReturn("");
93+ util.when(() -> HttpUtil.get(anyString())).thenReturn("succ");
94+ down.when(() -> Download.download(anyString(), anyString())).thenReturn(File.createTempFile("test", "tmp"));
95+ prometheusService.install(new WsSession(null, "id"), "id", "", "name", "password", 9998);
96+ prometheusService.uninstall(new WsSession(null, "id"), "id");
97+ }
98+ }
99+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/SessionServiceTest.java+87-0
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertNotNull;
8+import static org.mockito.ArgumentMatchers.anyString;
9+import static org.mockito.Mockito.when;
10+ 
11+import java.util.Collections;
12+import java.util.List;
13+import java.util.Map;
14+ 
15+import org.junit.jupiter.api.Test;
16+import org.junit.jupiter.api.extension.ExtendWith;
17+import org.mockito.InjectMocks;
18+import org.mockito.Mock;
19+import org.mockito.junit.jupiter.MockitoExtension;
20+ 
21+import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
22+import com.nctigba.observability.instance.mapper.SessionMapper;
23+ 
24+/**
25+ * SessionServiceImplTest.java
26+ *
27+ * @since 2023717
28+ */
29+@ExtendWith(MockitoExtension.class)
30+class SessionServiceTest {
31+ @InjectMocks
32+ private SessionService sessionService;
33+ @Mock
34+ private SessionMapper sessionMapper;
35+ 
36+ private static String getId() {
37+ return "12345";
38+ }
39+ 
40+ @Test
41+ public void testDetailGeneral() {
42+ when(sessionMapper.generalMesList(anyString())).thenReturn(List.of(Map.of("1", 1)));
43+ when(sessionMapper.sessionIsWaiting(anyString())).thenReturn(1);
44+ sessionService.detailGeneral(getId(), getId());
45+ }
46+ 
47+ @Test
48+ public void testDetailStatistic() {
49+ when(sessionMapper.statistic(anyString())).thenReturn(Collections.emptyList());
50+ // Act
51+ List<DetailStatisticDto> result = sessionService.detailStatistic(getId(), getId());
52+ // Assert
53+ assertNotNull(result);
54+ }
55+ 
56+ @Test
57+ public void testDetailWaiting() {
58+ sessionService.detailWaiting(getId(), getId());
59+ }
60+ 
61+ @Test
62+ public void testDetailBlockTree() {
63+ sessionService.detailBlockTree(getId(), getId());
64+ }
65+ 
66+ @Test
67+ public void testSimpleStatistic() {
68+ sessionService.simpleStatistic(getId());
69+ }
70+ 
71+ @Test
72+ public void testLongTxc() {
73+ sessionService.longTxc(getId());
74+ }
75+ 
76+ @Test
77+ public void testBlockAndLongTxc() {
78+ sessionService.blockAndLongTxc(getId());
79+ }
80+ 
81+ @Test
82+ public void testDetail() {
83+ when(sessionMapper.generalMesList(anyString())).thenReturn(List.of(Map.of("1", 1)));
84+ when(sessionMapper.sessionIsWaiting(anyString())).thenReturn(1);
85+ sessionService.detail(getId(), getId());
86+ }
87+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/TopSQLServiceTest.java+95-0
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.service;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertEquals;
8+import static org.mockito.ArgumentMatchers.any;
9+import static org.mockito.ArgumentMatchers.anyString;
10+ 
11+import java.util.ArrayList;
12+import java.util.Arrays;
13+import java.util.List;
14+ 
15+import org.junit.jupiter.api.Test;
16+import org.junit.jupiter.api.extension.ExtendWith;
17+import org.mockito.Mock;
18+import org.mockito.Mockito;
19+import org.mockito.junit.jupiter.MockitoExtension;
20+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO;
21+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
22+ 
23+import com.alibaba.fastjson.JSONObject;
24+ 
25+/**
26+ * TopSQLServiceTest.java
27+ *
28+ * @since 2023717
29+ */
30+@ExtendWith(MockitoExtension.class)
31+class TopSQLServiceTest {
32+ @Mock
33+ private ClusterManager clusterManager;
34+ 
35+ @Mock
36+ private TopSQLService topSQLService;
37+ 
38+ @Test
39+ public void testGetCluster() {
40+ Mockito.doReturn(mockClusterList()).when(clusterManager).getAllOpsCluster();
41+ assertEquals(mockClusterList(), clusterManager.getAllOpsCluster());
42+ }
43+ 
44+ // @Test
45+ void testGetTopSQLAction() {
46+ Mockito.doReturn(mockTopSQLList()).when(topSQLService).topSQLList(any());
47+ Mockito.doReturn(mockJsonObject()).when(topSQLService).detail(anyString(), anyString());
48+ Mockito.doReturn(mockJsonObject()).when(topSQLService).executionPlan(anyString(), anyString());
49+ Mockito.doReturn(mockStringList()).when(topSQLService).indexAdvice(anyString(), anyString());
50+ Mockito.doReturn(mockJsonObject()).when(topSQLService).objectInfo(anyString(), anyString());
51+ }
52+ 
53+ private List<OpsClusterVO> mockClusterList() {
54+ OpsClusterNodeVO opsClusterNode = new OpsClusterNodeVO() {
55+ {
56+ setPublicIp("192.168.0.1");
57+ setPrivateIp("192.168.0.1");
58+ setDbPort(15400);
59+ setDbName("postgres");
60+ setDbUser("root");
61+ setDbUserPassword("123");
62+ setAzName("local version");
63+ setAzAddress("192.168.0.1");
64+ setClusterRole("local version");
65+ setNodeId("ffd3dbd2d5a5b920b65441485d949e27");
66+ }
67+ };
68+ OpsClusterVO opsClusterVO = new OpsClusterVO();
69+ opsClusterVO.setClusterId("TestID");
70+ opsClusterVO.setClusterName("TestMASTER");
71+ opsClusterVO.setClusterNodes(Arrays.asList(opsClusterNode));
72+ List<OpsClusterVO> opsClusterVOList = new ArrayList<>();
73+ opsClusterVOList.add(opsClusterVO);
74+ return opsClusterVOList;
75+ }
76+ 
77+ private List<String> mockStringList() {
78+ List<String> stringList = new ArrayList<>();
79+ stringList.add("no data");
80+ return stringList;
81+ }
82+ 
83+ private JSONObject mockJsonObject() {
84+ JSONObject jsonObject = new JSONObject();
85+ jsonObject.put("data", new ArrayList<>());
86+ return jsonObject;
87+ }
88+ 
89+ private List<JSONObject> mockTopSQLList() {
90+ List<JSONObject> jsonObjectList = new ArrayList<>();
91+ jsonObjectList.add(new JSONObject());
92+ jsonObjectList.add(new JSONObject());
93+ return jsonObjectList;
94+ }
95+}
Dplugins/observability-instance/src/test/java/com/nctigba/observability/instance/service/impl/SessionServiceImplTest.java+0-198
@@ -1,198 +0,0 @@
1-/*
2- * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3- */
4- 
5-package com.nctigba.observability.instance.service.impl;
6- 
7-import static org.junit.jupiter.api.Assertions.assertEquals;
8-import static org.junit.jupiter.api.Assertions.assertNotNull;
9-import static org.mockito.ArgumentMatchers.any;
10-import static org.mockito.Mockito.times;
11-import static org.mockito.Mockito.verify;
12-import static org.mockito.Mockito.when;
13- 
14-import java.sql.Connection;
15-import java.util.HashMap;
16-import java.util.List;
17-import java.util.Map;
18- 
19-import org.junit.jupiter.api.BeforeEach;
20-import org.junit.jupiter.api.Test;
21-import org.junit.jupiter.api.extension.ExtendWith;
22-import org.mockito.InjectMocks;
23-import org.mockito.Mock;
24-import org.mockito.junit.jupiter.MockitoExtension;
25- 
26-import com.alibaba.fastjson.JSONObject;
27-import com.nctigba.observability.instance.constants.DatabaseType;
28-import com.nctigba.observability.instance.dto.session.DetailStatisticDto;
29-import com.nctigba.observability.instance.factory.SessionHandlerFactory;
30-import com.nctigba.observability.instance.handler.session.SessionHandler;
31-import com.nctigba.observability.instance.model.InstanceNodeInfo;
32-import com.nctigba.observability.instance.service.ClusterManager;
33- 
34-/**
35- * SessionServiceImplTest.java
36- *
37- * @since 2023717
38- */
39-@ExtendWith(MockitoExtension.class)
40-class SessionServiceImplTest {
41- @InjectMocks
42- private SessionServiceImpl sessionService;
43- @Mock
44- private SessionHandlerFactory sessionHandlerFactory;
45- @Mock
46- private ClusterManager opsFacade;
47- @Mock
48- private SessionHandler sessionHandler;
49- private Connection connection;
50- private InstanceNodeInfo instanceNodeInfo;
51- private ClusterManager.OpsClusterNodeVOSub opsClusterNode;
52- 
53- @BeforeEach
54- public void setUp() {
55- instanceNodeInfo = new InstanceNodeInfo();
56- instanceNodeInfo.setIp("127.0.0.1");
57- instanceNodeInfo.setPort(5432);
58- instanceNodeInfo.setDbName("testDb");
59- instanceNodeInfo.setDbUser("testUser");
60- instanceNodeInfo.setDbUserPassword("testPassword");
61- instanceNodeInfo.setDbType(DatabaseType.DEFAULT.getDbType());
62- opsClusterNode = new ClusterManager.OpsClusterNodeVOSub();
63- opsClusterNode.setPublicIp("127.0.0.1");
64- opsClusterNode.setDbPort(5432);
65- opsClusterNode.setDbName("testDb");
66- opsClusterNode.setDbUser("testUser");
67- opsClusterNode.setDbUserPassword("testPassword");
68- when(opsFacade.getOpsNodeById(any())).thenReturn(opsClusterNode);
69- when(sessionHandlerFactory.getInstance(DatabaseType.DEFAULT.getDbType())).thenReturn(sessionHandler);
70- when(sessionHandler.getConnection(instanceNodeInfo)).thenReturn(connection);
71- }
72- 
73- private static String getId() {
74- return "12345";
75- }
76- 
77- @Test
78- public void testDetailGeneral() {
79- JSONObject expectedResult = new JSONObject();
80- expectedResult.put("key", "value");
81- when(sessionHandler.detailGeneral(connection, getId())).thenReturn(expectedResult);
82- JSONObject result = sessionService.detailGeneral(getId(), getId());
83- assertEquals(expectedResult, result);
84- verify(sessionHandlerFactory, times(1)).getInstance(DatabaseType.DEFAULT.getDbType());
85- verify(sessionHandler, times(1)).getConnection(instanceNodeInfo);
86- verify(sessionHandler, times(1)).detailGeneral(connection, getId());
87- verify(sessionHandler, times(1)).close(connection);
88- }
89- 
90- @Test
91- public void testDetailStatistic() {
92- // Arrange
93- List<DetailStatisticDto> expectedResult = List.of(new DetailStatisticDto());
94- when(sessionHandler.detailStatistic(connection, getId())).thenReturn(expectedResult);
95- // Act
96- List<DetailStatisticDto> result = sessionService.detailStatistic(getId(), getId());
97- // Assert
98- assertNotNull(result);
99- assertEquals(expectedResult, result);
100- verify(sessionHandler, times(1)).detailStatistic(connection, getId());
101- verify(sessionHandler, times(1)).close(connection);
102- }
103- 
104- @Test
105- public void testDetailWaiting() {
106- JSONObject object = new JSONObject();
107- object.put("key", "value");
108- List<JSONObject> expectedResult = List.of(object);
109- when(sessionHandler.detailWaiting(connection, getId())).thenReturn(expectedResult);
110- List<JSONObject> result = sessionService.detailWaiting(getId(), getId());
111- assertEquals(expectedResult, result);
112- verify(sessionHandlerFactory, times(1)).getInstance(DatabaseType.DEFAULT.getDbType());
113- verify(sessionHandler, times(1)).getConnection(instanceNodeInfo);
114- verify(sessionHandler, times(1)).detailWaiting(connection, getId());
115- verify(sessionHandler, times(1)).close(connection);
116- }
117- 
118- @Test
119- public void testDetailBlockTree() {
120- JSONObject object = new JSONObject();
121- object.put("key", "value");
122- List<JSONObject> expectedResult = List.of(object);
123- when(sessionHandler.detailBlockTree(connection, getId())).thenReturn(expectedResult);
124- List<JSONObject> result = sessionService.detailBlockTree(getId(), getId());
125- assertEquals(expectedResult, result);
126- verify(sessionHandlerFactory, times(1)).getInstance(DatabaseType.DEFAULT.getDbType());
127- verify(sessionHandler, times(1)).getConnection(instanceNodeInfo);
128- verify(sessionHandler, times(1)).detailBlockTree(connection, getId());
129- verify(sessionHandler, times(1)).close(connection);
130- }
131- 
132- @Test
133- public void testSimpleStatistic() {
134- JSONObject expectedResult = new JSONObject();
135- expectedResult.put("key", "value");
136- when(sessionHandler.simpleStatistic(connection)).thenReturn(expectedResult);
137- JSONObject result = sessionService.simpleStatistic(getId());
138- assertEquals(expectedResult, result);
139- 
140- verify(sessionHandlerFactory, times(1)).getInstance(DatabaseType.DEFAULT.getDbType());
141- verify(sessionHandler, times(1)).getConnection(instanceNodeInfo);
142- verify(sessionHandler, times(1)).simpleStatistic(connection);
143- verify(sessionHandler, times(1)).close(connection);
144- }
145- 
146- @Test
147- public void testLongTxc() {
148- JSONObject object = new JSONObject();
149- object.put("key", "value");
150- List<JSONObject> expectedResult = List.of(object);
151- when(sessionHandler.longTxc(connection)).thenReturn(expectedResult);
152- List<JSONObject> result = sessionService.longTxc(getId());
153- assertEquals(expectedResult, result);
154- verify(sessionHandlerFactory, times(1)).getInstance(DatabaseType.DEFAULT.getDbType());
155- verify(sessionHandler, times(1)).getConnection(instanceNodeInfo);
156- verify(sessionHandler, times(1)).longTxc(connection);
157- verify(sessionHandler, times(1)).close(connection);
158- }
159- 
160- @Test
161- public void testBlockAndLongTxc() {
162- JSONObject object = new JSONObject();
163- object.put("key", "value");
164- List<JSONObject> longTxc = List.of(object);
165- List<JSONObject> blockTree = List.of(object);
166- HashMap<String, List<JSONObject>> expectedResult = new HashMap<>();
167- expectedResult.put("blockTree", blockTree);
168- expectedResult.put("longTxc", longTxc);
169- 
170- when(sessionHandler.detailBlockTree(connection, null)).thenReturn(blockTree);
171- when(sessionHandler.longTxc(connection)).thenReturn(longTxc);
172- 
173- HashMap<String, List<JSONObject>> result = sessionService.blockAndLongTxc(getId());
174- assertEquals(expectedResult, result);
175- }
176- 
177- @Test
178- public void testDetail() {
179- JSONObject object = new JSONObject();
180- object.put("key", "value");
181- List<JSONObject> blockTree = List.of(object);
182- List<JSONObject> waiting = List.of(object);
183- List<DetailStatisticDto> statistic = List.of(new DetailStatisticDto());
184- HashMap<String, Object> expectedResult = new HashMap<>();
185- expectedResult.put("blockTree", blockTree);
186- expectedResult.put("general", object);
187- expectedResult.put("statistic", statistic);
188- expectedResult.put("waiting", waiting);
189- 
190- when(sessionHandler.detailWaiting(connection, getId())).thenReturn(waiting);
191- when(sessionHandler.detailStatistic(connection, getId())).thenReturn(statistic);
192- when(sessionHandler.detailBlockTree(connection, getId())).thenReturn(blockTree);
193- when(sessionHandler.detailGeneral(connection, getId())).thenReturn(object);
194- 
195- Map<String, Object> result = sessionService.detail(getId(), getId());
196- assertEquals(expectedResult, result);
197- }
198-}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/util/DownloadTest.java+37-0
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.util;
6+ 
7+import static org.junit.jupiter.api.Assertions.assertThrows;
8+ 
9+import java.io.File;
10+import java.io.IOException;
11+ 
12+import org.junit.jupiter.api.Test;
13+ 
14+/**
15+ * DownloadTest.java
16+ *
17+ * @since 2023717
18+ */
19+class DownloadTest {
20+ private static final String HTTP = "http://www.baidu.com";
21+ private static final String PATH = "1.tmp";
22+ 
23+ @Test
24+ void test() throws IOException {
25+ var file = new File(PATH);
26+ if (file.exists()) {
27+ file.delete();
28+ }
29+ Download.download(HTTP, file.getCanonicalPath());
30+ assertThrows(RuntimeException.class, () -> {
31+ Download.download(HTTP, PATH);
32+ });
33+ if (file.exists()) {
34+ file.delete();
35+ }
36+ }
37+}
Aplugins/observability-instance/src/test/java/com/nctigba/observability/instance/util/YamlUtilTest.java+28-0
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package com.nctigba.observability.instance.util;
6+ 
7+import java.util.HashMap;
8+import java.util.Map;
9+ 
10+import org.junit.jupiter.api.Test;
11+ 
12+/**
13+ * YamlUtilTest.java
14+ *
15+ * @since 2023717
16+ */
17+class YamlUtilTest {
18+ @Test
19+ void test() {
20+ Map<String, String> map = new HashMap<>();
21+ map.put("a", null);
22+ map.put("b", "c");
23+ map.put(null, null);
24+ var str = YamlUtil.dump(map);
25+ YamlUtil.loadAs(str, Map.class);
26+ YamlUtil.dump(null);
27+ }
28+}
Aplugins/observability-instance/web-ui/src/api/asp.ts+40-0
@@ -0,0 +1,40 @@
1+import ogRequest from '@/request'
2+import { useMonitorStore } from '@/store/monitor'
3+import dayjs from 'dayjs'
4+import utc from 'dayjs/plugin/utc'
5+import timezone from 'dayjs/plugin/timezone'
6+ 
7+export type AspCountData = {
8+ sampleTime: string[]
9+ sessionCount: []
10+}
11+export async function getAspCount(tabId: string): Promise<void | AspCountData> {
12+ dayjs.extend(utc)
13+ dayjs.extend(timezone)
14+ const monitorStore = useMonitorStore(tabId)
15+ const { instanceId, culRangeTimeAndStep } = monitorStore
16+ let timeRange = culRangeTimeAndStep()
17+ return ogRequest.get('/instanceMonitoring/api/v1/asp/count', {
18+ id: instanceId,
19+ startTime: dayjs(new Date(timeRange[0] * 1000))
20+ .utc()
21+ .format(),
22+ finishTime: dayjs(new Date(timeRange[1] * 1000))
23+ .utc()
24+ .format(),
25+ })
26+}
27+export async function getAspAnalysis(tabId: string): Promise<void | any[]> {
28+ const monitorStore = useMonitorStore(tabId)
29+ const { instanceId, culRangeTimeAndStep } = monitorStore
30+ let timeRange = culRangeTimeAndStep()
31+ return ogRequest.get('/instanceMonitoring/api/v1/asp/analysis', {
32+ id: instanceId,
33+ startTime: dayjs(new Date(timeRange[0] * 1000))
34+ .utc()
35+ .format(),
36+ finishTime: dayjs(new Date(timeRange[1] * 1000))
37+ .utc()
38+ .format(),
39+ })
40+}
Aplugins/observability-instance/web-ui/src/api/cluster.ts+61-0
@@ -0,0 +1,61 @@
1+import ogRequest from '@/request'
2+ 
3+export type ClusterListItem = {
4+ clusterId: string
5+ versionNum: string
6+ databaseUsername: string
7+ port: string
8+ clusterState: string
9+ envPath: string
10+ arch: string
11+ desc: string
12+ color: string
13+ nodeId: string
14+}
15+export async function getAllClusters(): Promise<void | ClusterListItem[]> {
16+ return ogRequest.get('/instanceMonitoring/api/v1/clusters/list')
17+}
18+ 
19+export type ClusterStateItem = {
20+ clusterId: string
21+ versionNum: string
22+ databaseUsername: string
23+ port: string
24+ clusterState: string
25+ envPath: string
26+ desc: string
27+ primaryNodeId: string
28+}
29+export async function getAllClustersStates(): Promise<void | ClusterStateItem[]> {
30+ return ogRequest.get('/instanceMonitoring/api/v1/clusters/allClusterState')
31+}
32+ 
33+export type NodeStateItem = {
34+ clusterId: string
35+ sync: string
36+ nodeName: string
37+ syncState: any
38+}
39+export async function getAllNodes(): Promise<void | NodeStateItem[]> {
40+ return ogRequest.get('/instanceMonitoring/api/v1/clusters/nodes')
41+}
42+ 
43+export type ClusterDetail = {}
44+export async function getClusterDetails(clusterId: string): Promise<void | ClusterDetail[]> {
45+ let start = 0
46+ let end = 0
47+ let min = 0
48+ let now = new Date()
49+ min = 1 * 60
50+ start = Number.parseInt(`${(now.getTime() - 1000 * min * 60) / 1000}`)
51+ end = Number.parseInt(`${now.getTime() / 1000}`)
52+ let step = Math.max(14, Number.parseInt(`${Math.round((end - start) / 260)}`))
53+ return ogRequest.get(
54+ '/instanceMonitoring/api/v1/clusters/' + clusterId + '/metrics?start=' + start + '&end=' + end + '&step=' + step
55+ )
56+}
57+ 
58+export type ClusterNode = {}
59+export async function getClusterNodes(clusterId: string): Promise<void | ClusterNode[]> {
60+ return ogRequest.get('/instanceMonitoring/api/v1/clusters/' + clusterId + '/nodes')
61+}
Mplugins/observability-instance/web-ui/src/api/observability.ts+317-271
@@ -7,352 +7,398 @@ import ogRequest from '@/request'
7import { useMonitorStore } from '@/store/monitor'7import { useMonitorStore } from '@/store/monitor'
8 8 
9export type OpenGaussNode = {9export type OpenGaussNode = {
10- dbName: string10+ dbName: string
11- dbType: string11+ dbType: string
12- dbUser: string12+ dbUser: string
13- dbUserPassword: string13+ dbUserPassword: string
14- id: string14+ id: string
15- instanceId: string15+ instanceId: string
16- ip: string16+ ip: string
17- port: number17+ port: number
18- serverId: string18+ serverId: string
19}19}
20 20 
21export type ServerInfo = {21export type ServerInfo = {
22- id: string22+ id: string
23- ip: string23+ ip: string
24- os: string24+ os: string
25- port: number25+ port: number
26- userName: string26+ userName: string
27- userPassword: string27+ userPassword: string
28}28}
29 29 
30export type OpenGaussInstance = {30export type OpenGaussInstance = {
31- dbVersion: string31+ dbVersion: string
32- id: string32+ id: string
33- name: string33+ name: string
34- nodeInfo: Partial<OpenGaussNode>[]34+ nodeInfo: Partial<OpenGaussNode>[]
35- remark: string35+ remark: string
36- serverInfoReq: Partial<ServerInfo>36+ serverInfoReq: Partial<ServerInfo>
37- serverInfoResp?: Partial<ServerInfo>37+ serverInfoResp?: Partial<ServerInfo>
38- type: string38+ type: string
39- vip: string39+ vip: string
40- port: number40+ port: number
41}41}
42 42 
43export async function getList(keyword?: string, page?: number, limit?: number): Promise<Partial<OpenGaussInstance>[]> {43export async function getList(keyword?: string, page?: number, limit?: number): Promise<Partial<OpenGaussInstance>[]> {
44- let resp = await axios.post('/sql-diagnosis/api/v1/instance/list', { keyword, page, limit })44+ let resp = await axios.post('/sql-diagnosis/api/v1/instance/list', { keyword, page, limit })
45- return resp.data.data45+ return resp.data.data
46}46}
47 47 
48export async function getDetail(id: string): Promise<Partial<OpenGaussInstance>> {48export async function getDetail(id: string): Promise<Partial<OpenGaussInstance>> {
49- let resp = await axios.get(`/sql-diagnosis/api/v1/instance/detail/${id}`)49+ let resp = await axios.get(`/sql-diagnosis/api/v1/instance/detail/${id}`)
50- return resp.data.data50+ return resp.data.data
51}51}
52 52 
53export async function deleteInstance(id: string): Promise<boolean> {53export async function deleteInstance(id: string): Promise<boolean> {
54- let resp = await axios.post('/sql-diagnosis/api/v1/instance/delete', [id])54+ let resp = await axios.post('/sql-diagnosis/api/v1/instance/delete', [id])
55- return resp.data.data55+ return resp.data.data
56}56}
57 57 
58export async function addInstance(entry: Partial<OpenGaussInstance>): Promise<Partial<OpenGaussInstance>> {58export async function addInstance(entry: Partial<OpenGaussInstance>): Promise<Partial<OpenGaussInstance>> {
59- let resp = await axios.post('/sql-diagnosis/api/v1/instance/add', entry)59+ let resp = await axios.post('/sql-diagnosis/api/v1/instance/add', entry)
60- return resp.data.data60+ return resp.data.data
61}61}
62 62 
63export async function updateInstance(entry: Partial<OpenGaussInstance>): Promise<Partial<OpenGaussInstance>> {63export async function updateInstance(entry: Partial<OpenGaussInstance>): Promise<Partial<OpenGaussInstance>> {
64- let resp = await axios.post(`/sql-diagnosis/api/v1/instance/update/${entry.id}`, entry)64+ let resp = await axios.post(`/sql-diagnosis/api/v1/instance/update/${entry.id}`, entry)
65- return resp.data.data65+ return resp.data.data
66}66}
67 67 
68export async function testConnection(dbNode: Partial<OpenGaussNode>): Promise<boolean> {68export async function testConnection(dbNode: Partial<OpenGaussNode>): Promise<boolean> {
69- let resp = await axios.post('/sql-diagnosis/api/v1/instance/connect', dbNode)69+ let resp = await axios.post('/sql-diagnosis/api/v1/instance/connect', dbNode)
70- return resp.data.code === '200' || resp.data.code === 20070+ return resp.data.code === '200' || resp.data.code === 200
71}71}
72 72 
73export async function getIndexMetrics(tabId: string): Promise<void | {73export async function getIndexMetrics(tabId: string): Promise<void | {
74- max_conn: number74+ max_conn: number
75- active: number75+ active: number
76- waiting: number76+ waiting: number
77- max_runtime: number77+ max_runtime: number
78- CPU: number[]78+ CPU: number[]
79- IO: number[]79+ IO: number[]
80- MEMORY: number[]80+ MEMORY: number[]
81- NETWORK_IN_TOTAL: number[]81+ NETWORK_IN_TOTAL: number[]
82- NETWORK_OUT_TOTAL: number[]82+ NETWORK_OUT_TOTAL: number[]
83- SWAP: number[]83+ SWAP: number[]
84- DB_THREAD_POOL: number[]84+ DB_THREAD_POOL: number[]
85- DB_ACTIVE_SESSION: Record<string, number[]>85+ DB_ACTIVE_SESSION: Record<string, number[]>
86- time: string[]86+ time: string[]
87}> {87}> {
88- const monitorStore = useMonitorStore(tabId)88+ const monitorStore = useMonitorStore(tabId)
89- const { instanceId, culRangeTimeAndStep } = monitorStore89+ const { instanceId, culRangeTimeAndStep } = monitorStore
90- let timeRange = culRangeTimeAndStep()90+ let timeRange = culRangeTimeAndStep()
91- return ogRequest.get('/instanceMonitoring/api/v1/mainMetrics', {91+ return ogRequest.get('/instanceMonitoring/api/v1/mainMetrics', {
92- id: instanceId,92+ id: instanceId,
93- start: timeRange[0],93+ start: timeRange[0],
94- end: timeRange[1],94+ end: timeRange[1],
95- step: timeRange[2],95+ step: timeRange[2],
96- type: 'LINE',96+ type: 'LINE',
97- })97+ })
98}98}
99 99 
100export async function getCPUMetrics(tabId: string): Promise<void | {100export async function getCPUMetrics(tabId: string): Promise<void | {
101- CPU_IOWAIT: number[]101+ CPU_IOWAIT: number[]
102- CPU_SYSTEM: number[]102+ CPU_SYSTEM: number[]
103- CPU_TOTAL: number[]103+ CPU_TOTAL: number[]
104- CPU_TOTAL_5M_LOAD: number[]104+ CPU_TOTAL_5M_LOAD: number[]
105- CPU_TOTAL_AVERAGE_UTILIZATION: number[]105+ CPU_TOTAL_AVERAGE_UTILIZATION: number[]
106- CPU_TOTAL_CORE_NUM: number[]106+ CPU_TOTAL_CORE_NUM: number[]
107- CPU_USER: number[]107+ CPU_USER: number[]
108- CPU_DB: number[]108+ CPU_DB: number[]
109- time: string[]109+ CPU_NICE: number[]
110- name: string110+ CPU_IRQ: number[]
111+ CPU_SOFTIRQ: number[]
112+ CPU_STEAL: number[]
113+ CPU_IDLE: number[]
114+ time: string[]
115+ name: string
111}> {116}> {
112- const monitorStore = useMonitorStore(tabId)117+ const monitorStore = useMonitorStore(tabId)
113- const { instanceId, culRangeTimeAndStep } = monitorStore118+ const { instanceId, culRangeTimeAndStep } = monitorStore
114- let timeRange = culRangeTimeAndStep()119+ let timeRange = culRangeTimeAndStep()
115- return ogRequest.get('/instanceMonitoring/api/v1/cpu', {120+ return ogRequest.get('/instanceMonitoring/api/v1/cpu', {
116- id: instanceId,121+ id: instanceId,
117- start: timeRange[0],122+ start: timeRange[0],
118- end: timeRange[1],123+ end: timeRange[1],
119- step: timeRange[2],124+ step: timeRange[2],
120- type: 'LINE',125+ type: 'LINE',
121- })126+ })
122}127}
123 128 
124export async function getMemoryMetrics(tabId: string): Promise<void | {129export async function getMemoryMetrics(tabId: string): Promise<void | {
125- GLOBAL_CONFIG_SETTINGS: any130+ GLOBAL_CONFIG_SETTINGS: any
126- GS_TOTAL_MEMORY_DETAIL: any131+ GS_TOTAL_MEMORY_DETAIL: any
127- MEMORY_USED: number[]132+ MEMORY_USED: number[]
128- MEMORY_DB_USED: number[]133+ MEMORY_DB_USED: number[]
129- MEMORY_SWAP: number[]134+ MEMORY_SWAP: number[]
130- MEM_CACHE: number135+ MEM_CACHE: number
131- MEM_FREE: number136+ MEM_FREE: number
132- MEM_TOTAL: number137+ MEM_TOTAL: number
133- MEM_USED: number138+ MEM_USED: number
134- SWAP_FREE: number139+ MEMORY_DB_USED_CURR: number
135- SWAP_TOTAL: number140+ SWAP_FREE: number
136- SWAP_USED: number141+ SWAP_TOTAL: number
137- memoryConfig: any[]142+ SWAP_USED: number
138- memoryNodeDetail: any[]143+ memoryConfig: any[]
139- time: string[]144+ memoryNodeDetail: any[]
145+ time: string[]
140}> {146}> {
141- const monitorStore = useMonitorStore(tabId)147+ const monitorStore = useMonitorStore(tabId)
142- const { instanceId, culRangeTimeAndStep } = monitorStore148+ const { instanceId, culRangeTimeAndStep } = monitorStore
143- let timeRange = culRangeTimeAndStep()149+ let timeRange = culRangeTimeAndStep()
144- return ogRequest.get('/instanceMonitoring/api/v1/memory', {150+ return ogRequest.get('/instanceMonitoring/api/v1/memory', {
145- id: instanceId,151+ id: instanceId,
146- start: timeRange[0],152+ start: timeRange[0],
147- end: timeRange[1],153+ end: timeRange[1],
148- step: timeRange[2],154+ step: timeRange[2],
149- type: 'LINE',155+ type: 'LINE',
150- })156+ })
151}157}
152 158 
153export async function getNetworkMetrics(tabId: string): Promise<void | {159export async function getNetworkMetrics(tabId: string): Promise<void | {
154- NETWORK_IN: Record<string, number[]>160+ NETWORK_IN: Record<string, number[]>
155- NETWORK_OUT: Record<string, number[]>161+ NETWORK_OUT: Record<string, number[]>
156- NETWORK_LOST_PACKAGE: Record<string, number[]>162+ NETWORK_LOST_PACKAGE: Record<string, number[]>
157- NETWORK_TCP_ALLOC: number[]163+ NETWORK_TCP_ALLOC: number[]
158- NETWORK_CURRESTAB: number[]164+ NETWORK_CURRESTAB: number[]
159- NETWORK_TCP_INSEGS: number[]165+ NETWORK_TCP_INSEGS: number[]
160- NETWORK_TCP_OUTSEGS: number[]166+ NETWORK_TCP_OUTSEGS: number[]
161- NETWORK_TCP_SOCKET: Record<string, number[]>167+ NETWORK_TCP_SOCKET: Record<string, number[]>
162- NETWORK_UDP_SOCKET: number[]168+ NETWORK_UDP_SOCKET: number[]
163- table: any[]169+ table: any[]
164- time: string[]170+ time: string[]
165- name: string171+ name: string
166}> {172}> {
167- const monitorStore = useMonitorStore(tabId)173+ const monitorStore = useMonitorStore(tabId)
168- const { instanceId, culRangeTimeAndStep } = monitorStore174+ const { instanceId, culRangeTimeAndStep } = monitorStore
169- let timeRange = culRangeTimeAndStep()175+ let timeRange = culRangeTimeAndStep()
170- return ogRequest.get('/instanceMonitoring/api/v1/network', {176+ return ogRequest.get('/instanceMonitoring/api/v1/network', {
171- id: instanceId,177+ id: instanceId,
172- start: timeRange[0],178+ start: timeRange[0],
173- end: timeRange[1],179+ end: timeRange[1],
174- step: timeRange[2],180+ step: timeRange[2],
175- type: 'LINE',181+ type: 'LINE',
176- })182+ })
177}183}
178 184 
179export async function getIOMetrics(tabId: string): Promise<void | {185export async function getIOMetrics(tabId: string): Promise<void | {
180- table: any[]186+ table: any[]
181- IOPS_R: Record<string, number[]>187+ IOPS_R: Record<string, number[]>
182- IOPS_W: Record<string, number[]>188+ IOPS_W: Record<string, number[]>
183- IO_AVG_REPONSE_TIME_READ: Record<string, number[]>189+ IO_AVG_REPONSE_TIME_READ: Record<string, number[]>
184- IO_AVG_REPONSE_TIME_RW: Record<string, number[]>190+ IO_AVG_REPONSE_TIME_RW: Record<string, number[]>
185- IO_AVG_REPONSE_TIME_WRITE: Record<string, number[]>191+ IO_AVG_REPONSE_TIME_WRITE: Record<string, number[]>
186- IO_DISK_READ_BYTES_PER_SECOND: Record<string, number[]>192+ IO_DISK_READ_BYTES_PER_SECOND: Record<string, number[]>
187- IO_DISK_WRITE_BYTES_PER_SECOND: Record<string, number[]>193+ IO_DISK_WRITE_BYTES_PER_SECOND: Record<string, number[]>
188- IO_QUEUE_LENGTH: Record<string, number[]>194+ IO_QUEUE_LENGTH: Record<string, number[]>
189- IO_UTIL: Record<string, number[]>195+ IO_UTIL: Record<string, number[]>
190- time: string[]196+ time: string[]
191}> {197}> {
192- const monitorStore = useMonitorStore(tabId)198+ const monitorStore = useMonitorStore(tabId)
193- const { instanceId, culRangeTimeAndStep } = monitorStore199+ const { instanceId, culRangeTimeAndStep } = monitorStore
194- let timeRange = culRangeTimeAndStep()200+ let timeRange = culRangeTimeAndStep()
195- return ogRequest.get('/instanceMonitoring/api/v1/io', {201+ return ogRequest.get('/instanceMonitoring/api/v1/io', {
196- id: instanceId,202+ id: instanceId,
197- start: timeRange[0],203+ start: timeRange[0],
198- end: timeRange[1],204+ end: timeRange[1],
199- step: timeRange[2],205+ step: timeRange[2],
200- type: 'LINE',206+ type: 'LINE',
201- })207+ })
202}208}
203 209 
204export type TopSQLNow = {210export type TopSQLNow = {
205- id: string211+ id: string
206- ip: string212+ ip: string
207- os: string213+ os: string
208- port: number214+ port: number
209- userName: string215+ userName: string
210- userPassword: string216+ userPassword: string
217+ duration: string
211}218}
212export type BlockTable = {219export type BlockTable = {
213- depth: string220+ depth: string
214- application_name: string221+ application_name: string
215- tree_id: string222+ tree_id: string
216- backend_start: string223+ backend_start: string
217- hasChildren?: boolean224+ hasChildren?: boolean
218- children?: undefined | BlockTable[]225+ children?: undefined | BlockTable[]
219- client_addr: string226+ client_addr: string
220- datname: string227+ datname: string
221- pathid: string228+ pathid: string
222- id: string229+ id: string
223- usename: string230+ usename: string
224- state: string231+ state: string
225- parentid: string232+ parentid: string
226}233}
227export type TransTable = {234export type TransTable = {
228- application_name: string235+ application_name: string
229- client_addr: string236+ client_addr: string
230- datname: string237+ datname: string
231- pid: string238+ pid: string
232- query: string239+ query: string
233- query_duration: string240+ query_duration: string
234- query_start: string241+ query_start: string
235- sessionid: string242+ sessionid: string
236- state: string243+ state: string
237- usename: string244+ usename: string
238- xact_duration: string245+ xact_duration: string
239- xact_start: string246+ xact_start: string
247+}
248+export type WaitEvent = {
249+ block_sessionid: string
250+ db_name: string
251+ lockmode: string
252+ locktag: string
253+ node_name: string
254+ query_id: string
255+ sessionid: string
256+ tag: string
257+ thread_name: string
258+ tid: string
259+ wait_event: string
260+ wait_status: string
240}261}
241export type MainNowTable = {262export type MainNowTable = {
242- blockTree: BlockTable[]263+ blockTree: BlockTable[]
243- longTxc: TransTable[]264+ longTxc: TransTable[]
244- topSQLNow: TopSQLNow[]265+ topSQLNow: TopSQLNow[]
266+ waitEvents: WaitEvent[]
245}267}
246export async function getTOPSQLNow(tabId: string): Promise<void | MainNowTable> {268export async function getTOPSQLNow(tabId: string): Promise<void | MainNowTable> {
247- const monitorStore = useMonitorStore(tabId)269+ const monitorStore = useMonitorStore(tabId)
248- const { instanceId } = monitorStore270+ const { instanceId } = monitorStore
249- return ogRequest.get('/instanceMonitoring/api/v1/topSQLNow', {271+ return ogRequest.get('/instanceMonitoring/api/v1/topSQLNow', {
250- id: instanceId,272+ id: instanceId,
251- })273+ })
252}274}
253 275 
254export type TopCPUProcessNow = {276export type TopCPUProcessNow = {
255- '%CPU': string277+ '%CPU': string
256- '%MEM': string278+ '%MEM': string
257- COMMAND: string279+ COMMAND: string
258- NI: number280+ publicIp: string
259- PID: string281+ NI: number
260- PR: string282+ PID: string
261- RES: string283+ PR: string
262- S: string284+ RES: string
263- SHR: string285+ S: string
264- 'TIME+': string286+ SHR: string
265- USER: string287+ 'TIME+': string
266- VIRT: string288+ USER: string
289+ VIRT: string
290+ port: string
267}291}
268export async function getTOPCPUProcessNow(tabId: string): Promise<void | TopCPUProcessNow[][]> {292export async function getTOPCPUProcessNow(tabId: string): Promise<void | TopCPUProcessNow[][]> {
269- const monitorStore = useMonitorStore(tabId)293+ const monitorStore = useMonitorStore(tabId)
270- const { instanceId } = monitorStore294+ const { instanceId } = monitorStore
271- return ogRequest.get('/instanceMonitoring/api/v1/topOSProcessAndDBThread', {295+ return ogRequest.get('/instanceMonitoring/api/v1/topOSProcessAndDBThread', {
272- id: instanceId,296+ id: instanceId,
273- })297+ })
274}298}
275 299 
276export type TopMemoryProcessNow = {300export type TopMemoryProcessNow = {
277- '%CPU': string301+ '%CPU': string
278- '%MEM': string302+ '%MEM': string
279- COMMAND: string303+ COMMAND: string
280- NI: number304+ publicIp: string
281- PID: string305+ NI: number
282- PR: string306+ PID: string
283- RES: string307+ PR: string
284- S: string308+ RES: string
285- SHR: string309+ S: string
286- 'TIME+': string310+ SHR: string
287- USER: string311+ 'TIME+': string
288- VIRT: string312+ USER: string
313+ VIRT: string
314+ port: string
289}315}
290-export async function getTOPMemoryProcessNow(tabId: string): Promise<void | TopCPUProcessNow[][]> {316+export async function getTOPMemoryProcessNow(tabId: string): Promise<void | TopMemoryProcessNow[][]> {
291- const monitorStore = useMonitorStore(tabId)317+ const monitorStore = useMonitorStore(tabId)
292- const { instanceId } = monitorStore318+ const { instanceId } = monitorStore
293- return ogRequest.get('/instanceMonitoring/api/v1/topOSProcessAndDBThread', {319+ return ogRequest.get('/instanceMonitoring/api/v1/topOSProcessAndDBThread', {
294- id: instanceId,320+ id: instanceId,
295- sort: '%MEM',321+ sort: '%MEM',
296- })322+ })
297}323}
298 324 
299export async function getInstanceMetrics(tabId: string): Promise<void | {325export async function getInstanceMetrics(tabId: string): Promise<void | {
300- INSTANCE_DB_CONNECTION_ACTIVE: number[]326+ INSTANCE_DB_CONNECTION_ACTIVE: number[]
301- INSTANCE_DB_CONNECTION_CURR: number[]327+ INSTANCE_DB_CONNECTION_CURR: number[]
302- INSTANCE_DB_CONNECTION_IDLE: number[]328+ INSTANCE_DB_CONNECTION_IDLE: number[]
303- INSTANCE_DB_CONNECTION_TOTAL: number[]329+ INSTANCE_DB_CONNECTION_TOTAL: number[]
304- INSTANCE_DB_SLOWSQL: number[]330+ INSTANCE_DB_SLOWSQL: number[]
305- INSTANCE_QPS: number[]331+ INSTANCE_QPS: number[]
306- INSTANCE_TPS_COMMIT: number[]332+ INSTANCE_TPS_COMMIT: number[]
307- INSTANCE_TPS_CR: number[]333+ INSTANCE_TPS_CR: number[]
308- INSTANCE_TPS_ROLLBACK: number[]334+ INSTANCE_TPS_ROLLBACK: number[]
309- time: string[]335+ time: string[]
310}> {336}> {
311- const monitorStore = useMonitorStore(tabId)337+ const monitorStore = useMonitorStore(tabId)
312- const { instanceId, culRangeTimeAndStep } = monitorStore338+ const { instanceId, culRangeTimeAndStep } = monitorStore
313- let timeRange = culRangeTimeAndStep()339+ let timeRange = culRangeTimeAndStep()
314- return ogRequest.get('/instanceMonitoring/api/v1/instance', {340+ return ogRequest.get('/instanceMonitoring/api/v1/instance', {
315- id: instanceId,341+ id: instanceId,
316- start: timeRange[0],342+ start: timeRange[0],
317- end: timeRange[1],343+ end: timeRange[1],
318- step: timeRange[2],344+ step: timeRange[2],
319- type: 'LINE',345+ type: 'LINE',
320- })346+ })
321}347}
322 348 
323export async function getSessionMetrics(tabId: string): Promise<void | {349export async function getSessionMetrics(tabId: string): Promise<void | {
324- max_conn: number350+ max_conn: number
325- active: number351+ active: number
326- waiting: number352+ waiting: number
327- max_runtime: number353+ max_runtime: number
328- SESSION_ACTIVE_CONNECTION: number[]354+ SESSION_ACTIVE_CONNECTION: number[]
329- SESSION_IDLE_CONNECTION: number[]355+ SESSION_IDLE_CONNECTION: number[]
330- SESSION_MAX_CONNECTION: number[]356+ SESSION_MAX_CONNECTION: number[]
331- SESSION_WAITING_CONNECTION: number[]357+ SESSION_WAITING_CONNECTION: number[]
332- gauss_wait_events_value: any[]358+ gauss_wait_events_value: any[]
333- time: string[]359+ time: string[]
334- name: string360+ name: string
335}> {361}> {
336- const monitorStore = useMonitorStore(tabId)362+ const monitorStore = useMonitorStore(tabId)
337- const { instanceId, culRangeTimeAndStep } = monitorStore363+ const { instanceId, culRangeTimeAndStep } = monitorStore
338- let timeRange = culRangeTimeAndStep()364+ let timeRange = culRangeTimeAndStep()
339- return ogRequest.get('/instanceMonitoring/api/v1/session/sessionStatistic', {365+ return ogRequest.get('/instanceMonitoring/api/v1/session/sessionStatistic', {
340- id: instanceId,366+ id: instanceId,
341- start: timeRange[0],367+ start: timeRange[0],
342- end: timeRange[1],368+ end: timeRange[1],
343- step: timeRange[2],369+ step: timeRange[2],
344- type: 'LINE',370+ type: 'LINE',
345- })371+ })
346}372}
347 373 
348export type SessionTables = {374export type SessionTables = {
349- blockTree: BlockTable[]375+ blockTree: BlockTable[]
350- longTxc: TransTable[]376+ longTxc: TransTable[]
351}377}
352export async function getSessionTables(tabId: string): Promise<void | SessionTables> {378export async function getSessionTables(tabId: string): Promise<void | SessionTables> {
353- const monitorStore = useMonitorStore(tabId)379+ const monitorStore = useMonitorStore(tabId)
354- const { instanceId } = monitorStore380+ const { instanceId } = monitorStore
355- return ogRequest.get('/instanceMonitoring/api/v1/session/blockAndLongTxc', {381+ return ogRequest.get('/instanceMonitoring/api/v1/session/blockAndLongTxc', {
356- id: instanceId,382+ id: instanceId,
357- })383+ })
384+}
385+ 
386+export type NodeInfo = {
387+ dbDataPath: string
388+ CPUmodel: string
389+ CPUmanufacturer: string
390+ osVersion: string
391+ dbLogPath: string
392+ CPUcores: string
393+ TotalMemory: string
394+ time: string
395+ version: string
396+ archiveMode: string
397+}
398+export async function getNodeInfo(tabId: string): Promise<void | NodeInfo> {
399+ const monitorStore = useMonitorStore(tabId)
400+ const { instanceId } = monitorStore
401+ return ogRequest.get('/instanceMonitoring/api/v1/nodeInfo', {
402+ id: instanceId,
403+ })
358}404}
Mplugins/observability-instance/web-ui/src/api/prometheus.ts+61-65
@@ -1,7 +1,3 @@
1-///
2-/// Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3-///
4- 
5import { useRequest } from 'vue-request'1import { useRequest } from 'vue-request'
6import ogRequest from '@/request'2import ogRequest from '@/request'
7import { useMonitorStore } from '@/store/monitor'3import { useMonitorStore } from '@/store/monitor'
@@ -11,70 +7,70 @@ import { tabKeys } from '@/pages/dashboardV2/common'
11let tabIdTemp: string = ''7let tabIdTemp: string = ''
12 8 
13const { data, run: getDatabaseMetrics } = useRequest<Record<9const { data, run: getDatabaseMetrics } = useRequest<Record<
14- string,10+ string,
15- { data: string[]; time: string[]; name: string }[]11+ { data: string[]; time: string[]; name: string }[]
16> | void>(12> | void>(
17- (tabId: string) => {13+ (tabId: string, dbId?: string) => {
18- tabIdTemp = tabId14+ tabIdTemp = tabId
19- const monitorStore = useMonitorStore(tabIdTemp)15+ const monitorStore = useMonitorStore(tabIdTemp)
20- const { rangeTime, time, instanceId, fixedRangeTime, tab } = monitorStore16+ const { rangeTime, time, instanceId, fixedRangeTime, tab } = monitorStore
21- let start = 017+ let start = 0
22- let end = 018+ let end = 0
23- let step = 6019+ let step = 60
24- if (tab === 1 && Array.isArray(fixedRangeTime) && fixedRangeTime.length === 2) {20+ if (tab === 1 && Array.isArray(fixedRangeTime) && fixedRangeTime.length === 2) {
25- end = Math.floor(moment(fixedRangeTime[1]).valueOf() / 1000)21+ end = Math.floor(moment(fixedRangeTime[1]).valueOf() / 1000)
26- start = Math.floor(moment(fixedRangeTime[0]).valueOf() / 1000)22+ start = Math.floor(moment(fixedRangeTime[0]).valueOf() / 1000)
27- if (end - start < 2 * 60) {23+ if (end - start < 2 * 60) {
28- const mid = start + Math.floor((end - start) / 2)24+ const mid = start + Math.floor((end - start) / 2)
29- end = mid + 6025+ end = mid + 60
30- start = mid - 6026+ start = mid - 60
31- }27+ }
32- console.log('sql detail - system source', start, end)28+ console.log('sql detail - system source', start, end)
33- } else {29+ } else {
34- if (rangeTime > 0) {30+ if (rangeTime > 0) {
35- const _time = moment()31+ const _time = moment()
36- end = Number.parseInt(32+ end = Number.parseInt(
37- `${33+ `${
38- _time34+ _time
39- .subtract(60 * rangeTime, 'second')35+ .subtract(60 * rangeTime, 'second')
40- .toDate()36+ .toDate()
41- .getTime() / 100037+ .getTime() / 1000
42- }`38+ }`
43- )39+ )
44- start = Number.parseInt(`${_time.subtract(rangeTime, 'hour').toDate().getTime() / 1000}`)40+ start = Number.parseInt(`${_time.subtract(rangeTime, 'hour').toDate().getTime() / 1000}`)
45- step = 60 * rangeTime41+ step = 60 * rangeTime
46- } else {42+ } else {
47- start = Number.parseInt(`${time![0].getTime() / 1000}`)43+ start = Number.parseInt(`${time![0].getTime() / 1000}`)
48- end = Number.parseInt(`${time![1].getTime() / 1000}`)44+ end = Number.parseInt(`${time![1].getTime() / 1000}`)
49- step = Number.parseInt(`${(end - start) / 120}`)45+ step = Number.parseInt(`${(end - start) / 120}`)
50- }46+ }
51- }47+ }
52- monitorStore.promethuesStart = start48+ monitorStore.promethuesStart = start
53- monitorStore.promethuesEnd = end49+ monitorStore.promethuesEnd = end
54- monitorStore.promethuesStep = Math.max(60, step)50+ monitorStore.promethuesStep = Math.max(60, step)
55- return ogRequest.get('/observability/v1/monitoring/database-metrics', {51+ return ogRequest.get('/observability/v1/monitoring/database-metrics', {
56- id: instanceId,52+ id: dbId !== undefined ? dbId : instanceId,
57- start,53+ start,
58- end,54+ end,
59- step: Math.max(60, step),55+ step: Math.max(60, step),
60- type: 'LINE',56+ type: 'LINE',
61- })57+ })
62- },58+ },
63- { manual: true }59+ { manual: true }
64)60)
65 61 
66watch(62watch(
67- data,63+ data,
68- () => {64+ () => {
69- console.log('prometheus:tabIdTemp2', tabIdTemp)65+ console.log('prometheus:tabIdTemp2', tabIdTemp)
70- const monitorStore = useMonitorStore(tabIdTemp)66+ const monitorStore = useMonitorStore(tabIdTemp)
71- if (monitorStore.tabNow === tabKeys.Home) {67+ if (monitorStore.tabNow === tabKeys.Home) {
72- monitorStore.databaseData = data.value || {}68+ monitorStore.databaseData = data.value || {}
73- console.log('prometheus:databaseData', monitorStore.databaseData)69+ console.log('prometheus:databaseData', monitorStore.databaseData)
74- } else {70+ } else {
75- monitorStore.serverData = data.value || {}71+ monitorStore.serverData = data.value || {}
76- }72+ }
77- },73+ },
78- { deep: true }74+ { deep: true }
79)75)
80export { getDatabaseMetrics }76export { getDatabaseMetrics }
Mplugins/observability-instance/web-ui/src/api/sqlDetail.ts+40-13
@@ -1,20 +1,47 @@
1-///
2-/// Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3-///
4- 
5import ogRequest from '@/request'1import ogRequest from '@/request'
6 2 
7export type SQLEvent = {3export type SQLEvent = {
8- event: string4+ event: string
9- id: string5+ id: string
10- lockType: string6+ lockType: string
11- time: string7+ time: string
12- unknown: string8+ unknown: string
13}9}
14 10 
15export async function getSQLEvent(instanceId: string, sqlId: string): Promise<void | SQLEvent[]> {11export async function getSQLEvent(instanceId: string, sqlId: string): Promise<void | SQLEvent[]> {
16- return ogRequest.get('/observability/v1/topsql/waitevent', {12+ return ogRequest.get('/observability/v1/topsql/waitevent', {
17- id: instanceId,13+ id: instanceId,
18- sqlId,14+ sqlId,
19- })15+ })
16+}
17+ 
18+export async function getSQLMetrics(
19+ instanceId: String,
20+ startTime: String,
21+ endTime: String,
22+ step: String
23+): Promise<void | {
24+ CPU_DB: number[]
25+ CPU_IDLE: number[]
26+ CPU_IOWAIT: number[]
27+ CPU_IRQ: number[]
28+ CPU_NICE: number[]
29+ CPU_SOFTIRQ: number[]
30+ CPU_STEAL: number[]
31+ CPU_SYSTEM: number[]
32+ CPU_TOTAL: number[]
33+ CPU_USER: number[]
34+ IO_UTIL: number[][]
35+ MEMORY_DB_USED: number[]
36+ MEMORY_USED: number[]
37+ NETWORK_IN_TOTAL: number[]
38+ NETWORK_OUT_TOTAL: number[]
39+ time: string[]
40+}> {
41+ return ogRequest.get('/observability/v1/topsql/sysResource', {
42+ id: instanceId,
43+ start: startTime,
44+ end: endTime,
45+ step,
46+ })
20}47}
Aplugins/observability-instance/web-ui/src/api/wdr.ts+17-0
@@ -0,0 +1,17 @@
1+import ogRequest from '@/request'
2+import { useMonitorStore } from '@/store/monitor'
3+ 
4+export type WDRSnapshots = {
5+ start: string
6+ end: string
7+ wdrId: string[]
8+}
9+export async function getWDRSnapshot(tabId: string): Promise<void | WDRSnapshots> {
10+ const monitorStore = useMonitorStore(tabId)
11+ const { instanceId, timeRange } = monitorStore
12+ return ogRequest.get('/wdr/findSnapshot', {
13+ id: instanceId,
14+ start: timeRange == null ? '' : new Date(timeRange[0]).toISOString().replace(/\.\d+/, ''),
15+ end: timeRange == null ? '' : new Date(timeRange[1]).toISOString().replace(/\.\d+/, ''),
16+ })
17+}
Mplugins/observability-instance/web-ui/src/assets/style/common.scss+226-180
@@ -7,260 +7,306 @@
7 7 
8// page-header8// page-header
9.page-header {9.page-header {
10- display: flex;10+ display: flex;
11- flex-direction: row;11+ flex-direction: row;
12- align-items: center;12+ align-items: center;
13- padding-bottom: 7px;13+ padding-bottom: 7px;
14- border-bottom: 1px solid var(--border-1);14+ border-bottom: 1px solid var(--border-1);
15 15 
16- .icon {16+ .icon {
17- width: 4px;17+ width: 4px;
18- height: 14px;18+ height: 14px;
19- background: var(--primary-6);19+ background: var(--primary-6);
20- border-radius: 1px;20+ border-radius: 1px;
21- margin-right: 4px;21+ margin-right: 4px;
22- }22+ }
23- .title {23+ .title {
24- font-family: 'Source Han Sans CN';24+ font-family: 'Source Han Sans CN';
25- font-style: normal;25+ font-style: normal;
26- font-weight: 700;26+ font-weight: 700;
27- font-size: 16px;27+ font-size: 16px;
28- line-height: 22px;28+ line-height: 22px;
29- height: 22px;29+ height: 22px;
30- /* identical to box height, or 138% */30+ /* identical to box height, or 138% */
31- letter-spacing: -0.01px;31+ letter-spacing: -0.01px;
32- color: var(--text-1);32+ color: var(--text-1);
33- }33+ }
34- .seperator {34+ .seperator {
35- margin: 0px 16px;35+ margin: 0px 16px;
36- width: 18px;36+ width: 18px;
37- height: 0px;37+ height: 0px;
38- border: 1px solid var(--border-1);38+ border: 1px solid var(--border-1);
39- transform: rotate(90deg);39+ transform: rotate(90deg);
40- }40+ }
41- .cluster-title {41+ .cluster-title {
42- height: 22px;42+ height: 22px;
43- font-family: 'Source Han Sans CN';43+ font-family: 'Source Han Sans CN';
44- font-style: normal;44+ font-style: normal;
45- font-weight: 400;45+ font-weight: 400;
46- font-size: 14px;46+ font-size: 14px;
47- line-height: 22px;47+ line-height: 22px;
48- color: var(--text-1);48+ color: var(--text-1);
49- }49+ }
50- .instance-info {50+ .instance-info {
51- margin-left: 4px;51+ margin-left: 4px;
52- }52+ }
53+}
54+ 
55+.main-container {
56+ margin-top: 16px;
53}57}
54 58 
55// card margin bottom59// card margin bottom
56.card-margin-bottom {60.card-margin-bottom {
57- margin-bottom: 16px;61+ margin-bottom: 16px;
58}62}
59.card-links {63.card-links {
60- .el-link {64+ .el-link {
61- margin-right: 10px;65+ margin-right: 10px;
62- }66+ }
63}67}
64 68 
65// tabs in card69// tabs in card
66.card-tabs {70.card-tabs {
67- &.el-tabs {71+ &.el-tabs {
68- --el-tabs-header-height: 32px;72+ --el-tabs-header-height: 32px;
69- > .el-tabs__header {73+ > .el-tabs__header {
70- .el-tabs__item {74+ .el-tabs__item {
71- padding: 0px 16px !important;75+ padding: 0px 16px !important;
72- }76+ }
73- }
74 }77 }
78+ }
75}79}
76 80 
77// table in card81// table in card
78.table-in-card {82.table-in-card {
79- padding: 0px 16px;83+ padding: 0px 16px;
80- .el-table--border {84+ .el-table--border {
81- border: 1px solid rgba(22, 93, 255, 0.4);85+ border: 1px solid rgba(22, 93, 255, 0.4);
82- }86+ }
83}87}
84 88 
85// text in card89// text in card
86.text-in-card {90.text-in-card {
87- padding: 8px 16px;91+ padding: 8px 16px;
88- .text-row {92+ .text-row {
89- display: flex;93+ display: flex;
90- flex-direction: row;94+ flex-direction: row;
91- .label {95+ .label {
92- width: 115px;96+ width: 115px;
93- text-align: right;97+ text-align: right;
94- }
95 }98 }
99+ }
96}100}
97 101 
98// normal grid header102// normal grid header
99.grid-header {103.grid-header {
100- background: var(--background-color-2) !important;104+ background: var(--background-color-2) !important;
101- .cell {105+ .cell {
102- line-height: 22px !important;106+ line-height: 22px !important;
103- }107+ }
108+}
109+.highlight-row {
110+ background: #fff8df !important;
104}111}
105- 
106 112 
107// grid header when grid is in card113// grid header when grid is in card
108.grid-header-in-card {114.grid-header-in-card {
109- background: var(--background-color-3) !important;115+ background: var(--background-color-3) !important;
110- .cell {116+ .cell {
111- line-height: 22px !important;117+ line-height: 22px !important;
112- }118+ }
113}119}
114 120 
115.table-link {121.table-link {
116- color: var(--link-6);122+ color: var(--link-6);
117- cursor: pointer;123+ cursor: pointer;
118}124}
119 125 
120.el-tabs__item.is-disabled {126.el-tabs__item.is-disabled {
121- cursor: auto !important;127+ cursor: auto !important;
122}128}
123 129 
124// table130// table
131+.filter-bar {
132+ display: flex;
133+ flex-direction: row;
134+ align-items: center;
135+ justify-content: flex-end;
136+ width: 100%;
137+ margin-bottom: 8px;
138+ .item:not(:last-child) {
139+ display: flex;
140+ align-items: center;
141+ margin-right: 16px;
142+ }
143+}
125.normal-table {144.normal-table {
126- .operate-btns {145+ .operate-btns {
127- display: flex;146+ display: flex;
128- justify-content: space-around;147+ justify-content: space-around;
129- }148+ }
130}149}
131.search-form {150.search-form {
151+ display: flex;
152+ justify-content: flex-end;
153+ align-items: center;
154+ margin-bottom: 20px;
155+ .search-time-range {
156+ width: 300px;
157+ }
158+ .filter {
159+ display: flex;
160+ align-items: center;
161+ }
162+ .filter:not(:last-child) {
163+ margin-right: 15px;
164+ }
165+ .seperator {
166+ margin: auto;
167+ }
168+}
169+.search-form-multirow {
170+ .row {
132 display: flex;171 display: flex;
133 justify-content: flex-end;172 justify-content: flex-end;
134 align-items: center;173 align-items: center;
135- margin-bottom: 20px;
136 .search-time-range {174 .search-time-range {
137- width: 300px;175+ width: 300px;
138 }176 }
139 .filter {177 .filter {
140- display: flex;178+ display: flex;
141- align-items: center;179+ align-items: center;
142 }180 }
143 .filter:not(:last-child) {181 .filter:not(:last-child) {
144- margin-right: 15px;182+ margin-right: 15px;
145 }183 }
146 .seperator {184 .seperator {
147- margin: auto;185+ margin: auto;
148- }
149-}
150-.search-form-multirow {
151- .row {
152- display: flex;
153- justify-content: flex-end;
154- align-items: center;
155- .search-time-range {
156- width: 300px;
157- }
158- .filter {
159- display: flex;
160- align-items: center;
161- }
162- .filter:not(:last-child) {
163- margin-right: 15px;
164- }
165- .seperator {
166- margin: auto;
167- }
168- margin-bottom: 10px;
169 }186 }
170 margin-bottom: 10px;187 margin-bottom: 10px;
188+ }
189+ margin-bottom: 10px;
190+}
191+.state-row {
192+ display: flex;
193+ flex-direction: row;
194+ align-items: center;
195+ .state {
196+ width: 6px;
197+ height: 6px;
198+ margin-right: 4px;
199+ border-radius: 100px;
200+ &.green {
201+ background: var(--green, #52c41a);
202+ box-shadow: 0px 1px 3px 0px #52c41a;
203+ }
204+ &.yellow {
205+ background: var(--green, #ffa53c);
206+ box-shadow: 0px 1px 3px 0px #ffa53c;
207+ }
208+ &.red {
209+ background: var(--green, #f6605a);
210+ box-shadow: 0px 1px 3px 0px #f6605a;
211+ }
212+ &.grey {
213+ background: var(--green, #b8b8b8);
214+ box-shadow: 0px 1px 3px 0px #b8b8b8;
215+ }
216+ }
171}217}
172 218 
173.refresh-bar {219.refresh-bar {
174- &-container {220+ &-container {
175- height: 500px;221+ height: 500px;
176- position: absolute;222+ position: absolute;
177- right: -14px;223+ right: -14px;
178- top: 0px;224+ top: 0px;
179- padding-top: 1px;225+ padding-top: 1px;
180- display: flex;226+ display: flex;
181- margin-bottom: 10px;227+ margin-bottom: 10px;
182- justify-content: end;228+ justify-content: end;
183- overflow: hidden;229+ overflow: hidden;
184- font-size: 12px;230+ font-size: 12px;
231+ }
232+ 
233+ &-filter {
234+ font-size: 12px;
235+ width: 800px;
236+ z-index: 10;
237+ padding-right: 16px;
238+ display: flex;
239+ align-items: center;
240+ padding: 0 10px;
241+ height: 30px;
242+ > div:not(:last-of-type),
243+ > span,
244+ > button {
245+ margin-right: 4px;
185 }246 }
186 247 
187- &-filter {248+ :deep(.el-button .el-icon svg) {
188- font-size: 12px;249+ color: var(--el-color-icon-refresh-color);
189- width: 800px;
190- z-index: 10;
191- padding-right: 16px;
192- display: flex;
193- align-items: center;
194- padding: 0 10px;
195- height: 30px;
196- > div:not(:last-of-type),
197- > span,
198- > button {
199- margin-right: 4px;
200- }
201- 
202- :deep(.el-button .el-icon svg) {
203- color: var(--el-color-icon-refresh-color);
204- }
205- 
206- :deep(.el-select .el-input.is-focus .el-input__wrapper) {
207- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
208- }
209- :deep(.el-select-dropdown__item.selected) {
210- color: var(--el-color-tabbar-active) !important;
211- }
212- :deep(.el-range-editor.is-active) {
213- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
214- }
215 }250 }
216 251 
217- :deep(.el-range-input) {252+ :deep(.el-select .el-input.is-focus .el-input__wrapper) {
218- background-color: var(--el-bg-color);253+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
219 }254 }
255+ :deep(.el-select-dropdown__item.selected) {
256+ color: var(--el-color-tabbar-active) !important;
257+ }
258+ :deep(.el-range-editor.is-active) {
259+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
260+ }
261+ }
220 262 
221- :deep(.el-date-editor--datetimerange) {263+ :deep(.el-range-input) {
222- width: 100px;264+ background-color: var(--el-bg-color);
223- background-color: var(--el-bg-color);265+ }
224- }266+ 
267+ :deep(.el-date-editor--datetimerange) {
268+ width: 100px;
269+ background-color: var(--el-bg-color);
270+ }
225}271}
226 272 
227.refresh-button.el-button.el-button--primary {273.refresh-button.el-button.el-button--primary {
228- background-color: var(--button-1-background) !important;274+ background-color: var(--button-1-background) !important;
229- border: 1px solid var(--button-1-border) !important;275+ border: 1px solid var(--button-1-border) !important;
230- color: var(--button-1-color) !important;276+ color: var(--button-1-color) !important;
231- padding: 8px !important;277+ padding: 8px !important;
232}278}
233 279 
234.divider {280.divider {
235- height: 24px;281+ height: 24px;
236- width: 1px;282+ width: 1px;
237- margin: 0 8px !important;283+ margin: 0 8px !important;
238- background-color: var(--el-color-divider-border-color);284+ background-color: var(--el-color-divider-border-color);
239}285}
240 286 
241.gap-row {287.gap-row {
242- margin-bottom: 16px;288+ margin-bottom: 16px;
243}289}
244 290 
245.line-tips {291.line-tips {
246- position: absolute;292+ position: absolute;
247- top: 8px;293+ top: 8px;
248- left: 13px;294+ left: 13px;
295+ display: flex;
296+ > div {
297+ font-size: 12px;
249 display: flex;298 display: flex;
250- > div {299+ flex-direction: row;
251- font-size: 12px;300+ align-items: center;
252- display: flex;301+ padding: 4px 12px;
253- flex-direction: row;302+ gap: 4px;
254- align-items: center;303+ border: 1px solid var(--border-2);
255- padding: 4px 12px;304+ border-radius: 2px;
256- gap: 4px;305+ }
257- border: 1px solid var(--border-2);
258- border-radius: 2px;
259- }
260 306 
261- &.center {307+ &.center {
262- position: unset;308+ position: unset;
263- justify-content: center;309+ justify-content: center;
264- flex-direction: row;310+ flex-direction: row;
265- }311+ }
266}312}
Aplugins/observability-instance/web-ui/src/assets/svg/close.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.05709 7.9999L2.81445 3.75726L3.75726 2.81445L7.9999 7.05709L12.2425 2.81445L13.1854 3.75726L8.94271 7.9999L13.1854 12.2425L12.2425 13.1854L7.9999 8.94271L3.75726 13.1854L2.81445 12.2425L7.05709 7.9999Z" fill="#4E5969"/>
3+</svg>
Mplugins/observability-instance/web-ui/src/components.d.ts+2-0
@@ -9,6 +9,8 @@ declare module '@vue/runtime-core' {
9 export interface GlobalComponents {9 export interface GlobalComponents {
10 ClusterCascader: typeof import('./components/ClusterCascader.vue')['default']10 ClusterCascader: typeof import('./components/ClusterCascader.vue')['default']
11 ElAside: typeof import('element-plus/es')['ElAside']11 ElAside: typeof import('element-plus/es')['ElAside']
12+ ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
13+ ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
12 ElButton: typeof import('element-plus/es')['ElButton']14 ElButton: typeof import('element-plus/es')['ElButton']
13 ElCascader: typeof import('element-plus/es')['ElCascader']15 ElCascader: typeof import('element-plus/es')['ElCascader']
14 ElCheckbox: typeof import('element-plus/es')['ElCheckbox']16 ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
Mplugins/observability-instance/web-ui/src/components/IndexBar.vue+2-1
@@ -3,7 +3,8 @@
3 <div class="refresh-bar-filter" >3 <div class="refresh-bar-filter" >
4 <div style="width: 200px;"></div>4 <div style="width: 200px;"></div>
5 <div style="white-space: nowrap">{{ $t('app.autoRefresh') }}:</div>5 <div style="white-space: nowrap">{{ $t('app.autoRefresh') }}:</div>
6- <el-select v-model="autoRefreshTime" style="width: 80px; margin: 0 4px">6+ <el-select v-model="autoRefreshTime" style="width: 130px; margin: 0 4px">
7+ <el-option :value="99999999" label="NO-AUTO" />
7 <el-option :value="1" label="1s" />8 <el-option :value="1" label="1s" />
8 <el-option :value="15" label="15s" />9 <el-option :value="15" label="15s" />
9 <el-option :value="30" label="30s" />10 <el-option :value="30" label="30s" />
Mplugins/observability-instance/web-ui/src/components/echarts/LazyLine.vue+39-13
@@ -89,11 +89,14 @@ const props = withDefaults(
89 interval?: number // yAxis interval89 interval?: number // yAxis interval
90 tabId: string90 tabId: string
91 rangeSelect: boolean91 rangeSelect: boolean
92+ legendShown: boolean
92 isTooltipsFormatDate: boolean // x value is date,and format to YYYY-MM-DD HH:mm:ss93 isTooltipsFormatDate: boolean // x value is date,and format to YYYY-MM-DD HH:mm:ss
93 94 
94 unit?: string95 unit?: string
95 scatterUnit?: string96 scatterUnit?: string
96 tips?: string97 tips?: string
98+ toolTipsSort?: string // desc asc
99+ toolTipsExcludeZero?: boolean
97 // yAxis Scatter Data100 // yAxis Scatter Data
98 scatterData?: LineData101 scatterData?: LineData
99 // LineChart use areaStyle102 // LineChart use areaStyle
@@ -116,6 +119,7 @@ const props = withDefaults(
116 enterable?: boolean119 enterable?: boolean
117 translate?: boolean120 translate?: boolean
118 countByDataTimePicker: boolean121 countByDataTimePicker: boolean
122+ xFormater?: string
119 }>(),123 }>(),
120 {124 {
121 xData: () => [],125 xData: () => [],
@@ -124,6 +128,7 @@ const props = withDefaults(
124 scatterUnit: '',128 scatterUnit: '',
125 areaStyle: false,129 areaStyle: false,
126 rangeSelect: false,130 rangeSelect: false,
131+ legendShown: true,
127 translate: true,132 translate: true,
128 isTooltipsFormatDate: true,133 isTooltipsFormatDate: true,
129 enterable: true,134 enterable: true,
@@ -168,10 +173,12 @@ const renderChart = () => {
168 areaStyle: props.areaStyle ? {} : undefined,173 areaStyle: props.areaStyle ? {} : undefined,
169 ...d,174 ...d,
170 }175 }
171- if (props.bar) {176+ if (props.bar && d.type === undefined) {
172 o.type = 'bar'177 o.type = 'bar'
178+ o['barGap'] = '0%'
179+ o['barCategoryGap'] = '0%'
180+ o['barWidth'] = '100%'
173 o['stack'] = 'total'181 o['stack'] = 'total'
174- o['barMaxWidth'] = 12
175 }182 }
176 if (props.stack) {183 if (props.stack) {
177 o['stack'] = 'total'184 o['stack'] = 'total'
@@ -210,7 +217,7 @@ const renderChart = () => {
210 },217 },
211 color: props.color ? props.color : colorArray,218 color: props.color ? props.color : colorArray,
212 legend: {219 legend: {
213- show: true,220+ show: props.legendShown,
214 type: 'scroll',221 type: 'scroll',
215 left: 10,222 left: 10,
216 bottom: 0,223 bottom: 0,
@@ -227,7 +234,7 @@ const renderChart = () => {
227 left: 15,234 left: 15,
228 right: 15,235 right: 15,
229 top: props.tips ? 50 : 25,236 top: props.tips ? 50 : 25,
230- bottom: 28,237+ bottom: props.legendShown ? 28 : 15,
231 containLabel: true,238 containLabel: true,
232 },239 },
233 tooltip: {240 tooltip: {
@@ -247,19 +254,30 @@ const renderChart = () => {
247 } else {254 } else {
248 htmlStr += '<div style="font-size:14px">' + params[0].axisValue + '</div>'255 htmlStr += '<div style="font-size:14px">' + params[0].axisValue + '</div>'
249 }256 }
250- 257+ let tempParams = params.filter((item: any) => {
251- for (let i = 0; i < params.length; i++) {258+ if (
259+ props.toolTipsExcludeZero &&
260+ (Number.isNaN(item.value) || item.value === 'NaN' || Number(item.value) === 0)
261+ ) {
262+ return false
263+ } else {
264+ return true
265+ }
266+ })
267+ if (props.toolTipsSort === 'desc') tempParams.sort((a: any, b: any) => b.value - a.value)
268+ if (props.toolTipsSort === 'asc') tempParams.sort((a: any, b: any) => a.value - b.value)
269+ for (let i = 0; i < tempParams.length; i++) {
252 // htmlStr += '<div ">' + params[i].marker + params[i].seriesName + ':' + params[i].value + '</div>'270 // htmlStr += '<div ">' + params[i].marker + params[i].seriesName + ':' + params[i].value + '</div>'
253 htmlStr +=271 htmlStr +=
254 '<div style="display: flex;flex-direction: row;align-items:center;font-size:12px">' +272 '<div style="display: flex;flex-direction: row;align-items:center;font-size:12px">' +
255 '<div style="display: inline-block; width: 14px; height: 4px;border-radius: 1px;margin-right:8px;background-color: ' +273 '<div style="display: inline-block; width: 14px; height: 4px;border-radius: 1px;margin-right:8px;background-color: ' +
256- params[i].color +274+ tempParams[i].color +
257 ';"></div>' +275 ';"></div>' +
258 '<div style="flex-grow:1;padding-right:12px">' +276 '<div style="flex-grow:1;padding-right:12px">' +
259- params[i].seriesName +277+ tempParams[i].seriesName +
260 '</div>' +278 '</div>' +
261 '<div style="">' +279 '<div style="">' +
262- params[i].value +280+ tempParams[i].value +
263 (props.unit ? props.unit : '') +281 (props.unit ? props.unit : '') +
264 '</div>' +282 '</div>' +
265 '</div>'283 '</div>'
@@ -304,7 +322,10 @@ const renderChart = () => {
304 axisLabel: {322 axisLabel: {
305 color: theme.value === 'dark' ? '#FFFFFF' : '#4E5969',323 color: theme.value === 'dark' ? '#FFFFFF' : '#4E5969',
306 fontSize: 10,324 fontSize: 10,
307- formatter: (v) => moment(new Date(v)).format('HH:mm'),325+ formatter: (v) => {
326+ if (props.xFormater) return moment(new Date(v)).format(props.xFormater)
327+ else return moment(new Date(v)).format('HH:mm')
328+ },
308 },329 },
309 data: props.xData,330 data: props.xData,
310 },331 },
@@ -347,6 +368,13 @@ const renderChart = () => {
347 },368 },
348 series: data.length > 0 ? data : [],369 series: data.length > 0 ? data : [],
349 }370 }
371+ 
372+ myChart.on('legendselectchanged', function (params: any) {
373+ let selectedLegend = params.name
374+ 
375+ myEmit('legendSelected', selectedLegend)
376+ })
377+ 
350 myChart.setOption(option, true)378 myChart.setOption(option, true)
351 if (379 if (
352 !props.countByDataTimePicker &&380 !props.countByDataTimePicker &&
@@ -387,9 +415,7 @@ const renderChart = () => {
387 }415 }
388}416}
389// lazy load417// lazy load
390-const myEmit = defineEmits<{418+const myEmit = defineEmits(['load', 'legendSelected'])
391- (event: 'load'): void
392-}>()
393const loadRef = ref<HTMLDivElement>()419const loadRef = ref<HTMLDivElement>()
394const { stop } = useIntersectionObserver(loadRef, ([{ isIntersecting }]) => {420const { stop } = useIntersectionObserver(loadRef, ([{ isIntersecting }]) => {
395 if (isIntersecting) {421 if (isIntersecting) {
Mplugins/observability-instance/web-ui/src/i18n/en.ts+118-1
@@ -20,7 +20,8 @@ export default {
20 cancel: 'cancel',20 cancel: 'cancel',
21 confirm: 'confirm',21 confirm: 'confirm',
22 edit: 'edit',22 edit: 'edit',
23- operate: 'operate',23+ operate: 'Operate',
24+ detail: 'Detail',
24 view: 'view',25 view: 'view',
25 download: 'download',26 download: 'download',
26 reset: 'Reset',27 reset: 'Reset',
@@ -34,12 +35,39 @@ export default {
34 autoRefreshFor: 'Auto Refresh:',35 autoRefreshFor: 'Auto Refresh:',
35 needSQLDiagnosis: 'Please install the smart diagnosis plug-in first',36 needSQLDiagnosis: 'Please install the smart diagnosis plug-in first',
36 diagnosis: 'Diagnosis',37 diagnosis: 'Diagnosis',
38+ all: 'All',
37 },39 },
38 instanceMonitor: {40 instanceMonitor: {
39 instanceMonitor: 'Instance Monitor',41 instanceMonitor: 'Instance Monitor',
40 clusterTitle: 'Cluster/Instances:',42 clusterTitle: 'Cluster/Instances:',
41 index: 'Home',43 index: 'Home',
42 resourceMonitor: 'Resource Monitor',44 resourceMonitor: 'Resource Monitor',
45+ thisInstance: ',Current Instance',
46+ asp: {
47+ sampleActiveSessionCount: 'Sample Active Session Count',
48+ aspAnalysis: 'ASP Analysis',
49+ analysisMetrics: 'Analysis Metrics',
50+ filterConditions: 'Filter Conditions',
51+ clickLegendToAddFilter: 'Click Legend to Add Filter',
52+ sampleCount: 'Sample Count',
53+ activeSessionCount: 'Active Session Count',
54+ },
55+ nodeInfo: {
56+ instanceInfo: 'Instance information',
57+ databaseVersion: 'Database version:',
58+ databaseStartTime: 'Database start time:',
59+ databaseDataDirectory: 'Database data directory:',
60+ databaseLogDirectory: 'Database log directory:',
61+ enableArchiving: 'Enable archiving:',
62+ yes: 'Yes',
63+ no: 'No',
64+ operatingSystemVersion: 'Operating system version:',
65+ serverCPUManufacturer: 'Server CPU manufacturer:',
66+ serverCPUModel: 'Server CPU model:',
67+ serverCPUCoreCount: 'Server CPU core count:',
68+ cores: 'cores',
69+ totalMemorySize: 'Total memory size:',
70+ },
43 instance: {71 instance: {
44 connectionQty: 'Connections',72 connectionQty: 'Connections',
45 slowSQL3s: 'Number Of Slow Sqls(>3s)',73 slowSQL3s: 'Number Of Slow Sqls(>3s)',
@@ -54,6 +82,73 @@ export default {
54 slowSQLQty: 'Slow SQL Number',82 slowSQLQty: 'Slow SQL Number',
55 },83 },
56 },84 },
85+ clusterMonitor: {
86+ clusterMonitor: 'Cluster Monitor',
87+ instanceMonitor: 'Instance Monitor',
88+ clusterList: 'Clusters',
89+ delayList: 'Primary-Backup Delay',
90+ detail: {
91+ title: 'Cluster Details (Cluster Name = temp)',
92+ info: 'Cluster Information',
93+ instance: {
94+ legend: 'Legend',
95+ nodeName: 'Node Name',
96+ ipPort: 'IP Port',
97+ role: 'Role',
98+ nodeStatus: 'Node Status',
99+ syncMode: 'Sync Mode',
100+ syncPriority: 'Sync Priority',
101+ syncStatus: 'Sync Status',
102+ receiveDelay: 'Receive Delay',
103+ diskDelay: 'Disk Delay',
104+ replayDelay: 'Replay Delay',
105+ performanceMonitoring: 'Performance Monitoring',
106+ memory: 'Memory',
107+ networkReceive: 'Network (Receive)',
108+ networkSend: 'Network (Send)',
109+ diskRead: 'Disk Read',
110+ sqlResponseTime80: '80% SQL Response Time',
111+ sqlResponseTime95: '95% SQL Response Time',
112+ primaryWalAccumulation: 'Primary WAL Accumulation (Daily) (KB)',
113+ primaryWalSendPressure: 'Primary WAL Send Pressure (KB)',
114+ primaryWalWriteRate: 'Primary WAL Write Rate (KB/S)',
115+ standbyReceiveDelay: 'Standby Receive Delay (KB)',
116+ standbyDiskDelay: 'Standby Disk Delay (KB)',
117+ standbyReplayDelay: 'Standby Replay Delay (KB)',
118+ },
119+ },
120+ list: {
121+ name: 'Cluster Name',
122+ state: 'Cluster Status',
123+ stateDesc: 'Status Description',
124+ pointCount: 'Number of Nodes',
125+ user: 'Database User',
126+ posrt: 'Database Port',
127+ deploymentMethod: 'Architecture',
128+ deploymentMethodN: 'Architecture:',
129+ faildNodesNum: 'Number of Failure Nodes:',
130+ searchInstanceName: 'Search instance name',
131+ version: 'Database Version',
132+ versionNum: 'Version Num',
133+ path: 'ENV File Path',
134+ noNodeId: 'Failed to retrieve master node information!',
135+ },
136+ delay: {
137+ nodeName: 'Node Name',
138+ nodeStatus: 'Node Status',
139+ primaryIpPort: 'Primary IP Port',
140+ secondaryIpPort: 'Secondary IP Port',
141+ syncMode: 'Sync Mode',
142+ syncStatus: 'Sync Status',
143+ syncModeN: 'Sync Mode:',
144+ syncStatusN: 'Sync Status:',
145+ syncPriority: 'Sync Priority',
146+ receiveDelay: 'Receive Delay',
147+ diskDelay: 'Disk Delay',
148+ replayDelay: 'Replay Delay',
149+ searchNodeName: 'Search Node Name',
150+ },
151+ },
57 resourceMonitor: {152 resourceMonitor: {
58 memoryTab: 'Memory',153 memoryTab: 'Memory',
59 ioTab: 'IO',154 ioTab: 'IO',
@@ -74,6 +169,7 @@ export default {
74 usedMemory: 'Used Memory',169 usedMemory: 'Used Memory',
75 freeMemory: 'Free Memory',170 freeMemory: 'Free Memory',
76 cachedMemory: 'Cached Memory',171 cachedMemory: 'Cached Memory',
172+ dbMemory: 'Database Memory Usage',
77 interactiveAreaUsage: 'Interactive Area Usage',173 interactiveAreaUsage: 'Interactive Area Usage',
78 totalExchangeArea: 'Total Exchange Area',174 totalExchangeArea: 'Total Exchange Area',
79 ysedSwapArea: 'Used Swap Area',175 ysedSwapArea: 'Used Swap Area',
@@ -119,6 +215,7 @@ export default {
119 costTime: 'Cost Time(s)',215 costTime: 'Cost Time(s)',
120 sessionId: 'Session ID',216 sessionId: 'Session ID',
121 detail: 'detail',217 detail: 'detail',
218+ wdrAnalysis: 'WDR analysis',
122 },219 },
123 trans: {220 trans: {
124 longTransaction: 'LongTransaction',221 longTransaction: 'LongTransaction',
@@ -153,6 +250,20 @@ export default {
153 clientIP: 'Client IP',250 clientIP: 'Client IP',
154 appName: 'Application Name',251 appName: 'Application Name',
155 },252 },
253+ waitEventTab: {
254+ title: 'Wait Events',
255+ blockSessionid: 'Blocked Session ID',
256+ dbName: 'Database Name',
257+ lockmode: 'Lock Mode',
258+ locktag: 'Lock Information',
259+ nodeName: 'Node Name',
260+ queryId: 'Query ID',
261+ sessionid: 'Session ID',
262+ threadName: 'Thread Name',
263+ tid: 'Thread ID',
264+ waitEvent: 'Wait Event',
265+ waitStatus: 'Wait Status',
266+ },
156 trans: {267 trans: {
157 tabTitle: 'Long Transactions',268 tabTitle: 'Long Transactions',
158 longTransaction: 'Long Transaction',269 longTransaction: 'Long Transaction',
@@ -318,6 +429,7 @@ export default {
318 'Number Of Database Avtivity Session',429 'Number Of Database Avtivity Session',
319 'Number Of Database Block Session',430 'Number Of Database Block Session',
320 ],431 ],
432+ clusters: 'Cluster Monitoring',
321 instance: 'Instance Monitoring',433 instance: 'Instance Monitoring',
322 load: 'Performance Load',434 load: 'Performance Load',
323 systemConfig: {435 systemConfig: {
@@ -346,6 +458,8 @@ export default {
346 createSnapshot: 'Create Snapshot',458 createSnapshot: 'Create Snapshot',
347 snapshotID: 'Snapshot ID',459 snapshotID: 'Snapshot ID',
348 captureTime: 'Capture Time',460 captureTime: 'Capture Time',
461+ startTime: 'Snapshot Start Time',
462+ endTime: 'Snapshot End Time',
349 buildSuccess:463 buildSuccess:
350 'Created successfully! There may be a lag in the asynchronous writing of the snapshot list, please refresh the list manually!',464 'Created successfully! There may be a lag in the asynchronous writing of the snapshot list, please refresh the list manually!',
351 },465 },
@@ -355,7 +469,10 @@ export default {
355 build: 'Generate',469 build: 'Generate',
356 buildSuccess: 'Generate suceed',470 buildSuccess: 'Generate suceed',
357 buildFail: 'Generate fail',471 buildFail: 'Generate fail',
472+ startTime: 'Start Time',
473+ endTime: 'End Time',
358 },474 },
475+ wdrErrtip: 'No matching WDR report and corresponding snapshot available for generating WDR report',
359 },476 },
360 session: 'Session Management',477 session: 'Session Management',
361 slow: 'Slow SQL',478 slow: 'Slow SQL',
Mplugins/observability-instance/web-ui/src/i18n/zhCn.ts+117-0
@@ -21,6 +21,7 @@ export default {
21 confirm: '确定',21 confirm: '确定',
22 edit: '编辑',22 edit: '编辑',
23 operate: '操作',23 operate: '操作',
24+ detail: '详情',
24 view: '查看',25 view: '查看',
25 download: '下载',26 download: '下载',
26 reset: '重置',27 reset: '重置',
@@ -34,12 +35,106 @@ export default {
34 autoRefreshFor: '自动刷新:',35 autoRefreshFor: '自动刷新:',
35 needSQLDiagnosis: '请先安装智能诊断插件',36 needSQLDiagnosis: '请先安装智能诊断插件',
36 diagnosis: '智能诊断',37 diagnosis: '智能诊断',
38+ all: '全部',
39+ },
40+ clusterMonitor: {
41+ clusterMonitor: '集群监控',
42+ instanceMonitor: '实例监控',
43+ clusterList: '集群列表',
44+ delayList: '主备延迟列表',
45+ detail: {
46+ title: '集群详情(集群名称=temp)',
47+ info: '集群信息',
48+ instance: {
49+ legend: '图例',
50+ nodeName: '节点名称',
51+ ipPort: 'IP 端口',
52+ role: '角色',
53+ nodeStatus: '节点状态',
54+ syncMode: '同步方式',
55+ syncPriority: '同步优先级',
56+ syncStatus: '同步状态',
57+ receiveDelay: '接收延迟',
58+ diskDelay: '落盘延迟',
59+ replayDelay: '回放延迟',
60+ performanceMonitoring: '性能监控',
61+ memory: '内存',
62+ networkReceive: '网络(接收)',
63+ networkSend: '网络(发送)',
64+ diskRead: '磁盘读',
65+ sqlResponseTime80: '80%的SQL响应时间',
66+ sqlResponseTime95: '95%的SQL响应时间',
67+ primaryWalAccumulation: '主库wal累积量(日)(KB)',
68+ primaryWalSendPressure: '主库wal发送压力(KB)',
69+ primaryWalWriteRate: '主库wal写频率(KB/S)',
70+ standbyReceiveDelay: '备库接收延迟(KB)',
71+ standbyDiskDelay: '备库落盘延迟(KB)',
72+ standbyReplayDelay: '备库回放延迟(KB)',
73+ },
74+ },
75+ list: {
76+ name: '集群名称',
77+ state: '集群状态',
78+ stateDesc: '状态说明',
79+ pointCount: '节点数量',
80+ user: '数据库用户',
81+ posrt: '数据库端口',
82+ deploymentMethod: '架构',
83+ deploymentMethodN: '架构:',
84+ faildNodesNum: '故障节点数:',
85+ searchInstanceName: '搜索实例名称',
86+ version: '数据库版本',
87+ versionNum: '版本号',
88+ path: '环境分离文件路径',
89+ noNodeId: '未获取到主节点信息!',
90+ },
91+ delay: {
92+ nodeName: '节点名称',
93+ nodeStatus: '节点状态',
94+ primaryIpPort: '主库IP 端口',
95+ secondaryIpPort: '备库IP 端口',
96+ syncMode: '同步方式',
97+ syncStatus: '同步状态',
98+ syncModeN: '同步方式:',
99+ syncStatusN: '同步状态:',
100+ syncPriority: '同步优先级',
101+ receiveDelay: '接收延迟',
102+ diskDelay: '落盘延迟',
103+ replayDelay: '回放延迟',
104+ searchNodeName: '搜索节点名称',
105+ },
37 },106 },
38 instanceMonitor: {107 instanceMonitor: {
39 instanceMonitor: '实例监控',108 instanceMonitor: '实例监控',
40 clusterTitle: '集群/实例:',109 clusterTitle: '集群/实例:',
41 index: '首页',110 index: '首页',
42 resourceMonitor: '资源监控',111 resourceMonitor: '资源监控',
112+ thisInstance: ',当前实例',
113+ asp: {
114+ sampleActiveSessionCount: '采样活跃会话数量',
115+ aspAnalysis: 'ASP分析',
116+ analysisMetrics: '分析指标',
117+ filterConditions: '过滤条件',
118+ clickLegendToAddFilter: '单击图例可以增加对应过滤条件',
119+ sampleCount: '采样数量',
120+ activeSessionCount: '活跃会话数量',
121+ },
122+ nodeInfo: {
123+ instanceInfo: '实例信息',
124+ databaseVersion: '数据库版本:',
125+ databaseStartTime: '数据库开始运行时间:',
126+ databaseDataDirectory: '数据库数据目录:',
127+ databaseLogDirectory: '数据库日志目录:',
128+ enableArchiving: '是否开启归档:',
129+ yes: '是',
130+ no: '否',
131+ operatingSystemVersion: '操作系统版本:',
132+ serverCPUManufacturer: '服务器CPU厂商:',
133+ serverCPUModel: '服务器CPU型号:',
134+ serverCPUCoreCount: '服务器CPU核数:',
135+ cores: '核',
136+ totalMemorySize: '内存总大小:',
137+ },
43 instance: {138 instance: {
44 connectionQty: '连接数',139 connectionQty: '连接数',
45 slowSQL3s: '慢SQL数(大于3秒)',140 slowSQL3s: '慢SQL数(大于3秒)',
@@ -74,6 +169,7 @@ export default {
74 usedMemory: '已用内存',169 usedMemory: '已用内存',
75 freeMemory: '空闲内存',170 freeMemory: '空闲内存',
76 cachedMemory: '缓存的内存',171 cachedMemory: '缓存的内存',
172+ dbMemory: '数据库占用内存',
77 interactiveAreaUsage: '交互区使用情况',173 interactiveAreaUsage: '交互区使用情况',
78 totalExchangeArea: '交换区总量',174 totalExchangeArea: '交换区总量',
79 ysedSwapArea: '已用交换区',175 ysedSwapArea: '已用交换区',
@@ -119,6 +215,7 @@ export default {
119 costTime: '耗时(s)',215 costTime: '耗时(s)',
120 sessionId: '会话ID',216 sessionId: '会话ID',
121 detail: '详情',217 detail: '详情',
218+ wdrAnalysis: 'WDR分析',
122 },219 },
123 trans: {220 trans: {
124 longTransaction: '长事务',221 longTransaction: '长事务',
@@ -153,6 +250,20 @@ export default {
153 clientIP: '客户端IP',250 clientIP: '客户端IP',
154 appName: '应用名称',251 appName: '应用名称',
155 },252 },
253+ waitEventTab: {
254+ title: '等待事件',
255+ blockSessionid: '阻塞会话ID',
256+ dbName: '数据库名称',
257+ lockmode: '锁模式',
258+ locktag: '锁信息',
259+ nodeName: '节点名称',
260+ queryId: '查询ID',
261+ sessionid: '会话ID',
262+ threadName: '线程名称',
263+ tid: '线程号',
264+ waitEvent: '等待事件',
265+ waitStatus: '等待状态',
266+ },
156 trans: {267 trans: {
157 longTransaction: '长事务',268 longTransaction: '长事务',
158 sessionID: '会话ID',269 sessionID: '会话ID',
@@ -308,6 +419,7 @@ export default {
308 '数据库活动会话数',419 '数据库活动会话数',
309 '数据库阻塞会话数',420 '数据库阻塞会话数',
310 ],421 ],
422+ clusters: '集群监控',
311 instance: '实例监控',423 instance: '实例监控',
312 load: '系统负载',424 load: '系统负载',
313 systemConfig: {425 systemConfig: {
@@ -336,6 +448,8 @@ export default {
336 createSnapshot: '创建快照',448 createSnapshot: '创建快照',
337 snapshotID: '快照ID',449 snapshotID: '快照ID',
338 captureTime: '捕获时间',450 captureTime: '捕获时间',
451+ startTime: '快照开始时间',
452+ endTime: '快照结束时间',
339 buildSuccess: '创建成功!快照列表异步写入可能存在滞后,请手动刷新列表!',453 buildSuccess: '创建成功!快照列表异步写入可能存在滞后,请手动刷新列表!',
340 },454 },
341 buildWDRDialog: {455 buildWDRDialog: {
@@ -344,7 +458,10 @@ export default {
344 build: '生成',458 build: '生成',
345 buildSuccess: 'WDR生成成功',459 buildSuccess: 'WDR生成成功',
346 buildFail: 'WDR生成失败',460 buildFail: 'WDR生成失败',
461+ startTime: '开始时间',
462+ endTime: '结束时间',
347 },463 },
464+ wdrErrtip: '没有匹配的WDR报告和可生成WDR报告的相应快照',
348 },465 },
349 session: '会话管理',466 session: '会话管理',
350 slow: '慢SQL',467 slow: '慢SQL',
Aplugins/observability-instance/web-ui/src/pages/clusterMonitor/Index.vue+335-0
@@ -0,0 +1,335 @@
1+<template>
2+ <cluster ref="clusterRef" v-if="indexs.showingComponent == 'cluster'" @goback="backToHome"></cluster>
3+ <div class="tab-wrapper" v-show="indexs.showingComponent == 'list'">
4+ <el-container>
5+ <el-main style="position: relative; padding-top: 0px" class="padding-fix">
6+ <div class="page-header" style="padding-left: 20px">
7+ <div class="icon"></div>
8+ <div class="title">{{ $t('clusterMonitor.clusterMonitor') }}</div>
9+ <div class="seperator"></div>
10+ <el-breadcrumb separator="/" style="flex-grow: 1">
11+ <el-breadcrumb-item :to="{ path: '/vem/dashboard/clusters' }">{{
12+ $t('clusterMonitor.clusterMonitor')
13+ }}</el-breadcrumb-item>
14+ </el-breadcrumb>
15+ 
16+ <el-button
17+ class="refresh-button"
18+ type="primary"
19+ :icon="Refresh"
20+ style="padding: 8px"
21+ @click="autoRefreshFn"
22+ />
23+ </div>
24+ 
25+ <div class="main-container">
26+ <el-tabs v-model="dashboardTabKey" class="index-tabs" @tab-change="tabChange">
27+ <el-tab-pane class="min-height" :label="$t('clusterMonitor.clusterList')" name="clusterList">
28+ <div class="filter-bar">
29+ <div class="item">
30+ <div style="white-space: nowrap">{{ $t('clusterMonitor.list.deploymentMethodN') }}</div>
31+ <el-select v-model="selectedArch" style="width: 130px; margin: 0 4px">
32+ <el-option :value="''" :label="$t('app.all')" />
33+ <el-option v-for="item in archSelections" :key="item" :value="item" :label="item" />
34+ </el-select>
35+ </div>
36+ <div class="item">
37+ <el-input
38+ v-model="searchText"
39+ :placeholder="$t('clusterMonitor.list.searchInstanceName')"
40+ :suffix-icon="Search"
41+ style="width: 268px"
42+ />
43+ </div>
44+ </div>
45+ <el-table
46+ :table-layout="'auto'"
47+ :data="filterClusters"
48+ style="width: 100%"
49+ border
50+ :header-cell-class-name="
51+ () => {
52+ return 'grid-header'
53+ }
54+ "
55+ v-loading="loading"
56+ >
57+ <el-table-column :label="$t('clusterMonitor.list.name')" width="200">
58+ <template #default="scope">
59+ <el-link size="small" type="primary" @click="gotoCluster(scope.row)">{{
60+ scope.row.clusterId
61+ }}</el-link>
62+ </template>
63+ </el-table-column>
64+ <el-table-column prop="clusterState.value" :label="$t('clusterMonitor.list.state')" width="130">
65+ <template #default="scope">
66+ <div class="state-row">
67+ <div class="state" :class="[scope.row.clusterState?.color?.toLowerCase()]"></div>
68+ {{ scope.row.clusterState?.value }}
69+ </div>
70+ </template>
71+ </el-table-column>
72+ <el-table-column prop="nodeCount" :label="$t('clusterMonitor.list.pointCount')" width="100" />
73+ <el-table-column prop="arch" :label="$t('clusterMonitor.list.deploymentMethod')" width="100" />
74+ <el-table-column prop="version" :label="$t('clusterMonitor.list.version')" width="130">
75+ <template #default="scope"> {{ scope.row.version }} {{ scope.row.versionNum }} </template>
76+ </el-table-column>
77+ <el-table-column prop="desc" :label="$t('clusterMonitor.list.stateDesc')" show-overflow-tooltip />
78+ <el-table-column :label="$t('app.operate')" align="center" fixed="right" width="80">
79+ <template #default="scope">
80+ <el-link size="small" type="primary" @click="gotoInstance(scope.row)"
81+ >{{ $t('clusterMonitor.instanceMonitor') }}
82+ </el-link>
83+ </template>
84+ </el-table-column>
85+ </el-table>
86+ </el-tab-pane>
87+ <el-tab-pane class="min-height" :label="$t('clusterMonitor.delayList')" name="delayList">
88+ <div class="filter-bar">
89+ <div class="item">
90+ <div style="white-space: nowrap">{{ $t('clusterMonitor.delay.syncModeN') }}</div>
91+ <el-select v-model="selectedSyncMode" style="width: 130px; margin: 0 4px">
92+ <el-option :value="''" :label="$t('app.all')" />
93+ <el-option v-for="item in syncWaySelections" :key="item" :value="item" :label="item" />
94+ </el-select>
95+ </div>
96+ <div class="item">
97+ <div style="white-space: nowrap">{{ $t('clusterMonitor.delay.syncStatusN') }}</div>
98+ <el-select v-model="selectedSyncStatus" style="width: 130px; margin: 0 4px">
99+ <el-option :value="''" :label="$t('app.all')" />
100+ <el-option v-for="item in syncStateSelections" :key="item" :value="item" :label="item" />
101+ </el-select>
102+ </div>
103+ <div class="item">
104+ <el-input
105+ v-model="syncSearchText"
106+ :placeholder="$t('clusterMonitor.list.searchInstanceName')"
107+ :suffix-icon="Search"
108+ style="width: 268px"
109+ />
110+ </div>
111+ </div>
112+ 
113+ <el-table
114+ :table-layout="'auto'"
115+ :data="filterNodes"
116+ style="width: 100%"
117+ border
118+ :header-cell-class-name="
119+ () => {
120+ return 'grid-header'
121+ }
122+ "
123+ v-loading="loadingNodes"
124+ >
125+ <el-table-column :label="$t('clusterMonitor.delay.nodeName')">
126+ <template #default="scope">
127+ <el-link size="small" type="primary" @click="gotoCluster(scope.row)">{{
128+ scope.row.nodeName
129+ }}</el-link>
130+ </template>
131+ </el-table-column>
132+ <el-table-column prop="nodeState.value" :label="$t('clusterMonitor.delay.nodeStatus')" width="80">
133+ <template #default="scope">
134+ <div class="state-row">
135+ <div class="state" :class="[scope.row.nodeState?.color?.toLowerCase()]"></div>
136+ {{ scope.row.nodeState?.value }}
137+ </div>
138+ </template>
139+ </el-table-column>
140+ <el-table-column prop="primaryAddr" :label="$t('clusterMonitor.delay.primaryIpPort')" width="140" />
141+ <el-table-column prop="localAddr" :label="$t('clusterMonitor.delay.secondaryIpPort')" width="140" />
142+ <el-table-column prop="sync" :label="$t('clusterMonitor.delay.syncMode')" width="80" />
143+ <el-table-column prop="syncState.value" :label="$t('clusterMonitor.delay.syncStatus')" width="80">
144+ <template #default="scope">
145+ <div class="state-row">
146+ <div class="state" :class="[scope.row.syncState?.color?.toLowerCase()]"></div>
147+ {{ scope.row.syncState?.value }}
148+ </div>
149+ </template>
150+ </el-table-column>
151+ <el-table-column prop="syncPriority" :label="$t('clusterMonitor.delay.syncPriority')" width="80" />
152+ <el-table-column prop="receivedDelay" :label="$t('clusterMonitor.delay.receiveDelay')" width="100" />
153+ <el-table-column prop="writeDelay" :label="$t('clusterMonitor.delay.diskDelay')" width="100" />
154+ <el-table-column prop="replayDelay" :label="$t('clusterMonitor.delay.replayDelay')" width="100" />
155+ <el-table-column :label="$t('app.operate')" align="center" fixed="right" width="80">
156+ <template #default="scope">
157+ <el-link size="small" type="primary" @click="gotoInstance(scope.row)"
158+ >{{ $t('clusterMonitor.instanceMonitor') }}
159+ </el-link>
160+ </template>
161+ </el-table-column>
162+ </el-table>
163+ </el-tab-pane>
164+ </el-tabs>
165+ </div>
166+ </el-main>
167+ </el-container>
168+ </div>
169+</template>
170+ 
171+<script setup lang="ts">
172+import { ref } from 'vue'
173+import { useRequest } from 'vue-request'
174+import { ClusterListItem, getAllClusters, getAllClustersStates, getAllNodes, NodeStateItem } from '@/api/cluster'
175+import { Search, Refresh } from '@element-plus/icons-vue'
176+import Cluster from '@/pages/clusterMonitor/cluster/Index.vue'
177+import router from '@/router'
178+import type { TabPanelName } from 'element-plus'
179+import { ElMessage } from 'element-plus'
180+import { useI18n } from 'vue-i18n'
181+const { t } = useI18n()
182+const clusterRef = ref()
183+const dashboardTabKey = ref<string>('clusterList')
184+const selectedArch = ref<string>('')
185+const selectedSyncMode = ref<string>('')
186+const selectedSyncStatus = ref<string>('')
187+const searchText = ref<string>('')
188+const syncSearchText = ref<string>('')
189+const indexs = ref<any>({ listOpened: true, clusterOpened: false, showingComponent: 'list' })
190+const clusterData = ref<void | ClusterListItem[]>([])
191+const nodeData = ref<void | NodeStateItem[]>([])
192+ 
193+onMounted(() => {
194+ loadAllClusters()
195+})
196+const gotoCluster = (row: any) => {
197+ indexs.value.clusterOpened = true
198+ indexs.value.showingComponent = 'cluster'
199+ nextTick(() => {
200+ clusterRef.value!.goInto(row)
201+ })
202+}
203+const backToHome = () => {
204+ indexs.value.showingComponent = 'list'
205+}
206+const autoRefreshFn = () => {
207+ if (indexs.value.showingComponent === 'list') {
208+ if (dashboardTabKey.value === 'clusterList') {
209+ loadAllClusters()
210+ }
211+ if (dashboardTabKey.value === 'delayList') {
212+ loadAllNodes()
213+ }
214+ }
215+}
216+const gotoInstance = (row: any) => {
217+ let nodeId = row.nodeId
218+ if (!nodeId) {
219+ ElMessage({
220+ showClose: true,
221+ message: t('clusterMonitor.list.noNodeId'),
222+ type: 'warning',
223+ })
224+ return
225+ }
226+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
227+ if (curMode === 'wujie') {
228+ // @ts-ignore plug-in components
229+ window.$wujie?.props.methods.jump({
230+ name: `Static-pluginObservability-instanceVemDashboardInstance`,
231+ query: {
232+ nodeId,
233+ },
234+ })
235+ } else {
236+ // local
237+ router.push({ path: `/vem/dashboard/instance`, query: { nodeId } })
238+ }
239+}
240+ 
241+const tabChange = (tab: TabPanelName) => {
242+ if (tab === 'clusterList' && clusterData.value?.length === 0) {
243+ loadAllClusters()
244+ }
245+ if (tab === 'delayList' && nodeData.value?.length === 0) {
246+ loadAllNodes()
247+ }
248+}
249+ 
250+const archSelections = computed(() => {
251+ const result: string[] = Array.from(new Set(clusterData.value?.map((obj: ClusterListItem) => obj.arch)))
252+ return result
253+})
254+const syncWaySelections = computed(() => {
255+ const result: string[] = Array.from(new Set(nodeData.value?.map((obj: NodeStateItem) => obj.sync)))
256+ return result
257+})
258+const syncStateSelections = computed(() => {
259+ const result: string[] = Array.from(new Set(nodeData.value?.map((obj: NodeStateItem) => obj.syncState.value)))
260+ return result
261+})
262+ 
263+const filterClusters = computed(() => {
264+ return clusterData.value?.filter((obj: ClusterListItem) => {
265+ let result = true
266+ if (selectedArch.value !== '' && obj.arch !== selectedArch.value) result = false
267+ if (searchText.value !== '' && obj.clusterId.indexOf(searchText.value) < 0) result = false
268+ return result
269+ })
270+})
271+ 
272+const filterNodes = computed(() => {
273+ return nodeData.value?.filter((obj: NodeStateItem) => {
274+ let result = true
275+ if (selectedSyncMode.value !== '' && obj.sync !== selectedSyncMode.value) result = false
276+ if (selectedSyncStatus.value !== '' && obj.syncState.value !== selectedSyncStatus.value) result = false
277+ if (syncSearchText.value !== '' && obj.nodeName.indexOf(syncSearchText.value) < 0) result = false
278+ return result
279+ })
280+})
281+ 
282+const {
283+ data: allClusters,
284+ run: loadAllClusters,
285+ loading,
286+} = useRequest(
287+ () => {
288+ clusterData.value = []
289+ return getAllClusters()
290+ },
291+ { manual: true }
292+)
293+watch(allClusters, () => {
294+ if (!allClusters.value) return
295+ clusterData.value = allClusters.value
296+ loadAllClustersStates()
297+})
298+const { data: allClusterStates, run: loadAllClustersStates } = useRequest(getAllClustersStates, { manual: true })
299+watch(
300+ allClusterStates,
301+ () => {
302+ if (!allClusterStates.value) return
303+ allClusterStates.value.forEach((objA) => {
304+ const matchedObjB = clusterData.value?.find((objB) => objB.clusterId === objA.clusterId)
305+ if (matchedObjB) {
306+ matchedObjB.clusterState = objA.clusterState
307+ matchedObjB.desc = objA.desc
308+ matchedObjB.nodeId = objA.primaryNodeId
309+ }
310+ })
311+ },
312+ { deep: true }
313+)
314+const {
315+ data: allNodes,
316+ run: loadAllNodes,
317+ loading: loadingNodes,
318+} = useRequest(
319+ () => {
320+ nodeData.value = []
321+ return getAllNodes()
322+ },
323+ { manual: true }
324+)
325+watch(
326+ allNodes,
327+ () => {
328+ if (!allNodes.value) return
329+ nodeData.value = allNodes.value
330+ },
331+ { deep: true }
332+)
333+</script>
334+ 
335+<style scoped lang="scss"></style>
Aplugins/observability-instance/web-ui/src/pages/clusterMonitor/cluster/Index.vue+595-0
@@ -0,0 +1,595 @@
1+<template>
2+ <div class="tab-wrapper">
3+ <el-container>
4+ <el-main style="position: relative; padding-top: 0px" class="padding-fix">
5+ <div class="page-header" style="padding-left: 20px">
6+ <div class="icon"></div>
7+ <div class="title">{{ $t('clusterMonitor.clusterMonitor') }}</div>
8+ <div class="seperator"></div>
9+ <el-breadcrumb separator="/" style="flex-grow: 1">
10+ <el-breadcrumb-item>
11+ <div @click="goback">
12+ <a>{{ $t('clusterMonitor.clusterMonitor') }}</a>
13+ </div>
14+ </el-breadcrumb-item>
15+ <el-breadcrumb-item>{{
16+ $t('clusterMonitor.detail.title').replace('temp', clusterRow.clusterId)
17+ }}</el-breadcrumb-item>
18+ </el-breadcrumb>
19+ 
20+ <el-button
21+ class="refresh-button"
22+ type="primary"
23+ :icon="Refresh"
24+ style="padding: 8px"
25+ @click="loadClusterNodes(clusterRow.clusterId)"
26+ />
27+ </div>
28+ 
29+ <div class="main-container">
30+ <my-card :title="$t('clusterMonitor.detail.info')" :bodyPadding="false" skipBodyHeight>
31+ <el-table
32+ :data="clusterNodesData"
33+ style="width: 100%"
34+ border
35+ :header-cell-class-name="
36+ () => {
37+ return 'grid-header'
38+ }
39+ "
40+ @selection-change="selectionChange"
41+ v-loading="loadingNodes"
42+ >
43+ <el-table-column
44+ type="selection"
45+ prop="xxxx"
46+ :label="$t('clusterMonitor.detail.instance.legend')"
47+ align="center"
48+ width="40"
49+ />
50+ <el-table-column :label="$t('clusterMonitor.detail.instance.legend')" align="center" width="50">
51+ <template #default="scope">
52+ <div style="width: 100%; display: flex; flex-direction: row; justify-content: center">
53+ <div
54+ style="width: 12px; height: 12px; border-radius: 2px"
55+ :style="{ background: scope.row.color }"
56+ ></div>
57+ </div>
58+ </template>
59+ </el-table-column>
60+ <el-table-column prop="nodeName" :label="$t('clusterMonitor.detail.instance.nodeName')" />
61+ <el-table-column prop="localAddr" :label="$t('clusterMonitor.detail.instance.ipPort')" width="140" />
62+ <el-table-column prop="role" :label="$t('clusterMonitor.detail.instance.role')" width="60" />
63+ <el-table-column
64+ prop="nodeState.value"
65+ :label="$t('clusterMonitor.detail.instance.nodeStatus')"
66+ width="80"
67+ >
68+ <template #default="scope">
69+ <div class="state-row">
70+ <div class="state" :class="[scope.row.nodeState?.color?.toLowerCase()]"></div>
71+ {{ scope.row.nodeState?.value }}
72+ </div>
73+ </template>
74+ </el-table-column>
75+ <el-table-column prop="cmServerState.value" label="CM_server" width="90">
76+ <template #default="scope">
77+ <div class="state-row">
78+ <div class="state" :class="[scope.row.cmServerState?.color?.toLowerCase()]"></div>
79+ {{ scope.row.cmServerState?.value }}
80+ </div>
81+ </template>
82+ </el-table-column>
83+ <el-table-column prop="omMonitorState.value" label="OM_moniter" width="100">
84+ <template #default="scope">
85+ <div class="state-row">
86+ <div class="state" :class="[scope.row.omMonitorState?.color?.toLowerCase()]"></div>
87+ {{ scope.row.omMonitorState?.value }}
88+ </div>
89+ </template>
90+ </el-table-column>
91+ <el-table-column prop="cmAgentState.value" label="CM_agent" width="80">
92+ <template #default="scope">
93+ <div class="state-row">
94+ <div class="state" :class="[scope.row.cmAgentState?.color?.toLowerCase()]"></div>
95+ {{ scope.row.cmAgentState?.value }}
96+ </div>
97+ </template>
98+ </el-table-column>
99+ <el-table-column prop="sync" :label="$t('clusterMonitor.detail.instance.syncMode')" width="80" />
100+ <el-table-column
101+ prop="syncPriority"
102+ :label="$t('clusterMonitor.detail.instance.syncPriority')"
103+ width="80"
104+ />
105+ 
106+ <el-table-column
107+ prop="syncState.value"
108+ :label="$t('clusterMonitor.detail.instance.syncStatus')"
109+ width="80"
110+ >
111+ <template #default="scope">
112+ <div class="state-row">
113+ <div class="state" :class="[scope.row.syncState?.color?.toLowerCase()]"></div>
114+ {{ scope.row.syncState?.value }}
115+ </div>
116+ </template>
117+ </el-table-column>
118+ <el-table-column
119+ prop="receivedDelay"
120+ :label="$t('clusterMonitor.detail.instance.receiveDelay')"
121+ width="80"
122+ />
123+ <el-table-column prop="writeDelay" :label="$t('clusterMonitor.detail.instance.diskDelay')" width="80" />
124+ <el-table-column
125+ prop="replayDelay"
126+ :label="$t('clusterMonitor.detail.instance.replayDelay')"
127+ width="80"
128+ />
129+ <el-table-column :label="$t('app.operate')" align="center" fixed="right" width="80">
130+ <template #default="scope">
131+ <el-link size="small" type="primary" @click="gotoInstance(scope.row)">{{
132+ $t('clusterMonitor.detail.instance.performanceMonitoring')
133+ }}</el-link>
134+ </template>
135+ </el-table-column>
136+ </el-table>
137+ 
138+ <div style="padding: 12px" v-loading="loading">
139+ <el-row :gutter="12">
140+ <el-col :span="6">
141+ <my-card :title="'CPU'" height="200" :bodyPadding="false">
142+ <LazyLine
143+ :tabId="tabId"
144+ :formatter="toFixed"
145+ :data="metricsData.cpu"
146+ :xData="metricsData.time"
147+ :legendShown="false"
148+ :tool-tips-sort="'desc'"
149+ :tool-tips-exclude-zero="true"
150+ :max="100"
151+ :min="0"
152+ :interval="25"
153+ :unit="'%'"
154+ />
155+ </my-card>
156+ </el-col>
157+ <el-col :span="6">
158+ <my-card :title="$t('clusterMonitor.detail.instance.memory')" height="200" :bodyPadding="false">
159+ <LazyLine
160+ :tabId="tabId"
161+ :formatter="toFixed"
162+ :data="metricsData.memory"
163+ :xData="metricsData.time"
164+ :legendShown="false"
165+ :tool-tips-sort="'desc'"
166+ :tool-tips-exclude-zero="true"
167+ :max="100"
168+ :min="0"
169+ :interval="25"
170+ :unit="'%'"
171+ />
172+ </my-card>
173+ </el-col>
174+ <el-col :span="6">
175+ <my-card
176+ :title="$t('clusterMonitor.detail.instance.networkReceive')"
177+ height="200"
178+ :bodyPadding="false"
179+ >
180+ <LazyLine
181+ :tabId="tabId"
182+ :formatter="toFixed"
183+ :data="metricsData.networkIn"
184+ :xData="metricsData.time"
185+ :legendShown="false"
186+ :tool-tips-sort="'desc'"
187+ :tool-tips-exclude-zero="true"
188+ :unit="'M/S'"
189+ />
190+ </my-card>
191+ </el-col>
192+ <el-col :span="6">
193+ <my-card :title="$t('clusterMonitor.detail.instance.networkSend')" height="200" :bodyPadding="false">
194+ <LazyLine
195+ :tabId="tabId"
196+ :formatter="toFixed"
197+ :data="metricsData.networkOut"
198+ :xData="metricsData.time"
199+ :legendShown="false"
200+ :tool-tips-sort="'desc'"
201+ :tool-tips-exclude-zero="true"
202+ :unit="'M/S'"
203+ />
204+ </my-card>
205+ </el-col>
206+ </el-row>
207+ 
208+ <div class="gap-row"></div>
209+ <el-row :gutter="12">
210+ <el-col :span="6">
211+ <my-card :title="$t('clusterMonitor.detail.instance.diskRead')" height="200" :bodyPadding="false">
212+ <LazyLine
213+ :tabId="tabId"
214+ :formatter="toFixed"
215+ :data="metricsData.io"
216+ :xData="metricsData.time"
217+ :legendShown="false"
218+ :tool-tips-sort="'desc'"
219+ :tool-tips-exclude-zero="true"
220+ :unit="'B'"
221+ />
222+ </my-card>
223+ </el-col>
224+ <el-col :span="6">
225+ <my-card :title="'QPS'" height="200" :bodyPadding="false">
226+ <LazyLine
227+ :tabId="tabId"
228+ :formatter="toFixed"
229+ :data="metricsData.qps"
230+ :xData="metricsData.time"
231+ :legendShown="false"
232+ :tool-tips-sort="'desc'"
233+ :tool-tips-exclude-zero="true"
234+ />
235+ </my-card>
236+ </el-col>
237+ <el-col :span="6">
238+ <my-card
239+ :title="$t('clusterMonitor.detail.instance.sqlResponseTime80')"
240+ height="200"
241+ :bodyPadding="false"
242+ >
243+ <LazyLine
244+ :tabId="tabId"
245+ :formatter="toFixed"
246+ :data="metricsData.sql80"
247+ :xData="metricsData.time"
248+ :legendShown="false"
249+ :tool-tips-sort="'desc'"
250+ :tool-tips-exclude-zero="true"
251+ :unit="'ms'"
252+ />
253+ </my-card>
254+ </el-col>
255+ <el-col :span="6">
256+ <my-card
257+ :title="$t('clusterMonitor.detail.instance.sqlResponseTime95')"
258+ height="200"
259+ :bodyPadding="false"
260+ >
261+ <LazyLine
262+ :tabId="tabId"
263+ :formatter="toFixed"
264+ :data="metricsData.sql95"
265+ :xData="metricsData.time"
266+ :legendShown="false"
267+ :tool-tips-sort="'desc'"
268+ :tool-tips-exclude-zero="true"
269+ :unit="'ms'"
270+ />
271+ </my-card>
272+ </el-col>
273+ </el-row>
274+ 
275+ <div class="gap-row"></div>
276+ <el-row :gutter="12">
277+ <el-col :span="8">
278+ <my-card
279+ :title="$t('clusterMonitor.detail.instance.primaryWalAccumulation')"
280+ height="200"
281+ :bodyPadding="false"
282+ >
283+ <LazyLine
284+ :tabId="tabId"
285+ :formatter="toFixed"
286+ :data="metricsData.writeTotal"
287+ :xData="metricsData.writeTotalTime"
288+ :xFormater="'MM-DD'"
289+ :legendShown="false"
290+ :tool-tips-sort="'desc'"
291+ :tool-tips-exclude-zero="true"
292+ />
293+ </my-card>
294+ </el-col>
295+ <el-col :span="8">
296+ <my-card
297+ :title="$t('clusterMonitor.detail.instance.primaryWalSendPressure')"
298+ height="200"
299+ :bodyPadding="false"
300+ >
301+ <LazyLine
302+ :tabId="tabId"
303+ :formatter="toFixed"
304+ :data="metricsData.sendPressure"
305+ :xData="metricsData.time"
306+ :legendShown="false"
307+ :tool-tips-sort="'desc'"
308+ :tool-tips-exclude-zero="true"
309+ />
310+ </my-card>
311+ </el-col>
312+ <el-col :span="8">
313+ <my-card
314+ :title="$t('clusterMonitor.detail.instance.primaryWalWriteRate')"
315+ height="200"
316+ :bodyPadding="false"
317+ >
318+ <LazyLine
319+ :tabId="tabId"
320+ :formatter="toFixed"
321+ :data="metricsData.writePerSecond"
322+ :xData="metricsData.time"
323+ :legendShown="false"
324+ :tool-tips-sort="'desc'"
325+ :tool-tips-exclude-zero="true"
326+ />
327+ </my-card>
328+ </el-col>
329+ </el-row>
330+ 
331+ <div class="gap-row"></div>
332+ <el-row :gutter="12">
333+ <el-col :span="8">
334+ <my-card
335+ :title="$t('clusterMonitor.detail.instance.standbyReceiveDelay')"
336+ height="200"
337+ :bodyPadding="false"
338+ >
339+ <LazyLine
340+ :tabId="tabId"
341+ :formatter="toFixed"
342+ :data="metricsData.receivedDelay"
343+ :xData="metricsData.time"
344+ :legendShown="false"
345+ :tool-tips-sort="'desc'"
346+ :tool-tips-exclude-zero="true"
347+ />
348+ </my-card>
349+ </el-col>
350+ <el-col :span="8">
351+ <my-card
352+ :title="$t('clusterMonitor.detail.instance.standbyDiskDelay')"
353+ height="200"
354+ :bodyPadding="false"
355+ >
356+ <LazyLine
357+ :tabId="tabId"
358+ :formatter="toFixed"
359+ :data="metricsData.writeDelay"
360+ :xData="metricsData.time"
361+ :legendShown="false"
362+ :tool-tips-sort="'desc'"
363+ :tool-tips-exclude-zero="true"
364+ />
365+ </my-card>
366+ </el-col>
367+ <el-col :span="8">
368+ <my-card
369+ :title="$t('clusterMonitor.detail.instance.standbyReplayDelay')"
370+ height="200"
371+ :bodyPadding="false"
372+ >
373+ <LazyLine
374+ :tabId="tabId"
375+ :formatter="toFixed"
376+ :data="metricsData.replayDelay"
377+ :xData="metricsData.time"
378+ :legendShown="false"
379+ :tool-tips-sort="'desc'"
380+ :tool-tips-exclude-zero="true"
381+ />
382+ </my-card>
383+ </el-col>
384+ </el-row>
385+ </div>
386+ </my-card>
387+ </div>
388+ </el-main>
389+ </el-container>
390+ </div>
391+</template>
392+ 
393+<script setup lang="ts">
394+import { ref } from 'vue'
395+import { useRequest } from 'vue-request'
396+import { getClusterDetails, getClusterNodes, ClusterNode } from '@/api/cluster'
397+import { toFixed, uuid } from '@/shared'
398+import { ElMessage } from 'element-plus'
399+import { useI18n } from 'vue-i18n'
400+import colorCharts from '@/assets/style/color.module.scss'
401+import router from '@/router'
402+import { Refresh } from '@element-plus/icons-vue'
403+const { t } = useI18n()
404+const tabId = ref<string>(uuid())
405+const clusterRow = ref<any>({ clusterId: '' })
406+const clusterNodesData = ref<ClusterNode[]>([])
407+ 
408+interface LineData {
409+ name: string
410+ data: any[]
411+ [other: string]: any
412+}
413+interface MetricsData {
414+ cpu: LineData[]
415+ memory: LineData[]
416+ networkIn: LineData[]
417+ networkOut: LineData[]
418+ io: LineData[]
419+ qps: LineData[]
420+ sql80: LineData[]
421+ sql95: LineData[]
422+ sendPressure: LineData[]
423+ writePerSecond: LineData[]
424+ receivedDelay: LineData[]
425+ writeDelay: LineData[]
426+ replayDelay: LineData[]
427+ writeTotal: LineData[]
428+ writeTotalTime: string[]
429+ time: string[]
430+}
431+const defaultData = {
432+ cpu: [],
433+ memory: [],
434+ networkIn: [],
435+ networkOut: [],
436+ io: [],
437+ qps: [],
438+ sql80: [],
439+ sql95: [],
440+ sendPressure: [],
441+ writePerSecond: [],
442+ receivedDelay: [],
443+ writeDelay: [],
444+ replayDelay: [],
445+ writeTotal: [],
446+ writeTotalTime: [],
447+ time: [],
448+}
449+const metricsData = ref<MetricsData>(defaultData)
450+const colorArray: Array<string> = colorCharts.chartColors.split(',')
451+ 
452+onMounted(() => {})
453+ 
454+const myEmit = defineEmits(['goback'])
455+const goback = () => {
456+ myEmit('goback')
457+}
458+const goInto = (row: any) => {
459+ clusterRow.value = row
460+ loadClusterNodes(clusterRow.value.clusterId)
461+}
462+defineExpose({ goInto })
463+ 
464+const gotoInstance = (row: any) => {
465+ let nodeId = row.nodeId
466+ if (!nodeId) {
467+ ElMessage({
468+ showClose: true,
469+ message: t('clusterMonitor.list.noNodeId'),
470+ type: 'warning',
471+ })
472+ return
473+ }
474+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
475+ if (curMode === 'wujie') {
476+ // @ts-ignore plug-in components
477+ window.$wujie?.props.methods.jump({
478+ name: `Static-pluginObservability-instanceVemDashboardInstance`,
479+ query: {
480+ nodeId,
481+ },
482+ })
483+ } else {
484+ // local
485+ router.push({ path: `/vem/dashboard/instance`, query: { nodeId } })
486+ }
487+}
488+const selectionChange = (selection: Array) => {
489+ if (selection.length <= 0) {
490+ for (let prop in metricsData.value) {
491+ if (Object.prototype.hasOwnProperty.call(metricsData.value, prop)) {
492+ metricsData.value[prop].forEach((element) => {
493+ element.lineStyle = undefined
494+ })
495+ }
496+ }
497+ } else {
498+ for (let prop in metricsData.value) {
499+ if (Object.prototype.hasOwnProperty.call(metricsData.value, prop)) {
500+ metricsData.value[prop].forEach((element) => {
501+ if (
502+ selection.some((item: any) => {
503+ return item.nodeId === element.name
504+ })
505+ ) {
506+ element.lineStyle = undefined
507+ } else {
508+ element.lineStyle = {
509+ opacity: 0.1,
510+ }
511+ }
512+ })
513+ }
514+ }
515+ }
516+}
517+const { data: allClusters, run: loadAllClusters, loading } = useRequest(getClusterDetails, { manual: true })
518+watch(
519+ allClusters,
520+ () => {
521+ // clear data
522+ metricsData.value = JSON.parse(JSON.stringify(defaultData))
523+ 
524+ if (!allClusters.value) return
525+ 
526+ // get sort
527+ let nodeIds = clusterNodesData.value.map((node) => node.nodeId)
528+ 
529+ let responseKeys = [
530+ ['CPU', 'cpu'], // cpu
531+ ['MEMORY', 'memory'], // memory
532+ ['NETWORK_IN_TOTAL', 'networkIn'], // network
533+ ['NETWORK_OUT_TOTAL', 'networkOut'], // network
534+ ['IOPS_R_TOTAL', 'io'], // io
535+ ['INSTANCE_QPS', 'qps'], // qps
536+ ['INSTANCE_DB_RESPONSETIME_P80', 'sql80'], // 80%
537+ ['INSTANCE_DB_RESPONSETIME_P95', 'sql95'], // 95%
538+ ['CLUSTER_PRIMARY_WAL_SEND_PRESSURE', 'sendPressure'], // Send Pressure
539+ ['CLUSTER_PRIMARY_WAL_WRITE_PER_SEC', 'writePerSecond'], // write per sencend
540+ ['CLUSTER_WAL_RECEIVED_DELAY', 'receivedDelay'], // received delay
541+ ['CLUSTER_WAL_WRITE_DELAY', 'writeDelay'], // write delay
542+ ['CLUSTER_WAL_REPLAY_DELAY', 'replayDelay'], // replay delay
543+ ['CLUSTER_PRIMARY_WAL_WRITE_TOTAL', 'writeTotal'], // write total daily
544+ ]
545+ responseKeys.forEach((responseKeyArray) => {
546+ for (let key in nodeIds) {
547+ let tempData: string[] = []
548+ if (allClusters.value[responseKeyArray[0]][nodeIds[key]]) {
549+ allClusters.value[responseKeyArray[0]][nodeIds[key]].forEach((element) => {
550+ if (responseKeyArray[0] === 'NETWORK_IN_TOTAL' || responseKeyArray[0] === 'NETWORK_OUT_TOTAL') {
551+ tempData.push(toFixed(element / 1024 / 1024, 1))
552+ } else if (
553+ responseKeyArray[0] === 'INSTANCE_DB_RESPONSETIME_P80' ||
554+ responseKeyArray[0] === 'INSTANCE_DB_RESPONSETIME_P95'
555+ ) {
556+ tempData.push(toFixed(element / 1000, 1))
557+ } else tempData.push(toFixed(element))
558+ })
559+ metricsData.value[responseKeyArray[1]].push({ data: tempData, name: nodeIds[key], key: nodeIds[key] })
560+ } else {
561+ metricsData.value[responseKeyArray[1]].push({ data: [], name: nodeIds[key], key: nodeIds[key] })
562+ }
563+ }
564+ })
565+ 
566+ // time
567+ metricsData.value.time = allClusters.value.time
568+ metricsData.value.writeTotalTime = allClusters.value.CLUSTER_PRIMARY_WAL_WRITE_TOTAL_TIME
569+ },
570+ { deep: true }
571+)
572+ 
573+const {
574+ data: clusterNodes,
575+ run: loadClusterNodes,
576+ loading: loadingNodes,
577+} = useRequest(getClusterNodes, { manual: true })
578+watch(
579+ clusterNodes,
580+ () => {
581+ if (!clusterNodes.value) return
582+ 
583+ clusterNodesData.value = clusterNodes.value
584+ 
585+ for (let index = 0; index < clusterNodesData.value.length; index++) {
586+ const element = clusterNodesData.value[index]
587+ element.color = colorArray[index]
588+ }
589+ loadAllClusters(clusterRow.value.clusterId)
590+ },
591+ { deep: true }
592+)
593+</script>
594+ 
595+<style scoped lang="scss"></style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/Index.vue+0-359
@@ -1,359 +0,0 @@
1-<script setup lang="ts">
2-import { Fold, Expand } from "@element-plus/icons-vue";
3-import { Refresh } from "@element-plus/icons-vue";
4-import { storeToRefs } from "pinia";
5-import { useI18n } from "vue-i18n";
6-import { useMonitorStore } from "../../store/monitor";
7-import { useWindowStore } from "../../store/window";
8-import PerformanceLoad from "./performance_load/Index.vue";
9-import TopSql from "./topSQL/Index.vue";
10-import Wdr from "./wdr/Index.vue";
11-import { i18n } from "../../i18n";
12-import ogRequest from "../../request";
13-import { useRequest } from "vue-request";
14-import Install from "./install/Index.vue";
15-import SystemConfiguration from "./system_configuration/Index.vue";
16- 
17-const { t } = useI18n();
18- 
19-type Res =
20- | [
21- {
22- [propName: string]: string | number;
23- }
24- ]
25- | undefined;
26- 
27-const datePickerRef = ref<HTMLDivElement>();
28-const clusterNodeId = ref();
29-const clusterList = ref<Array<any>[]>([]);
30-const connectStatus = ref<boolean | undefined>(undefined);
31-const curServerInfoText = ref("");
32-const nodeVersion = ref<string>("");
33-const lastNodeId = ref<string>("");
34-const wdrComponent = ref(null);
35-const paramConfigComponent = ref(null);
36- 
37-const { serverInfoText } = storeToRefs(useWindowStore());
38-const { tab, filters, autoRefresh, rangeTime, instanceId } = storeToRefs(useMonitorStore());
39-// tab render only once
40-const tabLoaded = reactive([tab.value === 0, tab.value === 1, tab.value === 2, tab.value === 3]);
41-watch(tab, (v) => {
42- if (!tabLoaded[v]) {
43- tabLoaded[v] = true;
44- }
45- if (v === 0 || v === 1) {
46- nextTick(() => {
47- if (nodeIdChangedByOtherTabs.value !== "") {
48- clusterNodeId.value = nodeIdChangedByOtherTabs.value;
49- nodeIdChangedByOtherTabs.value = "";
50- }
51- });
52- }
53- if (v === 2) {
54- nextTick(() => {
55- wdrComponent.value.syncNodeId(lastNodeId.value);
56- });
57- }
58- if (v === 3) {
59- nextTick(() => {
60- paramConfigComponent.value.syncNodeId(lastNodeId.value);
61- });
62- }
63-});
64-const isCollapse = ref(true);
65-const toggleCollapse = () => {
66- isCollapse.value = !isCollapse.value;
67-};
68- 
69-// nodeId sync
70-const nodeIdChangedByOtherTabs = ref<string>("");
71-const nodeIdChanged = (nodeId: any) => {
72- nodeIdChangedByOtherTabs.value = nodeId;
73- lastNodeId.value = nodeId;
74-};
75- 
76-const autoRefreshFn = () => {
77- autoRefresh.value = !autoRefresh.value;
78-};
79- 
80-const { data: opsClusterData } = useRequest(() => ogRequest.get("/observability/v1/topsql/cluster"), { manual: false });
81- 
82-const { data: connectStatusData, run: runConnectStatus, loading: connectStatusLoadding } = useRequest((nodeId: String) => ogRequest.get(`/observability/v1/topsql/connect/${nodeId}`), { manual: true });
83- 
84-const treeTransform = (arr: any) => {
85- let obj: any = [];
86- if (arr instanceof Array) {
87- arr.forEach((item) => {
88- // init current cluster node
89- if (item.nodeId && item.nodeId === instanceId.value) {
90- clusterNodeId.value = instanceId.value;
91- }
92- obj.push({
93- label: item.clusterId ? item.clusterId : (item.azName ? item.azName + "_" : "") + item.publicIp + ":" + item.dbPort + (item.clusterRole ? "(" + item.clusterRole + ")" : ""),
94- value: item.clusterId ? item.clusterId : item.nodeId,
95- children: treeTransform(item.clusterNodes),
96- });
97- });
98- }
99- return obj;
100-};
101- 
102-const showConnectStatus = (status: boolean | undefined) => {
103- if (status === undefined) {
104- return "";
105- }
106- return status ? t("dashboard.connectStatus.success") : t("dashboard.connectStatus.error");
107-};
108- 
109-const onDatePackerVisible = (v: boolean) => {
110- if (!v) {
111- const docu = document.getElementsByClassName("el-range-input");
112- // @ts-ignore
113- docu[0]?.blur();
114- }
115-};
116- 
117-const getVersionByNodeId = (curNodeId: string) => {
118- if (!Array.isArray(opsClusterData.value)) {
119- return "";
120- }
121- for (let i = 0; i < opsClusterData.value.length; i += 1) {
122- const version = opsClusterData.value[i].version || "";
123- const children = opsClusterData.value[i].clusterNodes;
124- if (!Array.isArray(children)) {
125- continue;
126- }
127- for (let j = 0; j < children.length; j += 1) {
128- if (children[j]?.nodeId === curNodeId) {
129- return version.toLocaleUpperCase();
130- }
131- }
132- }
133- return "";
134-};
135- 
136-watch(opsClusterData, (res: Res) => {
137- if (res && Object.keys(res).length) {
138- clusterList.value = treeTransform(res);
139- }
140-});
141- 
142-watch(clusterNodeId, (res) => {
143- let curInstanceId = instanceId.value;
144- if (typeof res === "string") {
145- curInstanceId = res;
146- } else if (Array.isArray(res) && res.length > 0) {
147- curInstanceId = res[res.length - 1];
148- }
149- 
150- lastNodeId.value = curInstanceId;
151- // get connection status
152- runConnectStatus(curInstanceId);
153- // set instanceId value
154- instanceId.value = curInstanceId;
155- useMonitorStore().instanceId = curInstanceId;
156- // useMonitorStore().tab = 0;
157- nodeVersion.value = getVersionByNodeId(curInstanceId);
158-});
159- 
160-watch(connectStatusData, (res) => {
161- connectStatus.value = res;
162-});
163- 
164-watch(rangeTime, (r) => {
165- if (r !== -1) {
166- filters.value[tab.value].time = null;
167- }
168-});
169- 
170-watch(serverInfoText, (val) => {
171- if (typeof val === "string" && val !== "") {
172- curServerInfoText.value = val;
173- } else {
174- curServerInfoText.value = "";
175- }
176-});
177-</script>
178- 
179-<template>
180- <div class="tab-wrapper" :key="clusterNodeId">
181- <el-container>
182- <el-aside :width="isCollapse ? '0px' : '300px'">
183- <div style="height: 13px"></div>
184- <Install />
185- </el-aside>
186- <el-main style="position: relative;padding-top: 0px;">
187- <div>
188- <div style="position: absolute; left: 10px; top: 11px; z-index: 9999" @click="toggleCollapse">
189- <el-icon v-if="!isCollapse" size="20px"><Fold /></el-icon>
190- <el-icon v-if="isCollapse" size="20px"><Expand /></el-icon>
191- </div>
192- </div>
193- <el-tabs v-model="tab">
194- <div class="tab-wrapper-container" v-show="tab === 0 || tab === 1">
195- <div class="cluster-container">
196- <div class="cluster-container-title">{{ $t("datasource.cluterTitle") }}</div>
197- <el-cascader v-model="clusterNodeId" :options="clusterList" />
198- <div v-if="false" class="divider" />
199- <div class="cluster-info-loading" v-if="false && connectStatusLoadding" v-loading="connectStatusLoadding"></div>
200- <div class="cluster-info" v-if="false && connectStatus !== undefined && !connectStatusLoadding">
201- <span class="cluster-info-light" :style="{ backgroundColor: connectStatus ? 'green' : 'red' }" />
202- <span>{{ showConnectStatus(connectStatus) }}</span>
203- </div>
204- <div v-if="false && curServerInfoText !== ''" class="divider" />
205- <div v-if="false && curServerInfoText !== ''">{{ serverInfoText }}</div>
206- </div>
207- <div class="tab-wrapper-filter">
208- <span>{{ $t("app.autoRefresh") }}:</span>
209- <el-select v-model="filters[tab].refreshTime" style="width: 60px; margin: 0 4px">
210- <el-option :value="15" label="15s" />
211- <el-option :value="30" label="30s" />
212- <el-option :value="60" label="60s" />
213- </el-select>
214- <el-button type="primary" :icon="Refresh" style="padding: 8px" @click="autoRefreshFn" />
215- <div class="divider"></div>
216- <span>{{ $t("dashboard.range") }}:</span>
217- <el-select v-model="filters[tab].rangeTime" :style="{ width: i18n.global.locale.value === 'en' ? '115px' : '85px' }">
218- <el-option :value="1" :label="$t('dashboard.last1H')" />
219- <el-option :value="12" :label="$t('dashboard.last12H')" />
220- <el-option :value="24" :label="$t('dashboard.last1D')" />
221- <el-option :value="48" :label="$t('dashboard.last2D')" />
222- <el-option :value="168" :label="$t('dashboard.last7D')" />
223- <el-option :value="-1" :label="$t('app.custom')" />
224- </el-select>
225- <el-date-picker ref="datePickerRef" :disabled="filters[tab].rangeTime !== -1" type="datetimerange" v-model="filters[tab].time" :start-placeholder="$t('app.startDate')" :end-placeholder="$t('app.endDate')" :range-separator="$t('app.to')" @visible-change="onDatePackerVisible" />
226- </div>
227- </div>
228- 
229- <el-tab-pane :label="$t('dashboard.load')" :name="0">
230- <performance-load v-if="tabLoaded[0] || tab === 0" :nodeVersion="nodeVersion" />
231- </el-tab-pane>
232- <el-tab-pane :label="$t('dashboard.top')" :name="1">
233- <top-sql v-if="tabLoaded[1] || tab === 1" :instanceId="instanceId" />
234- </el-tab-pane>
235- <el-tab-pane :label="$t('dashboard.wdrReports.tabName')" :name="2">
236- <wdr @nodeIdChanged="nodeIdChanged" ref="wdrComponent" v-if="tabLoaded[2] || tab === 2" :instanceId="instanceId" />
237- </el-tab-pane>
238- <el-tab-pane :label="$t('dashboard.systemConfig.tabName')" :name="3">
239- <system-configuration @nodeIdChanged="nodeIdChanged" ref="paramConfigComponent" v-if="tabLoaded[3] || tab === 3" :instanceId="instanceId" />
240- </el-tab-pane>
241- </el-tabs>
242- </el-main>
243- </el-container>
244- </div>
245-</template>
246- 
247-<style scoped lang="scss">
248-.cluster-container {
249- height: 40px;
250- // background-color: var(--el-bg-color-sub);
251- padding: 0 16px;
252- display: flex;
253- align-items: center;
254- 
255- &-title {
256- font-size: 14px;
257- margin-right: 10px;
258- }
259- 
260- :deep(.el-cascader) {
261- width: 210px;
262- }
263- 
264- :deep(.el-input__wrapper) {
265- border-radius: 5px;
266- font-size: 12px;
267- font-weight: 700;
268- }
269-}
270- 
271-.cluster-info {
272- height: inherit;
273- display: flex;
274- align-items: center;
275- font-size: 12px;
276- 
277- &-light {
278- display: inline-block;
279- width: 6px;
280- height: 6px;
281- border-radius: 50%;
282- background-color: green;
283- margin-right: 8px;
284- }
285- 
286- &-loading {
287- width: 50px;
288- }
289-}
290- 
291-.tab-wrapper {
292- position: relative;
293- 
294- &-container {
295- display: flex;
296- align-items: center;
297- margin-bottom: 10px;
298- justify-content: end;
299- overflow: hidden;
300- font-size: 12px;
301- }
302- 
303- &-filter {
304- font-size: 12px;
305- width: 600px;
306- z-index: 10;
307- padding-right: 16px;
308- display: flex;
309- align-items: center;
310- padding: 0 10px;
311- height: 40px;
312- > div:not(:last-of-type),
313- > span,
314- > button {
315- margin-right: 4px;
316- }
317- 
318- :deep(.el-button .el-icon svg) {
319- color: var(--el-color-icon-refresh-color);
320- }
321- 
322- :deep(.el-button--small) {
323- background-color: var(--el-color-button-small-bg) !important;
324- border: 1px solid var(--el-bg-color-og-hover) !important;
325- color: var(--el-bg-color-og-hover) !important;
326- padding: 5px !important;
327- }
328- 
329- :deep(.el-select .el-input.is-focus .el-input__wrapper) {
330- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
331- }
332- :deep(.el-select-dropdown__item.selected) {
333- color: var(--el-color-tabbar-active) !important;
334- }
335- :deep(.el-range-editor.is-active) {
336- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
337- }
338- }
339- 
340- :deep(.el-range-input) {
341- background-color: var(--el-bg-color);
342- }
343- 
344- :deep(.el-date-editor--datetimerange) {
345- width: 100px;
346- background-color: var(--el-bg-color);
347- }
348-}
349-.divider {
350- height: 24px;
351- width: 1px;
352- margin: 0 8px !important;
353- background-color: var(--el-color-divider-border-color);
354-}
355-:deep(.el-tabs__header) {
356- padding: 0 16px;
357- // width: v-bind(tabHeaderW);
358-}
359-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/performance_load/Index.vue+0-626
@@ -1,626 +0,0 @@
1-<!-- eslint-disable camelcase -->
2-<script setup lang="ts">
3-import { useI18n } from 'vue-i18n';
4-import ListMetric from './ListMetric.vue';
5-import LazyLine from '../LazyLine.vue';
6-import { useMonitorStore } from '../../../store/monitor';
7-import { toFixed } from '../../../shared';
8-import { storeToRefs } from 'pinia';
9-import { getDatabaseMetrics } from '../../../api/prometheus';
10-import { useIntervalTime } from '../../../hooks/time';
11-
12-const { t } = useI18n();
13- 
14-const props = withDefaults(defineProps<{
15- nodeVersion?: string,
16-}>(), {
17- nodeVersion: "",
18-})
19- 
20-const brushRangeSign = ref<boolean>(false);
21-const tempSaveTime = ref<Array<Date>>([]);
22-const tempSaveRangeTime = ref<number>(-2);
23- 
24-const monitorStore = useMonitorStore()
25-const { refreshTime, rangeTime, time, tab: topTab, autoRefresh, instanceId, brushRange, timeRange } = storeToRefs(useMonitorStore())
26- 
27-const load = (checkTab?: boolean, checkRange?: boolean) => {
28- if (checkTab && topTab.value !== 0) {
29- return
30- }
31- if (checkRange && (brushRange.value.length > 0)) {
32- return
33- }
34- if (rangeTime.value > 0 || (time.value && time.value.length === 2)) {
35- getDatabaseMetrics()
36- }
37-}
38-if (topTab.value === 0) {
39- useIntervalTime(() => {
40- if (instanceId.value) {
41- load(true);
42- }
43- }, computed(() => refreshTime.value * 1000))
44-}
45- 
46-watch(autoRefresh, () => load(true, true))
47- 
48-watch(rangeTime, v => {
49- if (v > 0 && topTab.value === 0) {
50- if (brushRangeSign.value) {
51- brushRangeSign.value = false;
52- } else {
53- brushRange.value = []
54- }
55- getDatabaseMetrics()
56- }
57-})
58-watch(time, v => {
59- if (v && topTab.value === 0) {
60- if (brushRangeSign.value) {
61- brushRangeSign.value = false;
62- } else {
63- brushRange.value = []
64- }
65- load()
66- }
67-})
68- 
69-const ioRateData = ref<{ name: string, cur: string, min: string, avg: string, max: string }[]>([])
70-const loadRateData = (data: any[]) => {
71- if (data.length === 0) {
72- ioRateData.value = []
73- return
74- }
75- ioRateData.value = data.map(d => {
76- const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)
77- console.log("count", count);
78- return {
79- name: `${t(`metric.${d.name}`)}${t('dashboard.rate')}`,
80- cur: `${d.data[Math.max(0, d.data.length - 1)]}KB/s`,
81- min: `${toFixed(Math.min(...d.data))}KB/s`,
82- avg: `${d.data.length ? toFixed(count / d.data.length) : '0.00'}KB/s`,
83- max: `${toFixed(Math.max(...d.data))}KB/s`,
84- }
85- })
86-}
87-const ioData = ref<{ name: string, cur: string, min: string, avg: string, max: string }[]>([])
88-const loadData = (data: any[]) => {
89- if (data.length === 0) {
90- ioData.value = []
91- return
92- }
93- ioData.value = data.map(d => {
94- const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)
95- console.log("count", count);
96- return {
97- name: `${t(`metric.${d.name}`)}${t('dashboard.capacity')}`,
98- cur: `${d.data[Math.max(0, d.data.length - 1)]}MB/s`,
99- min: `${toFixed(Math.min(...d.data))}MB/s`,
100- avg: `${d.data.length ? toFixed(count / d.data.length) : '0.00'}MB/s`,
101- max: `${toFixed(Math.max(...d.data))}MB/s`,
102- }
103- })
104-}
105- 
106-const toTopSQL = () => {
107- monitorStore.filters[1].time = [new Date(timeRange.value[0]), new Date(timeRange.value[1])]
108- brushRange.value = []
109- monitorStore.tab = 1
110- monitorStore.filters[1].rangeTime = -1
111-}
112- 
113-const cardRef = ref()
114-const colors = [
115- '#F5222D', '#FA8C16', '#FADB14', '#52C41A', '#13C2C2', '#1890FF', '#2F54EB', '#722ED1', '#EB2F96', '#A97526',
116- '#FFB7B7', '#FFC9AB', '#FFFFAB', '#C0FF99', '#A8FFE5', '#B4E5FF', '#ADBAFF', '#D3ADF7', '#FFD1EC',
117- '#FF7875', '#FFC069', '#FFF566', '#95DE64', '#5CDBD3', '#69C0FF', '#597EF7', '#9254DE', '#FF85C0',
118- '#A8071A', '#AD4E00', '#AD8B00', '#237804', '#006D75', '#0050B3', '#1D39C4', '#531DAB', '#9E1068',
119-]
120-const waitLegends = ref<{color: string, name: string}[]>([])
121-const loadWaitData = (data: any[]) => {
122- waitLegends.value = data.map((d, i) => {
123- return {
124- color: colors[i] as string,
125- name: d.name as string,
126- }
127- })
128- nextTick(cardRef.value.trigger)
129-}
130- 
131-watch(instanceId, id => {
132- if (id) {
133- load()
134- }
135-}, { immediate: true })
136- 
137-const clearZoom = () => {
138- brushRange.value = [];
139- // reset
140- if (tempSaveRangeTime.value > 0) {
141- monitorStore.filters[0].rangeTime = tempSaveRangeTime.value;
142- monitorStore.filters[0].time = null;
143- } else {
144- monitorStore.filters[0].time = [tempSaveTime.value[0], tempSaveTime.value[1]];
145- monitorStore.filters[0].rangeTime = -1;
146- }
147- brushRangeSign.value = false;
148- // clear
149- tempSaveTime.value = [];
150- tempSaveRangeTime.value = -2;
151-}
152- 
153-watch(timeRange, () => {
154- if (tempSaveTime.value.length === 0 && tempSaveRangeTime.value === -2) {
155- if (rangeTime.value === -1) {
156- tempSaveTime.value = [time.value![0], time.value![1]];
157- } else {
158- tempSaveRangeTime.value = rangeTime.value;
159- }
160- }
161- monitorStore.filters[0].time = [new Date(timeRange.value[0]), new Date(timeRange.value[1])];
162- monitorStore.filters[0].rangeTime = -1;
163- brushRangeSign.value = true;
164-});
165- 
166-</script>
167- 
168-<template>
169-<ListMetric />
170-<div class="load-flex">
171- <el-row :gutter="16" :key="nodeVersion">
172- <el-col :span="props.nodeVersion === 'LITE' ? 24 : 12">
173- <my-card :title="$t('dashboard.timeConsumption')" height="300" :legend="[
174- {color: '#9CCC65', name: 'CPU_TIME'},
175- {color: '#FA8C16', name: 'NET_SEND_TIME'},
176- {color: '#00C7F9', name: 'DATA_IO_TIME'},
177- ]" :bodyPadding="false">
178- <div class="instance_time">
179- <div class="instance_time-tip"><svg-icon name="info"/>{{ $t('dashboard.runningInAnalysisTip') }}</div>
180- <div class="instance_time-button" v-show="brushRange && brushRange.length === 2">
181- <el-button type="primary" text bg @click="toTopSQL">{{ $t('dashboard.runningInAnalysis') }}</el-button>
182- <el-button type="primary" text bg @click="clearZoom">{{ $t('dashboard.uncheckRegion') }}</el-button>
183- </div>
184- </div>
185- <div style="height: 225px;">
186- <LazyLine
187- :color="['#9CCC65', '#FA8C16', '#00C7F9']"
188- :names="['CPU_TIME', 'NET_SEND_TIME', 'DATA_IO_TIME']"
189- brush="time"
190- :lineWidth="0"
191- areaStyle
192- stack
193- :translate="false"
194- />
195- </div>
196- </my-card>
197- </el-col>
198- <el-col :span="12" v-if="props.nodeVersion !== 'LITE'">
199- <my-card :title="$t('dashboard.waitEvent')" height="300" :legend="waitLegends" :bodyPadding="false" style="margin-bottom: 16px;" ref="cardRef">
200- <div class="instance_time">
201- <div><svg-icon name="info"/>{{ $t('dashboard.runningInAnalysisTip') }}</div>
202- <div v-show="brushRange && brushRange.length === 2">
203- <el-button type="primary" text bg @click="toTopSQL">{{ $t('dashboard.runningInAnalysis') }}</el-button>
204- <el-button type="primary" text bg @click="clearZoom">{{ $t('dashboard.uncheckRegion') }}</el-button>
205- </div>
206- </div>
207- <div style="height: 225px;">
208- <LazyLine
209- :color="colors"
210- @load-data="loadWaitData"
211- legendName="event"
212- brush="wait"
213- bar
214- stack
215- enterable
216- :translate="false"
217- />
218- </div>
219- </my-card>
220- </el-col>
221- </el-row>
222-</div>
223- 
224-<my-card :title="$t('dashboard.databaseLoad')" collapse style="margin-bottom: 16px;">
225- <el-row :gutter="16">
226- <el-col :span="12">
227- <my-card :title="$t('dashboard.tps')" height="255" :legend="[
228- {color: '#0D86E2', name: $t('metric.transactionRollbackNum')},
229- {color: '#9CCC65', name: $t('metric.transactionCommitments')},
230- {color: '#00C7F9', name: $t('metric.transactionAndRollbackTotal')},
231- ]" :bodyPadding="false">
232- <LazyLine
233- :color="['#0D86E2', '#9CCC65', '#00C7F9']"
234- :names="['transactionRollbackNum', 'transactionCommitments', 'transactionAndRollbackTotal']"
235- :formatter="toFixed"
236- />
237- </my-card>
238- </el-col>
239- <el-col :span="12">
240- <my-card :title="$t('dashboard.qps')" height="255" :legend="[
241- {color: '#00C7F9', name: $t('metric.queryTransactions')},
242- ]" :bodyPadding="false">
243- <LazyLine
244- :color="['#00C7F9']"
245- :names="['queryTransactions']"
246- />
247- </my-card>
248- </el-col>
249- <el-col :span="12">
250- <my-card :title="$t('dashboard.connectionNum')" height="255" :legend="[
251- {color: '#E64A19', name: $t('metric.currentIdleConnections')},
252- {color: '#0D86E2', name: $t('metric.currentActiveConnections')},
253- {color: '#9CCC65', name: $t('metric.currentConnections')},
254- {color: '#00C7F9', name: $t('metric.totalConnections')},
255- ]" :bodyPadding="false">
256- <LazyLine
257- :color="['#E64A19', '#0D86E2', '#9CCC65', '#00C7F9']"
258- :names="['currentIdleConnections', 'currentActiveConnections', 'currentConnections', 'totalConnections']"
259- />
260- </my-card>
261- </el-col>
262- <el-col :span="12">
263- <my-card :title="$t('dashboard.slowSqlMoreThan3Seconds')" height="255" :legend="[
264- {color: '#00C7F9', name: $t('metric.slowSqlNum')},
265- ]" :bodyPadding="false">
266- <LazyLine
267- :color="['#00C7F9']"
268- :names="['slowSqlNum']"
269- />
270- </my-card>
271- </el-col>
272- <el-col :span="12">
273- <my-card :title="$t('dashboard.longTransactionNumGreaterThan30Seconds')" height="255" :legend="[
274- {color: '#00C7F9', name: $t('metric.longTransactions')},
275- ]" :bodyPadding="false">
276- <LazyLine
277- :color="['#00C7F9']"
278- :names="['longTransactions']"
279- />
280- </my-card>
281- </el-col>
282- <el-col :span="12" v-if="props.nodeVersion !== 'LITE'">
283- <my-card :title="$t('dashboard.sqlResponseTime')" height="255" :legend="[
284- {color: '#9CCC65', name: $t('metric.sqlResponseTime80')},
285- {color: '#00C7F9', name: $t('metric.sqlResponseTime95')},
286- ]" :bodyPadding="false">
287- <LazyLine
288- :color="['#9CCC65', '#00C7F9']"
289- :names="['sqlResponseTime80', 'sqlResponseTime95']"
290- unit="ms"
291- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1000))"
292- />
293- </my-card>
294- </el-col>
295- <el-col :span="12">
296- <my-card :title="$t('dashboard.transactionLockInfo')" height="255" :legend="[
297- {color: '#E64A19', name: 'accessExclusiveLock'},
298- {color: '#0D86E2', name: 'accessShareLock'},
299- {color: '#9CCC65', name: 'ExclusiveLock'},
300- {color: '#00C7F9', name: 'ShareUpdateExclusiveLock'},
301- {color: '#0F866A', name: 'ShareRowExclusiveLock'},
302- {color: '#37D4D1', name: 'RowShareLock'},
303- {color: '#425ADD', name: 'RowExclusiveLock'},
304- {color: '#A97526', name: 'ShareLock'},
305- ]" :bodyPadding="false" :overflowHidden="false">
306- <LazyLine
307- :color="['#E64A19', '#0D86E2', '#9CCC65', '#00C7F9', '#0F866A', '#37D4D1', '#425ADD', '#A97526']"
308- :names="['accessExclusiveLock', 'accessShareLock', 'ExclusiveLock', 'ShareUpdateExclusiveLock', 'ShareRowExclusiveLock', 'RowShareLock', 'RowExclusiveLock', 'ShareLock']"
309- :translate="false"
310- />
311- </my-card>
312- </el-col>
313- <el-col :span="12">
314- <my-card :title="$t('dashboard.cacheHitRate')" height="255" :legend="[
315- {color: '#9CCC65', name: $t('metric.queryCacheHitRate')},
316- {color: '#00C7F9', name: $t('metric.databaseCacheHitRate')},
317- ]" :bodyPadding="false">
318- <LazyLine
319- :color="['#9CCC65', '#00C7F9']"
320- :names="['queryCacheHitRate', 'databaseCacheHitRate']"
321- unit="%"
322- :formatter="toFixed"
323- />
324- </my-card>
325- </el-col>
326- <el-col :span="12">
327- <my-card :title="$t('dashboard.ioBlockNumberTrend')" height="255" :legend="[
328- {color: '#9CCC65', name: $t('metric.readPhysicalFileBlockNum')},
329- {color: '#00C7F9', name: $t('metric.writePhysicalFileBlockNum')},
330- ]" :bodyPadding="false">
331- <LazyLine
332- :color="['#9CCC65', '#00C7F9']"
333- :names="['readPhysicalFileBlockNum', 'writePhysicalFileBlockNum']"
334- />
335- </my-card>
336- </el-col>
337- <el-col :span="12">
338- <my-card :title="$t('dashboard.ScrubDirtyPageInfo')" height="255" :legend="[
339- {color: '#9CCC65', name: $t('metric.lastBatchDirtyPageNum')},
340- {color: '#00C7F9', name: $t('metric.currentRemainingDirtyPages')},
341- ]" :bodyPadding="false">
342- <LazyLine
343- :color="['#9CCC65', '#00C7F9']"
344- :names="['lastBatchDirtyPageNum', 'currentRemainingDirtyPages']"
345- />
346- </my-card>
347- </el-col>
348- </el-row>
349-</my-card>
350-<my-card :title="$t('dashboard.serverResources')" collapse>
351- <el-row :gutter="16">
352- <el-col :span="12">
353- <my-card :title="$t('dashboard.loadAndCpu')" height="255" :legend="[
354- {color: '#E64A19', name: $t('metric.totalCoreNum')},
355- {color: '#9CCC65', name: $t('metric.total5mLoad')},
356- {color: '#00C7F9', name: $t('metric.totalAverageUtilization')},
357- ]" :bodyPadding="false">
358- <div class="linename">
359- <div>{{ $t('metric.totalCoreNum') }}</div>
360- <div>
361- <LazyLine
362- :color="['#E64A19', '#9CCC65', '#00C7F9']"
363- :names="['totalCoreNum', 'total5mLoad', 'totalAverageUtilization']"
364- :nameIndexs="[2]"
365- nameFix="1"
366- scatterUnit="%"
367- hasScatterData
368- />
369- </div>
370- <div>{{ $t('metric.totalAverageUtilization') }}</div>
371- </div>
372- </my-card>
373- </el-col>
374- <el-col :span="12">
375- <my-card :title="$t('dashboard.cpuUsage')" height="255" :legend="[
376- {color: '#E64A19', name: $t('metric.diskIOUsage')},
377- {color: '#0D86E2', name: $t('metric.systemUsage')},
378- {color: '#9CCC65', name: $t('metric.userUsage')},
379- {color: '#00C7F9', name: $t('metric.totalUsage')},
380- ]" :bodyPadding="false">
381- <LazyLine
382- :color="['#E64A19', '#0D86E2', '#9CCC65', '#00C7F9']"
383- :names="['diskIOUsage', 'systemUsage', 'userUsage', 'totalUsage']"
384- unit="%"
385- />
386- </my-card>
387- </el-col>
388- <el-col :span="12">
389- <my-card :title="$t('dashboard.memoryAndAverageMemory')" height="255" :legend="[
390- {color: '#9CCC65', name: $t('metric.totalMemory')},
391- {color: '#E64A19', name: $t('metric.usedMemory')},
392- {color: '#00C7F9', name: $t('metric.totalAverageUtilization')},
393- ]" :bodyPadding="false">
394- <div class="linename">
395- <div>{{ $t('metric.totalMemory') }}(GB)</div>
396- <div>
397- <LazyLine
398- :color="['#9CCC65', '#E64A19', '#00C7F9']"
399- :names="['totalMemory', 'usedMemory', 'totalAverageUtilization']"
400- :nameIndexs="[2]"
401- nameFix="2"
402- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1073741824))"
403- scatterUnit="%"
404- hasScatterData
405- />
406- </div>
407- <div>{{ $t('metric.totalAverageUtilization') }}</div>
408- </div>
409- </my-card>
410- </el-col>
411- <el-col :span="12">
412- <my-card :title="$t('dashboard.diskAndAverageDisk')" height="255" :legend="[
413- {color: '#9CCC65', name: $t('metric.totalDisks')},
414- {color: '#E64A19', name: $t('metric.totalNumber')},
415- {color: '#00C7F9', name: $t('metric.totalAverageUtilization')},
416- ]" :bodyPadding="false">
417- <div class="linename">
418- <div>{{ $t('metric.totalDisks') }}(GB)</div>
419- <div>
420- <LazyLine
421- :color="['#9CCC65', '#E64A19', '#00C7F9']"
422- :names="['totalDisks', 'totalNumber', 'totalAverageUtilization']"
423- :nameIndexs="[2]"
424- nameFix="3"
425- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1073741824))"
426- scatterUnit="%"
427- hasScatterData
428- />
429- </div>
430- <div>{{ $t('metric.totalAverageUtilization') }}</div>
431- </div>
432- </my-card>
433- </el-col>
434- <el-col :span="12">
435- <my-card :title="$t('dashboard.diskReadAndWriteRate')" height="255" style="margin-bottom: 0;" :legend="[
436- {color: '#0D86E2', name: $t('metric.read')},
437- {color: '#00C7F9', name: $t('metric.write')},
438- ]" :bodyPadding="false" :withTable="true">
439- <LazyLine
440- :color="['#0D86E2', '#00C7F9']"
441- :names="['read', 'write']"
442- :nameIndexs="[0, 1]"
443- nameFix="1"
444- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1024))"
445- unit="KB/s"
446- @load-data="loadRateData"
447- />
448- </my-card>
449- <el-table :data="ioRateData" border>
450- <el-table-column label="" prop="name" />
451- <el-table-column :label="$t('dashboard.currentRate')" prop="cur" />
452- <el-table-column :label="$t('dashboard.minimumRate')" prop="min" />
453- <el-table-column :label="$t('dashboard.averageRate')" prop="avg" />
454- <el-table-column :label="$t('dashboard.maxRate')" prop="max" />
455- </el-table>
456- </el-col>
457- <el-col :span="12">
458- <my-card :title="$t('dashboard.diskReadAndWritCapacity')" height="255" style="margin-bottom: 0;" :legend="[
459- {color: '#0D86E2', name: $t('metric.read')},
460- {color: '#00C7F9', name: $t('metric.write')},
461- ]" :bodyPadding="false" :withTable="true">
462- <LazyLine
463- :color="['#0D86E2', '#00C7F9']"
464- :names="['read', 'write']"
465- :nameIndexs="[0, 1]"
466- nameFix="2"
467- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1048576))"
468- unit="MB/s"
469- @load-data="loadData"
470- />
471- </my-card>
472- <el-table :data="ioData" border>
473- <el-table-column label="" prop="name" />
474- <el-table-column :label="$t('dashboard.currentCapacity')" prop="cur" />
475- <el-table-column :label="$t('dashboard.minimumCapacity')" prop="min" />
476- <el-table-column :label="$t('dashboard.averageCapacity')" prop="avg" />
477- <el-table-column :label="$t('dashboard.maxCapacity')" prop="max" />
478- </el-table>
479- </el-col>
480- <el-col :span="12">
481- <my-card :title="$t('dashboard.networkBandwidthUsage')" height="255" :legend="[
482- {color: '#0D86E2', name: $t('metric.upload')},
483- {color: '#00C7F9', name: $t('metric.download')},
484- ]" :bodyPadding="false">
485- <LazyLine
486- :color="['#0D86E2', '#00C7F9']"
487- :names="['upload', 'download']"
488- unit="MB/s"
489- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1048576))"
490- />
491- </my-card>
492- </el-col>
493- <el-col :span="12">
494- <my-card :title="$t('dashboard.networkSocketConnection')" height="255" :legend="[
495- {color: '#0D86E2', name: 'TCPAlloc'},
496- {color: '#9CCC65', name: 'CurrEstab'},
497- {color: '#E64A19', name: 'TcpOutSegs'},
498- {color: '#0F866A', name: 'TcpInSegs'},
499- {color: '#37D4D1', name: 'UdpInuse'},
500- {color: '#425ADD', name: 'TcpTw'},
501- {color: '#A97526', name: 'TcpRetransSegs'},
502- {color: '#00C7F9', name: 'SocketsUsed'},
503- ]" :bodyPadding="false" :overflowHidden="false">
504- <div class="linename">
505- <div>{{ $t('dashboard.totalLoad') }}</div>
506- <div>
507- <LazyLine
508- :color="['#0D86E2', '#9CCC65', '#E64A19', '#0F866A', '#37D4D1', '#425ADD', '#A97526', '#00C7F9']"
509- :names="['TCP_alloc', 'CurrEStab', 'Tcp_OutSegs', 'Tcp_InSegs', 'UDP_inuse', 'TCP_tw', 'Tcp_RetransSegs', 'Sockets_used']"
510- :formatter="toFixed"
511- hasScatterData
512- :translate="false"
513- />
514- </div>
515- <div>{{ $t('dashboard.allProtocolSocketsUsed') }}</div>
516- </div>
517- </my-card>
518- </el-col>
519- </el-row>
520-</my-card>
521- 
522-</template>
523- 
524-<style scoped lang="scss">
525-.og-server {
526- :deep(tr) {
527- background-color: $og-background-color;
528- }
529- :deep(.el-table__inner-wrapper::before) {
530- height: 0px;
531- }
532- :deep(.el-table__row:last-of-type td) {
533- border-bottom: none;
534- }
535- :deep(.el-table__inner-wrapper tr:first-child td:first-child) {
536- border-left: none;
537- }
538-}
539-.instance_time {
540- display: flex;
541- justify-content: space-between;
542- padding: 8px 16px 0;
543- font-size: 12px;
544- align-items: center;
545- svg {
546- margin-right: 8px;
547- }
548- 
549- &-tip {
550- width: 68%;
551- }
552- &-button {
553- width: 30%;
554- }
555- .el-button {
556- text-decoration: underline;
557- }
558- &_tabs {
559- :deep(.el-tabs__header) {
560- padding: 0 16px;
561- width: 100%;
562- background-color: $og-border-color !important;
563- }
564- :deep(.el-tabs__content) {
565- width: 100%;
566- height: 100%;
567- }
568- }
569-}
570-.linename {
571- display: flex;
572- width: 100%;
573- height: 100%;
574- align-items: center;
575- position: relative;
576- justify-content: center;
577- > div:nth-of-type(2) {
578- width: calc(100% - 60px);
579- height: 100%;
580- margin: 0 10px;
581- }
582- > div:nth-of-type(1), > div:nth-of-type(3) {
583- color: var(--el-color-line-text-color);
584- font-size: 12px;
585- text-align: center;
586- position: absolute;
587- width: 200px;
588- height: 15px;
589- }
590- > div:nth-of-type(1) {
591- transform: rotate(-90deg);
592- left: -80px;
593- }
594- > div:nth-of-type(3) {
595- transform: rotate(90deg);
596- right: -80px;
597- }
598-}
599-:deep(.og-card) {
600- margin-bottom: 16px;
601-}
602-:deep(.og-card-body .el-row) {
603- margin-bottom: -16px;
604-}
605-:deep(.el-table) {
606- background-color: var(--el-bg-color-og);
607- margin-bottom: 16px;
608-}
609-:deep(.el-table__header-wrapper th) {
610- background-color: var(--el-bg-color-og);
611- color: var(--el-text-color-og);
612- }
613-:deep(.el-table tr) {
614- background-color: var(--el-bg-color-og);
615-}
616-:deep(.el-table th.el-table__cell) {
617- background-color: var(--el-bg-color-og);
618-}
619- 
620-:deep(.el-table__row) {
621- background-color: var(--el-bg-color-og);
622-}
623-:deep(.el-table__row th) {
624- color: var(--el-text-color-og);
625-}
626-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/performance_load/ListMetric.vue+0-136
@@ -1,136 +0,0 @@
1-<script setup lang="ts">
2-import { storeToRefs } from 'pinia';
3-import { useRequest } from 'vue-request';
4-import { useIntervalTime } from '../../../hooks/time';
5-import ogRequest from '../../../request';
6-import { useMonitorStore } from '../../../store/monitor';
7- 
8-const { tab, refreshTime, instanceId } = storeToRefs(useMonitorStore())
9- 
10-const { data, run } = useRequest(() => {
11- if (tab.value !== 0) {
12- return new Promise(() => {})
13- }
14- return ogRequest.get("/observability/v1/monitoring/server-metric", {
15- // id: 'ogbrench',
16- id: instanceId.value || 'ogbrench',
17- })
18-}, { manual: true })
19- 
20-const formatter = (v: string | null, unit: string = '') => {
21- if (v == null) {
22- return '--'
23- }
24- let n = 1
25- if (unit && unit.startsWith('MB')) {
26- n = 1048576
27- } else if (unit && unit.startsWith('KB')) {
28- n = 1024
29- }
30- return `${(Number.parseFloat(v) / n).toFixed(2)}${unit}`
31-}
32- 
33-useIntervalTime(() => {
34- if (instanceId.value) {
35- run();
36- }
37-}, computed(() => refreshTime.value * 1000))
38-watch(instanceId, id => {
39- if (id) {
40- run()
41- }
42-}, { immediate: true })
43-</script>
44- 
45-<template>
46- <div class="og-list">
47- <div class="og-list-item">
48- <div>
49- <div>{{ $t('dashboard.cpuUsage') }}</div>
50- <div class="og-list-item--number">{{ (!data || data.cpu == undefined) ? '--' : formatter(data.cpu, '%') }}</div>
51- </div>
52- <svg-icon name="cpu" />
53- </div>
54- <div class="og-list-item">
55- <div>
56- <div>{{ $t('dashboard.load5m') }}</div>
57- <div class="og-list-item--number">{{ (!data || !data.node_load5 || data.node_load5.value == undefined) ? '--' : formatter(data.node_load5.value) }}</div>
58- </div>
59- <svg-icon name="load" />
60- </div>
61- <div class="og-list-item">
62- <div>
63- <div>{{ $t('dashboard.memoryUsage') }}</div>
64- <div class="og-list-item--number">{{ (!data || data.memory == undefined) ? '--' : formatter(data.memory, '%') }}</div>
65- </div>
66- <svg-icon name="memory" />
67- </div>
68- <div class="og-list-item">
69- <div>
70- <div>{{ $t('dashboard.diskReadRate') }}</div>
71- <div class="og-list-item--number">{{ (!data || data.disk_read == undefined) ? '--' : formatter(data.disk_read, 'KB/s') }}</div>
72- </div>
73- <svg-icon name="disk" />
74- </div>
75- <div class="og-list-item">
76- <div>
77- <div>{{ $t('dashboard.diskWriteRate') }}</div>
78- <div class="og-list-item--number">{{ (!data || data.disk_written == undefined) ? '--' : formatter(data.disk_written, 'KB/s') }}</div>
79- </div>
80- <svg-icon name="disk" />
81- </div>
82- <div class="og-list-item">
83- <div>
84- <div>{{ $t('dashboard.uploadRate') }} </div>
85- <div class="og-list-item--number">{{ (!data || data.network_transmit == undefined) ? '--' : formatter(data.network_transmit, 'MB/s') }}</div>
86- </div>
87- <svg-icon name="speed_up" />
88- </div>
89- <div class="og-list-item">
90- <div>
91- <div>{{ $t('dashboard.downloadRate') }}</div>
92- <div class="og-list-item--number">{{ (!data || data.network_receive == undefined) ? '--' : formatter(data.network_receive, 'MB/s') }}</div>
93- </div>
94- <svg-icon name="speed_down" />
95- </div>
96- </div>
97-</template>
98- 
99-<style scoped lang="scss">
100- .og-list {
101- margin-bottom: 16px;
102- display: flex;
103- justify-content: center;
104- align-items: center;
105- gap: 10px;
106- height: 82px;
107- background: var(--el-bg-color-og);
108- border: 1px solid var(--el-og-border-color);
109- border-radius: 8px;
110- align-self: stretch;
111- &-item {
112- height: 66px;
113- display: flex;
114- justify-content: center;
115- align-items: center;
116- gap: 38px;
117- flex: 1;
118- min-width: 0;
119- border-right: 1px solid var(--el-og-border-color);
120- &:last-of-type {
121- border-right: none;
122- }
123- svg {
124- width: 28px;
125- height: 28px;
126- fill: $og-svg-fill-color;
127- }
128- &--number {
129- color: $og-sub-text-color;
130- font-size: 16px;
131- line-height: 24px;
132- font-weight: 700;
133- }
134- }
135- }
136-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/system_configuration/Index.vue+0-354
@@ -1,354 +0,0 @@
1-<script setup lang="ts">
2-import { useRequest } from "vue-request";
3-import restRequest from "../../../request/restful";
4-import { useI18n } from "vue-i18n";
5-import { View, Guide } from "@element-plus/icons-vue";
6-import Password from "./password.vue";
7-import { FormRules, FormInstance, ElMessage } from 'element-plus'
8- 
9-const { t } = useI18n();
10- 
11-const errorInfo = ref<string | Error>();
12- 
13-const props = withDefaults(
14- defineProps<{
15- instanceId?: string;
16- }>(),
17- {
18- instanceId: "",
19- }
20-);
21- 
22-// nodeId sync
23-const emit = defineEmits(["nodeIdChanged"]);
24-const clusterComponent = ref(null);
25-const culsterLoaded = ref<boolean>(false);
26-const initNodeId = ref<string>("");
27-const syncNodeId = (syncNodeIdVal: string) => {
28- if (syncNodeIdVal === null || syncNodeIdVal === "") return;
29- if (!culsterLoaded.value) initNodeId.value = syncNodeIdVal;
30- else {
31- clusterComponent.value.setNodeId(syncNodeIdVal);
32- nextTick(() => {
33- refreshData("");
34- });
35- }
36-};
37-defineExpose({
38- syncNodeId,
39-});
40- 
41-const nodeId = ref<string>("");
42-const data = reactive<{
43- dbParamData: Array<Record<string, string>>;
44- osParamData: Array<Record<string, string>>;
45-}>({
46- dbParamData: [],
47- osParamData: [],
48-});
49- 
50-// password dialog
51-const snapshotManageShown = ref(false);
52-const showSnapshotManage = () => {
53- snapshotManageShown.value = true;
54-};
55-const changeModalSnapshotManage = (val: boolean) => {
56- snapshotManageShown.value = val;
57-};
58- 
59-// cluster component
60-const handleClusterValue = (val: any) => {
61- nodeId.value = val.length > 1 ? val[1] : "";
62- emit("nodeIdChanged", nodeId.value);
63-};
64-const clusterLoaded = (val: any) => {
65- culsterLoaded.value = true;
66- if (initNodeId.value) {
67- clusterComponent.value.setNodeId(initNodeId.value);
68- nextTick(() => {
69- refreshData("");
70- });
71- }
72-};
73- 
74-const handleQuery = () => {
75- if(!nodeId.value) {
76- ElMessage({
77- showClose: true,
78- message: t('configParam.queryValidInfo'),
79- type: 'warning',
80- })
81- return
82- }
83- showSnapshotManage();
84-};
85-const refreshData = (password: string,isRefresh: string) => {
86- // requestDBData(password);
87- // requestOSData(password);
88- if(!nodeId.value) {
89- ElMessage({
90- showClose: true,
91- message: t('configParam.queryValidInfo'),
92- type: 'warning',
93- })
94- return
95- }
96- requestData(password,isRefresh)
97-};
98-const {data: res,
99- run: requestData,
100- loading: loadingDBData,
101-} = useRequest((password,isRefresh = "0") => {
102- return restRequest
103- .get("/observability/v1/param/paramInfo", {
104- paramName: "",
105- nodeId: nodeId.value,
106- dbName: null,
107- password,
108- paramType: "",
109- isRefresh
110- })
111- .then(function (res) {
112- return res;
113- })
114- .catch(function (res) {
115- data.dbParamData = [];
116- data.osParamData = [];
117- });
118-},{manual: true});
119-watch(res, (res) => {
120- if(res && res.length > 0) {
121- data.dbParamData = res.filter(item => item.paramType === 'DB');
122- data.osParamData = res.filter(item => item.paramType === 'OS');
123- }
124-
125-});
126-// const {
127-// data: res,
128-// run: requestDBData,
129-// loading: loadingDBData,
130-// } = useRequest(
131-// (password) => {
132-// return restRequest
133-// .get("/observability/v1/param/databaseParamInfo", {
134-// nodeId: nodeId.value,
135-// })
136-// .then(function (res) {
137-// return res;
138-// })
139-// .catch(function (res) {
140-// data.dbParamData = [];
141-// });
142-// },
143-// { manual: true }
144-// );
145-// watch(res, (res) => {
146-// data.dbParamData = res;
147-// });
148- 
149-// const {
150-// data: resOS,
151-// run: requestOSData,
152-// loading: loadingOSData,
153-// } = useRequest(
154-// (password) => {
155-// return restRequest
156-// .get("/observability/v1/param/osParamInfo", {
157-// paramName: "",
158-// nodeId: nodeId.value,
159-// dbName: null,
160-// password,
161-// isRefresh: null,
162-// paramType: "",
163-// })
164-// .then(function (res) {
165-// return res;
166-// })
167-// .catch(function (res) {
168-// data.osParamData = null;
169-// });
170-// },
171-// { manual: true }
172-// );
173-// watch(resOS, (resOS) => {
174-// data.osParamData = resOS;
175-// });
176-const color = computed(() => {
177- if (localStorage.getItem("theme") === "dark") return "#fcef92";
178- else return "#E41D1D";
179-});
180-onMounted(() => {
181- 
182- // @ts-ignore
183- const wujie = window.$wujie;
184- // Judge whether it is a plug-in environment or a local environment through wujie
185- if (wujie) {
186- // Monitoring platform language change
187- wujie?.bus.$on('opengauss-locale-change', (val: string) => {
188- console.log('log-search catch locale change');
189- nextTick(() => {
190- if(nodeId.value) {
191- refreshData("","0");
192- }
193- });
194- });
195- }
196-});
197-</script>
198- 
199-<template>
200- <div class="" style="padding: 0px 15px">
201- <div class="search-form head">
202- <div class="filter title" style="margin-right: auto">{{ $t("configParam.tabTitle") }}</div>
203- 
204- <div class="filter">
205- <ClusterCascader notClearable ref="clusterComponent" @loaded="clusterLoaded" :title="$t('datasource.cluterTitle')" @getCluster="handleClusterValue" />
206- </div>
207- <div class="query filter">
208- <el-button @click="handleQuery">{{ $t("app.refresh") }}</el-button>
209- <el-button type="primary" @click="refreshData('')">{{ $t("app.query") }}</el-button>
210- </div>
211- </div>
212- <div class="list" v-loading="loadingDBData">
213- <div class="list-item">
214- <div class="item-title">{{ $t("configParam.systemConfig") }}</div>
215- <div>
216- <div class="item-list" v-for="item in data.osParamData" :key="item.seqNo">
217- <div class="item-list-left">
218- <div class="item-name">{{ item.paramName }}</div>
219- <el-popover placement="top-start" :title="$t('configParam.paramDesc')" :width="200" trigger="hover" :content="item.paramDetail">
220- <template #reference>
221- <el-icon class="detail-btn" :color="'#7d7d7d'" size="18px">
222- <View />
223- </el-icon>
224- </template>
225- </el-popover>
226- </div>
227- <div class="item-list-right">
228- <div class="item-value">{{ item.actualValue === undefined || item.actualValue === null ? "--" : item.actualValue }}</div>
229- <div class="suggest-btn-container">
230- <el-popover placement="top-start" :title="$t('configParam.paramTuning')" :width="300" trigger="hover" v-if="item.suggestValue != undefined && item.suggestValue != null && item.suggestValue != '' && item.suggestValue != item.actualValue">
231- <template #reference>
232- <el-icon class="suggest-btn" :color="color" size="18px" v-if="item.actualValue != item.suggestValue">
233- <Guide />
234- </el-icon>
235- </template>
236- <template #default>
237- <div class="demo-rich-conent" style="display: flex; gap: 16px; flex-direction: column">
238- <div>
239- <p class="demo-rich-content__name" style="margin: 0; font-weight: 500">{{ $t("configParam.suggestValue") }}{{ item.suggestValue }}</p>
240- <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ $t("configParam.suggestReason") }}</p>
241- <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ item.suggestExplain }}</p>
242- </div>
243- </div>
244- </template>
245- </el-popover>
246- </div>
247- </div>
248- </div>
249- </div>
250- </div>
251- <div class="list-item">
252- <div class="item-title">{{ $t("configParam.databaseConfig") }}</div>
253- <div>
254- <div class="item-list" v-for="item in data.dbParamData" :key="item.seqNo">
255- <div class="item-list-left">
256- <div class="item-name">{{ item.paramName }}</div>
257- <el-popover placement="top-start" :title="$t('configParam.paramDesc')" :width="200" trigger="hover" :content="item.paramDetail">
258- <template #reference>
259- <el-icon class="detail-btn" :color="'#7d7d7d'" size="18px">
260- <View />
261- </el-icon>
262- </template>
263- </el-popover>
264- </div>
265- <div class="item-list-right">
266- <div class="item-value">{{ item.actualValue === undefined || item.actualValue === null ? "--" : item.actualValue }}</div>
267- <div class="suggest-btn-container">
268- <el-popover placement="top-start" :title="$t('configParam.paramTuning')" :width="300" trigger="hover" v-if="item.suggestValue != undefined && item.suggestValue != null && item.suggestValue != '' && item.suggestValue != item.actualValue">
269- <template #reference>
270- <el-icon class="suggest-btn" :color="color" size="18px" v-if="item.actualValue != item.suggestValue">
271- <Guide />
272- </el-icon>
273- </template>
274- <template #default>
275- <div class="demo-rich-conent" style="display: flex; gap: 16px; flex-direction: column">
276- <div>
277- <p class="demo-rich-content__name" style="margin: 0; font-weight: 500">{{ $t("configParam.suggestValue") }}{{ item.suggestValue }}</p>
278- <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ $t("configParam.suggestReason") }}</p>
279- <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ item.suggestExplain }}</p>
280- </div>
281- </div>
282- </template>
283- </el-popover>
284- </div>
285- </div>
286- </div>
287- </div>
288- </div>
289- </div>
290- <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
291- <Password :show="snapshotManageShown" @changeModal="changeModalSnapshotManage" @confirm="refreshData" />
292- </div>
293-</template>
294- 
295-<style scoped lang="scss">
296-@import "../../../assets/style/style1.scss";
297-.head {
298- display: flex;
299- align-items: center;
300- margin-bottom: 15px;
301- .title {
302- font-size: 16px;
303- font-weight: bold;
304- }
305-}
306-.list {
307- display: flex;
308- flex-direction: row;
309- .list-item {
310- width: 50%;
311- .item-title {
312- font-size: 14px;
313- font-weight: bold;
314- margin-bottom: 5px;
315- }
316- .item-list {
317- display: flex;
318- margin: 5px 0px;
319- .item-list-left {
320- width: 55%;
321- display: flex;
322- align-items: center;
323- flex-shrink: 0;
324- .detail-btn {
325- margin-left: 10px;
326- }
327- }
328- .item-list-right {
329- width: 40%;
330- display: flex;
331- align-items: center;
332- overflow: hidden;
333- padding-right: 20px;
334- padding-left: 20px;
335- vertical-align: middle;
336- .item-value {
337- display: inline-block;
338- white-space: nowrap;
339- overflow: hidden;
340- text-overflow: ellipsis;
341- text-align: left;
342- }
343- .suggest-btn-container {
344- width: 20px;
345- display: flex;
346- }
347- .suggest-btn {
348- margin-left: 5px;
349- }
350- }
351- }
352- }
353-}
354-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/system_configuration/password.vue+0-78
@@ -1,78 +0,0 @@
1-<template>
2- <div class="dialog">
3- <el-dialog width="400px" :title="$t('configParam.rootPWDTitle')" v-model="visible" :close-on-click-modal="false" draggable @close="closeDialog">
4- <div class="dialog-content">
5- <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
6- <el-form-item :label="t('configParam.rootPWD')" prop="rootPassword">
7- <el-input v-model="formData.rootPassword" show-password style="width: 200px; margin: 0 4px" />
8- </el-form-item>
9- </el-form>
10- </div>
11- 
12- <template #footer>
13- <el-button style="padding: 5px 20px" type="primary" @click="handleconfirmModel">{{ $t("app.confirm") }}</el-button>
14- <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t("app.cancel") }}</el-button>
15- </template>
16- </el-dialog>
17- </div>
18-</template>
19- 
20-<script lang="ts" setup>
21-import { cloneDeep } from "lodash-es";
22-import { FormRules, FormInstance } from "element-plus";
23-import { useI18n } from "vue-i18n";
24-import { encryptPassword } from "../../../utils/jsencrypt";
25-const { t } = useI18n();
26- 
27-const visible = ref(false);
28-const props = withDefaults(
29- defineProps<{
30- show: boolean;
31- }>(),
32- {}
33-);
34-watch(
35- () => props.show,
36- (newValue) => {
37- visible.value = newValue;
38- },
39- { immediate: true }
40-);
41- 
42-// form data
43-const initFormData = {
44- rootPassword: "",
45-};
46-const formData = reactive(cloneDeep(initFormData));
47- 
48-// build
49-const emit = defineEmits(["changeModal", "confirm"]);
50-async function handleconfirmModel() {
51- let result = await connectionFormRef.value?.validate();
52- if (!result) return;
53- const encryptPwd = await encryptPassword(formData.rootPassword);
54- const isRefresh = "1";
55- emit("confirm", encryptPwd,isRefresh);
56- visible.value = false;
57- formData.rootPassword = ''
58- emit("changeModal", visible.value);
59-}
60-const connectionFormRef = ref<FormInstance>();
61-const connectionFormRules = reactive<FormRules>({
62- rootPassword: [{ required: true, message: t("configParam.rootPWDTitle"), trigger: "blur" }],
63-});
64- 
65-const handleCancelModel = () => {
66- visible.value = false;
67- formData.rootPassword = ''
68- emit("changeModal", visible.value);
69-};
70-const closeDialog = () => {
71- visible.value = false;
72- formData.rootPassword = ''
73- emit("changeModal", visible.value);
74-};
75-</script>
76-<style lang="scss" scoped>
77-@import "../../../assets/style/style1.scss";
78-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/top_sql/Index.vue+0-206
@@ -1,206 +0,0 @@
1-<template>
2- <div class="top-sql">
3- <el-tabs v-model="typeTab">
4- <el-tab-pane label="DB_TIME" name="db_time" />
5- <el-tab-pane label="CPU_TIME" name="cpu_time" />
6- <el-tab-pane label="EXEC_TIME" name="execution_time" />
7- </el-tabs>
8- <div class="top-sql-table" v-if="!errorInfo" v-loading="loading">
9- <el-table :data="data.tableData" border>
10- <el-table-column label="SQLID">
11- <template #default="scope">
12- <a class="top-sql-table-id" @click="gotoTopsqlDetail(scope.row.debug_query_id)">
13- {{ scope.row.debug_query_id }}
14- </a>
15- </template>
16- </el-table-column>
17- <el-table-column :label="$t('sql.dbName')" prop="db_name"></el-table-column>
18- <el-table-column :label="$t('sql.schemaName')" prop="schema_name"></el-table-column>
19- <el-table-column :label="$t('sql.userName')" prop="user_name"></el-table-column>
20- <el-table-column :label="$t('sql.applicationName')" prop="application_name"></el-table-column>
21- <el-table-column
22- :label="$t('sql.startTime')"
23- :formatter="(r: any) => moment(r.start_time).format('YYYY-MM-DD HH:mm:ss')"
24- width="120"
25- />
26- <el-table-column
27- :label="$t('sql.finishTime')"
28- :formatter="(r: any) => moment(r.finish_time).format('YYYY-MM-DD HH:mm:ss')"
29- width="120"
30- />
31- <el-table-column :label="$t('sql.dbTime')" prop="db_time" width="110"></el-table-column>
32- <el-table-column :label="$t('sql.cpuTime')" prop="cpu_time" width="115"></el-table-column>
33- <el-table-column
34- :label="$t('sql.excutionTime')"
35- prop="execution_time"
36- :width="i18n.global.locale.value === 'en' ? 150 : 105"
37- ></el-table-column>
38- </el-table>
39- </div>
40- <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
41- </div>
42-</template>
43- 
44-<script setup lang="ts">
45-import { useRequest } from 'vue-request'
46-import moment from 'moment'
47-import { useIntervalTime } from '../../../hooks/time'
48-import { useMonitorStore } from '../../../store/monitor'
49-import { storeToRefs } from 'pinia'
50-import ogRequest from '../../../request'
51-import router from '../../../router'
52-import { i18n } from '../../../i18n'
53-import { useI18n } from 'vue-i18n'
54- 
55-const { t } = useI18n()
56- 
57-const typeTab = ref('db_time')
58-const errorInfo = ref<string | Error>()
59- 
60-const props = withDefaults(
61- defineProps<{
62- instanceId?: string
63- }>(),
64- {
65- instanceId: '',
66- }
67-)
68- 
69-const data = reactive<{
70- tableData: Array<Record<string, string>>
71-}>({
72- tableData: [],
73-})
74- 
75-const curFilter = reactive({
76- dbid: '',
77- startTime: '2022-09-24 00:00:00',
78- finishTime: '2022-10-30 00:00:00',
79-})
80- 
81-const { tab, time, refreshTime, autoRefresh, rangeTime } = storeToRefs(useMonitorStore())
82- 
83-const { run: requestData, loading } = useRequest(
84- (query) => {
85- if (tab.value !== 1) {
86- return new Promise(() => {})
87- }
88- const res = new Promise((resolve, reject) => {
89- const result = ogRequest.getNative(
90- `/observability/v1/topsql/list?id=${query.dbid}&startTime=${query.startTime}&finishTime=${query.finishTime}&orderField=${typeTab.value}`
91- )
92- result ? resolve(result) : reject(result)
93- })
94- .then((r: any) => {
95- const code = r?.data.code
96- const list = r?.data.data
97- if (code === '602') {
98- errorInfo.value = t('dashboard.topsqlListTip')
99- } else if (code === '200' && Array.isArray(list)) {
100- data.tableData = list
101- }
102- })
103- .catch((e) => {
104- if (!props.instanceId) {
105- errorInfo.value = t('dashboard.pleaseChooseinstanceId')
106- } else {
107- errorInfo.value = e
108- }
109- })
110- return res
111- },
112- { manual: true }
113-)
114- 
115-useIntervalTime(
116- () => {
117- requestData(curFilter)
118- },
119- computed(() => refreshTime.value * 1000)
120-)
121- 
122-const gotoTopsqlDetail = (id: string) => {
123- const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
124- if (curMode === 'wujie') {
125- // @ts-ignore plug-in components
126- window.$wujie?.props.methods.jump({
127- name: `Static-pluginObservability-instanceVemSql_detail`,
128- query: {
129- dbid: curFilter.dbid,
130- id,
131- },
132- })
133- } else {
134- // local
135- window.sessionStorage.setItem('sqlId', id)
136- router.push(`/vem/sql_detail/${curFilter.dbid}/${id}`)
137- }
138-}
139- 
140-const setQueryTime = (range: number, curTime: [Date, Date] | null) => {
141- if (range === -1 && curTime != null) {
142- curFilter.startTime = moment(curTime[0]).format('YYYY-MM-DD HH:mm:ss')
143- curFilter.finishTime = moment(curTime[1]).format('YYYY-MM-DD HH:mm:ss')
144- return
145- }
146- if (range > 0) {
147- const finishTimeStamp = new Date().getTime()
148- const startTimeStamp = finishTimeStamp - range * 60 * 60 * 1000
149- curFilter.startTime = moment(startTimeStamp).format('YYYY-MM-DD HH:mm:ss')
150- curFilter.finishTime = moment(finishTimeStamp).format('YYYY-MM-DD HH:mm:ss')
151- }
152-}
153- 
154-setQueryTime(rangeTime.value, time.value)
155- 
156-onMounted(() => {
157- if (props.instanceId != null && props.instanceId !== '') {
158- curFilter.dbid = props.instanceId
159- }
160- requestData(curFilter)
161-})
162- 
163-watch(
164- () => props.instanceId,
165- (res) => {
166- if (typeof res === 'string') {
167- curFilter.dbid = res
168- }
169- }
170-)
171- 
172-watch(typeTab, () => {
173- requestData(curFilter)
174-})
175- 
176-watch(time, (curTime) => {
177- setQueryTime(-1, curTime)
178- requestData(curFilter)
179-})
180- 
181-watch(rangeTime, (r) => {
182- if (r === -1) {
183- time.value = null
184- return
185- }
186- setQueryTime(r, null)
187- requestData(curFilter)
188-})
189- 
190-watch(autoRefresh, () => {
191- requestData(curFilter)
192-})
193-</script>
194- 
195-<style scoped lang="scss">
196-.top-sql {
197- &:deep(.el-tabs__header) {
198- width: 100%;
199- }
200- 
201- &-table-id {
202- color: #0093ff;
203- cursor: pointer;
204- }
205-}
206-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/wdr/Index.vue+0-299
@@ -1,299 +0,0 @@
1-<script setup lang="ts">
2-import { useRequest } from "vue-request";
3-import moment from "moment";
4-import restRequest from "../../../request/restful";
5-import BuildWdr from "./build_wdr.vue";
6-import SnapshotManage from "./snapshot_manage.vue";
7-import { cloneDeep } from "lodash-es";
8-import { useI18n } from "vue-i18n";
9-const { t } = useI18n();
10- 
11-const shortcutsConfig = ref<any[]>(
12- [{ text: t('dashboard.last1H'), value: 1 }, { text: t('dashboard.last3H'), value: 3 }, { text: t('dashboard.last6H'), value: 6 }]
13-);
14- 
15-const errorInfo = ref<string | Error>();
16- 
17-const props = withDefaults(
18- defineProps<{
19- instanceId?: string;
20- }>(),
21- {
22- instanceId: "",
23- }
24-);
25- 
26-// nodeId sync
27-const emit = defineEmits(["nodeIdChanged"]);
28-const clusterComponent = ref(null);
29-const culsterLoaded = ref<boolean>(false);
30-const initNodeId = ref<string>("");
31-const syncNodeId = (syncNodeIdVal: string) => {
32- if (syncNodeIdVal === null || syncNodeIdVal === "") return;
33- if (!culsterLoaded.value) initNodeId.value = syncNodeIdVal;
34- else {
35- clusterComponent.value.setNodeId(syncNodeIdVal);
36- if (syncNodeIdVal !== cluster.value[1]) {
37- Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
38- nextTick(() => {
39- requestData();
40- });
41- }
42- }
43-};
44-defineExpose({
45- syncNodeId,
46-});
47- 
48-const initFormData = {
49- reportRange: "CLUSTER",
50- reportType: "DETAIL",
51- dateValue: [moment(new Date()).format("YYYY-MM-DD") + " 00:00:00", moment(new Date()).format("YYYY-MM-DD") + " 23:59:59"],
52-};
53-const formData = reactive(cloneDeep(initFormData));
54-const tableData = ref<Array<any>>([]);
55- 
56-const page = reactive({
57- currentPage: 1,
58- pageSize: 10,
59- total: 10,
60-});
61-const snapshotManageShown = ref(false);
62-const buildWDRShown = ref(false);
63-const showSnapshotManage = () => {
64- snapshotManageShown.value = true;
65-};
66-const changeModalSnapshotManage = (val: boolean) => {
67- snapshotManageShown.value = val;
68-};
69-const showBuildWDR = () => {
70- buildWDRShown.value = true;
71-};
72-const changeModalBuildWDR = (val: boolean) => {
73- buildWDRShown.value = val;
74-};
75-const bandleCoveyBuildWDR = (code: number) => {
76- requestData();
77-};
78-const handleQuery = () => {
79- requestData();
80-};
81-const handleReset = () => {
82- formData.reportRange = initFormData.reportRange;
83- formData.reportType = initFormData.reportType;
84- formData.dateValue = initFormData.dateValue;
85- requestData();
86-};
87- 
88-const cluster = ref<Array<any>>([]);
89-const handleClusterValue = (val: any) => {
90- cluster.value = val;
91- emit("nodeIdChanged", cluster.value[1]);
92-};
93-const clusterLoaded = (val: any) => {
94- culsterLoaded.value = true;
95- if (initNodeId.value) {
96- clusterComponent.value.setNodeId(initNodeId.value);
97- nextTick(() => {
98- requestData();
99- });
100- }
101-};
102-const {
103- data: res,
104- run: requestData,
105- loading,
106-} = useRequest(
107- () => {
108- const clusterId = cluster.value.length ? cluster.value[0] : "";
109- return restRequest
110- .get("/wdr/list", {
111- clusterId,
112- wdrScope: formData.reportRange,
113- wdrType: formData.reportType,
114- start: formData.dateValue && formData.dateValue.length > 0 ? formData.dateValue[0] : null,
115- end: formData.dateValue && formData.dateValue.length > 1 ? formData.dateValue[1] : null,
116- pageSize: page.pageSize,
117- pageNum: page.currentPage,
118- })
119- .then(function (res) {
120- return res;
121- })
122- .catch(function (res) {
123- tableData.value = [];
124- Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
125- });
126- },
127- { manual: true }
128-);
129-type Res =
130- | {
131- records: string[];
132- pageNum: number;
133- total: number;
134- }
135- | undefined;
136-watch(res, (res: Res) => {
137- if (res && res.records && res.records.length) {
138- const { total } = res;
139- tableData.value = res.records;
140- Object.assign(page, { pageSize: page.pageSize, total });
141- } else {
142- tableData.value = [];
143- }
144-});
145-const handleSizeChange = (val: number) => {
146- page.currentPage = 1;
147- page.pageSize = val;
148- changePageCurrent(page.currentPage);
149-};
150-const handleCurrentChange = (val: number) => {
151- page.currentPage = val;
152- changePageCurrent(page.currentPage);
153-};
154-const changePageCurrent = (data: number) => {
155- Object.assign(page, data);
156- requestData();
157-};
158- 
159-// view WDR
160-type Row = {
161- wdrId: string;
162- reportName: string;
163-};
164-const { run: handleView, loading: viewing } = useRequest(
165- (row: Row) => {
166- return restRequest
167- .get("/wdr/downloadWdr", {
168- wdrId: row?.wdrId,
169- })
170- .then(function (res) {
171- const newWindow = window.open(row.reportName, "_blank");
172- newWindow?.document.write(res);
173- })
174- .catch(function (res) {});
175- },
176- { manual: true }
177-);
178- 
179-// download WDR
180-const { run: handleDownload, loading: downloading } = useRequest(
181- (row: Row) => {
182- return restRequest
183- .get("/wdr/downloadWdr", {
184- wdrId: row?.wdrId,
185- })
186- .then(function (res) {
187- if (res) {
188- const blob = new Blob([res], {
189- type: "text/plain",
190- });
191- const a = document.createElement("a");
192- const URL = window.URL || window.webkitURL;
193- const herf = URL.createObjectURL(blob);
194- a.href = herf;
195- a.download = row.reportName;
196- document.body.appendChild(a);
197- a.click();
198- document.body.removeChild(a);
199- window.URL.revokeObjectURL(herf);
200- }
201- })
202- .catch(function (res) {
203- tableData.value = [];
204- Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
205- });
206- },
207- { manual: true }
208-);
209- 
210-// delete row
211-const { run: hanleDelete, loading: deleting } = useRequest(
212- (row: Row) => {
213- return restRequest
214- .delete(`/wdr/del/${row.wdrId}`)
215- .then(function (res) {
216- requestData();
217- })
218- .catch(function (res) {});
219- },
220- { manual: true }
221-);
222-</script>
223- 
224-<template>
225- <div class="top-sql">
226- <div class="tab-wrapper-container">
227- <div class="search-form-multirow">
228- <div class="row">
229- <div class="filter">
230- <ClusterCascader ref="clusterComponent" @loaded="clusterLoaded" notClearable :title="$t('dashboard.wdrReports.clusterName')" @getCluster="handleClusterValue" />
231- </div>
232- <div class="filter">
233- <span>{{ $t("dashboard.wdrReports.reportRange") }}&nbsp;</span>
234- <el-select v-model="formData.reportRange" style="width: 160px; margin: 0 4px">
235- <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
236- <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
237- </el-select>
238- </div>
239- <div class="filter">
240- <span>{{ $t("dashboard.wdrReports.reportType") }}&nbsp;</span>
241- <el-select v-model="formData.reportType" style="width: 160px; margin: 0 4px">
242- <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
243- <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
244- <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
245- </el-select>
246- </div>
247- 
248- <div class="filter">
249- <span>{{ $t("dashboard.wdrReports.buildTime") }}&nbsp;</span>
250- <MyDatePicker v-model="formData.dateValue" :teleported="true" :start-placeholder="$t('app.startDate')" :end-placeholder="$t('app.endDate')" :shortcutsConfig="shortcutsConfig" type="datetimerange" style="width: 300px" />
251- </div>
252- </div>
253- 
254- <div class="row">
255- <div class="filter">
256- <el-button @click="handleQuery">{{ $t("app.query") }}</el-button>
257- <el-button @click="handleReset">{{ $t("app.reset") }}</el-button>
258- <el-button type="primary" @click="showSnapshotManage">{{ $t("dashboard.wdrReports.snapshotManage") }}</el-button>
259- <el-button type="primary" @click="showBuildWDR">{{ $t("dashboard.wdrReports.buildWDR") }}</el-button>
260- </div>
261- </div>
262- </div>
263- </div>
264- 
265- <div class="page-container">
266- <div class="table-wrapper" v-loading="loading || viewing || downloading || deleting">
267- <el-table class="normal-table" :data="tableData" :header-cell-style="{ 'text-align': 'center' }" style="width: 100%" :default-sort="{ prop: 'date', order: 'descending' }">
268- <el-table-column prop="scope" :label="$t('dashboard.wdrReports.reportRange')" min-width="10%" align="center" />
269- <el-table-column prop="reportAt" :label="$t('dashboard.wdrReports.list.buildTime')" min-width="20%" align="center" />
270- <el-table-column prop="reportType" :label="$t('dashboard.wdrReports.reportType')" min-width="10%" align="center" />
271- <el-table-column prop="reportName" :label="$t('dashboard.wdrReports.list.reportName')" align="center" min-width="40%"/>
272- <el-table-column :label="$t('app.operate')" align="center" fixed="right" min-width="20%">
273- <template #default="scope">
274- <div class="operate-btns">
275- <el-link size="small" type="primary" @click="handleView(scope.row)">{{ $t("app.view") }}</el-link>
276- <el-link size="small" type="primary" @click="handleDownload(scope.row)">{{ $t("app.download") }}</el-link>
277- <el-popconfirm title="Are you sure to delete this?" @confirm="hanleDelete(scope.row)">
278- <template #reference>
279- <el-link size="small" type="primary">{{ $t("app.delete") }}</el-link>
280- </template>
281- </el-popconfirm>
282- </div>
283- </template>
284- </el-table-column>
285- </el-table>
286- </div>
287- </div>
288- <el-pagination :currentPage="page.currentPage" :pageSize="page.pageSize" :total="page.total" :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small @size-change="handleSizeChange" @current-change="handleCurrentChange" />
289- 
290- <SnapshotManage :show="snapshotManageShown" @changeModal="changeModalSnapshotManage" />
291- <BuildWdr :show="buildWDRShown" @changeModal="changeModalBuildWDR" @conveyFlag="bandleCoveyBuildWDR" />
292- 
293- <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
294- </div>
295-</template>
296- 
297-<style scoped lang="scss">
298-@import "../../../assets/style/style1.scss";
299-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/wdr/build_wdr.vue+0-236
@@ -1,236 +0,0 @@
1-<template>
2- <div class="dialog">
3- <el-dialog
4- width="400px"
5- :title="$t('dashboard.wdrReports.buildWDR')"
6- v-model="visible"
7- :close-on-click-modal="false"
8- draggable
9- @close="taskClose"
10- >
11- <div class="dialog-content" v-loading="generating">
12- <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
13- <el-form-item :label="$t('datasource.cluterTitle')" prop="hostId">
14- <ClusterCascader
15- ref="clusterComponent"
16- width="200"
17- instanceValueKey="hostId"
18- @loaded="loaded"
19- @getCluster="handleClusterValue"
20- notClearable
21- />
22- </el-form-item>
23- <el-form-item :label="$t('dashboard.wdrReports.reportRange')" prop="reportRange">
24- <el-select v-model="formData.scope" style="width: 200px; margin: 0 4px">
25- <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
26- <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
27- </el-select>
28- </el-form-item>
29- <el-form-item :label="$t('dashboard.wdrReports.reportType')" prop="reportType">
30- <el-select v-model="formData.type" style="width: 200px; margin: 0 4px">
31- <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
32- <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
33- <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
34- </el-select>
35- </el-form-item>
36- <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.startSnapshot')" prop="startId">
37- <el-select v-model="formData.startId" style="width: 200px; margin: 0 4px">
38- <el-option
39- v-for="item in tableData"
40- :key="item.snapshotId"
41- :label="item.snapshotId"
42- :value="item.snapshotId"
43- :disabled="parseInt(item.snapshotId) >= parseInt(formData.endId)"
44- />
45- </el-select>
46- </el-form-item>
47- <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.endSnapshot')" prop="endId">
48- <el-select v-model="formData.endId" style="width: 200px; margin: 0 4px">
49- <el-option
50- v-for="item in tableData"
51- :key="item.snapshotId"
52- :label="item.snapshotId"
53- :value="item.snapshotId"
54- :disabled="parseInt(item.snapshotId) <= parseInt(formData.startId)"
55- />
56- </el-select>
57- </el-form-item>
58- </el-form>
59- </div>
60- 
61- <template #footer>
62- <el-button style="padding: 5px 20px" :loading="generating" type="primary" @click="handleconfirmModel">{{
63- $t('dashboard.wdrReports.buildWDRDialog.build')
64- }}</el-button>
65- <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t('app.cancel') }}</el-button>
66- </template>
67- </el-dialog>
68- </div>
69-</template>
70- 
71-<script lang="ts" setup>
72-import { cloneDeep } from 'lodash-es'
73-import { useRequest } from 'vue-request'
74-import { FormRules, FormInstance, ElMessage } from 'element-plus'
75-import { useI18n } from 'vue-i18n'
76-import restRequest from '@/request/restful'
77-import { useMonitorStore } from '@/store/monitor'
78-const { t } = useI18n()
79- 
80-const visible = ref(true)
81-const props = withDefaults(defineProps<{ tabId: string }>(), {})
82- 
83-const clusterComponent = ref<any>(null)
84-const loaded = () => {
85- let instanceId = useMonitorStore(props.tabId).instanceId
86- if (instanceId && clusterComponent.value != null) {
87- clusterComponent.value.setNodeId(instanceId)
88- nextTick(() => {
89- requestData()
90- })
91- }
92-}
93- 
94-// form data
95-const initFormData = {
96- clusterId: '',
97- endId: '',
98- hostId: '',
99- scope: 'CLUSTER',
100- startId: '',
101- type: 'DETAIL',
102-}
103-const formData = reactive(cloneDeep(initFormData))
104- 
105-// cluster component
106-const handleClusterValue = (val: any) => {
107- formData.clusterId = val.length ? val[0] : ''
108- formData.hostId = val.length > 1 ? val[1] : ''
109- if (formData.hostId) requestData()
110-}
111- 
112-// snapshotList
113-const tableData = ref<Array<any>>([])
114-const { data: res, run: requestData } = useRequest(
115- () => {
116- return restRequest
117- .get('/wdr/listSnapshot', {
118- clusterId: formData.clusterId,
119- hostId: formData.hostId,
120- orderby: 'snapshot_id desc',
121- pageSize: 20,
122- pageNum: 1,
123- })
124- .then(function (res) {
125- return res
126- })
127- .catch(function (res) {
128- tableData.value = []
129- })
130- },
131- { manual: true }
132-)
133-watch(res, (res) => {
134- if (res && res.records && res.records.length) {
135- tableData.value = res.records
136- if (tableData.value.length > 0) {
137- formData.startId = tableData.value[tableData.value.length - 1].snapshotId
138- formData.endId = tableData.value[0].snapshotId
139- }
140- } else {
141- tableData.value = []
142- }
143-})
144- 
145-// build
146-const connectionFormRef = ref<FormInstance>()
147-async function handleconfirmModel() {
148- try {
149- let result = await connectionFormRef.value?.validate()
150- if (result) {
151- buildWDR()
152- }
153- } catch (error) {}
154-}
155-const validateStartId = (rule: any, value: any, callback: any) => {
156- if (!value || !formData.endId) {
157- callback()
158- } else {
159- if (parseInt(value) >= parseInt(formData.endId)) {
160- callback(new Error(t('datasource.trackFormRules[5]')))
161- return
162- }
163- callback()
164- }
165-}
166-const validateEndId = (rule: any, value: any, callback: any) => {
167- if (!value || !formData.startId) {
168- callback()
169- } else {
170- if (parseInt(value) <= parseInt(formData.startId)) {
171- callback(new Error(t('datasource.trackFormRules[6]')))
172- }
173- callback()
174- }
175-}
176-const connectionFormRules = reactive<FormRules>({
177- hostId: [{ required: true, message: t('datasource.trackFormRules[0]'), trigger: 'blur' }],
178- startId: [
179- { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },
180- { validator: validateStartId, trigger: 'blur' },
181- ],
182- endId: [
183- { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },
184- { validator: validateEndId, trigger: 'blur' },
185- ],
186-})
187-const {
188- data: rez,
189- run: buildWDR,
190- loading: generating,
191-} = useRequest(
192- () => {
193- return restRequest.post('/wdr/generate', formData).then(function (res) {
194- return res
195- })
196- },
197- {
198- manual: true,
199- onSuccess: (res) => {
200- if (res && res.code === 200) {
201- const msg = t('dashboard.wdrReports.buildWDRDialog.buildSuccess')
202- ElMessage({
203- showClose: true,
204- message: msg,
205- type: 'success',
206- })
207- } else {
208- const msg = t('dashboard.wdrReports.buildWDRDialog.buildFail')
209- ElMessage({
210- showClose: true,
211- message: msg,
212- type: 'error',
213- })
214- }
215- },
216- }
217-)
218-watch(rez, (rez) => {
219- emit('conveyFlag')
220- visible.value = false
221- emit('changeModal', visible.value)
222-})
223- 
224-const emit = defineEmits(['changeModal', 'conveyFlag'])
225-const taskClose = () => {
226- visible.value = false
227- emit('changeModal', visible.value)
228-}
229-const handleCancelModel = () => {
230- visible.value = false
231- emit('changeModal', visible.value)
232-}
233-</script>
234-<style lang="scss" scoped>
235-@import '@/assets/style/style1.scss';
236-</style>
Dplugins/observability-instance/web-ui/src/pages/dashboard/wdr/snapshot_manage.vue+0-170
@@ -1,170 +0,0 @@
1-<template>
2- <div class="task-dialog">
3- <el-dialog width="800px" :title="$t('dashboard.wdrReports.snapshotManageDialog.dialogName')" v-model="show" :close-on-click-modal="false" draggable @close="taskClose">
4- <div class="dialog-content" v-loading="creatingSnapshot || reading">
5- <div class="search-form">
6- <div class="filter" style="margin-right: auto">
7- <el-button type="primary" @click="handelBuild">{{ $t("dashboard.wdrReports.snapshotManageDialog.createSnapshot") }}</el-button>
8- </div>
9- <div class="filter">
10- <div class="cluster-container-title">{{ $t("datasource.cluterTitle") }}&nbsp;</div>
11- <ClusterCascader instanceValueKey="hostId" @loaded="requestData" @getCluster="handleClusterValue" autoSelectFirst :options="clusterList" notClearable />
12- </div>
13- <div class="filter">
14- <el-button type="primary" @click="handleQuery">{{ $t("app.query") }}</el-button>
15- </div>
16- </div>
17- 
18- <div class="table-wrapper">
19- <el-table :data="tableData" :header-cell-style="{ 'text-align': 'left' }" style="width: 100%" :default-sort="{ prop: 'date', order: 'descending' }">
20- <el-table-column prop="snapshotId" width="300" :label="$t('dashboard.wdrReports.snapshotManageDialog.snapshotID')" align="left" />
21- <el-table-column prop="endTs" :label="$t('dashboard.wdrReports.snapshotManageDialog.captureTime')" align="left" />
22- </el-table>
23- <el-pagination :currentPage="page.currentPage" :pageSize="page.pageSize" :total="page.total" :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small @size-change="handleSizeChange" @current-change="handleCurrentChange" />
24- </div>
25- </div>
26- </el-dialog>
27- </div>
28-</template>
29- 
30-<script lang="ts" setup>
31-import { useRequest } from "vue-request";
32-import restRequest from "../../../request/restful";
33-import { FormRules, FormInstance, ElMessage } from 'element-plus'
34- 
35-// const visible = ref(false);
36-const props = withDefaults(
37- defineProps<{
38- show: boolean;
39- }>(),
40- {}
41-);
42-const page = reactive({
43- pageSize: 10,
44- currentPage: 1,
45- total: 30,
46-});
47-const handleSizeChange = (val: number) => {
48- page.currentPage = 1;
49- page.pageSize = val;
50- changePageCurrent(page.currentPage);
51-};
52-const handleCurrentChange = (val: number) => {
53- page.currentPage = val;
54- changePageCurrent(page.currentPage);
55-};
56-const changePageCurrent = (data: number) => {
57- Object.assign(page, data);
58- requestData();
59-};
60-// watch(
61-// () => props.show,
62-// (newValue) => {
63-// visible.value = newValue;
64-// },
65-// { immediate: true }
66-// );
67- 
68-const cluster = ref<Array<any>>([]);
69-const handleClusterValue = (val: any) => {
70- cluster.value = val;
71-};
72- 
73-const handleQuery = () => {
74- requestData();
75-};
76- 
77-const handelBuild = () => {
78- createSnapshot()
79- 
80-};
81-const { data:createRes, run: createSnapshot, loading: creatingSnapshot } = useRequest(
82- () => {
83- return restRequest
84- .get("/wdr/createSnapshot", {
85- clusterId: cluster.value.length ? cluster.value[0] : "",
86- hostId: cluster.value.length > 1 ? cluster.value[1] : "",
87- })
88- .then(function (res) {
89- return res;
90- })
91- .catch(function (res) {});
92- },
93- { manual: true,
94- onSuccess: res => {
95- if(res && res.code === 200) {
96- ElMessage({
97- showClose: true,
98- message: "创建成功!快照列表异步写入可能存在滞后,请手动刷新列表!",
99- type: 'success',
100- })
101- }
102- reading.value=false
103- // setTimeout(() => {
104- // requestData();
105- // },800)
106- },
107- }
108-);
109- 
110-// watch(createRes,(createRes) => {
111-// if(createRes && createRes.code === 200) {
112-// reading.value=true
113-// setTimeout(() => {
114-// requestData();
115-// },800)
116-// }
117-// })
118- 
119-// list Data
120-const tableData = ref<Array<any>>([]);
121-const {
122- data: res,
123- run: requestData,
124- loading: reading,
125-} = useRequest(
126- () => {
127- return restRequest
128- .get("/wdr/listSnapshot", {
129- clusterId: cluster.value.length ? cluster.value[0] : "",
130- hostId: cluster.value.length > 1 ? cluster.value[1] : "",
131- orderby: "snapshot_id desc",
132- pageSize: page.pageSize,
133- pageNum: page.currentPage,
134- })
135- .then(function (res) {
136- return res;
137- })
138- .catch(function (res) {
139- tableData.value = [];
140- Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
141- });
142- },
143- { manual: true }
144-);
145-type Res =
146- | {
147- records: string[];
148- pageNum: number;
149- total: number;
150- }
151- | undefined;
152-watch(res, (res: Res) => {
153- if (res && res.records && res.records.length) {
154- const { total } = res
155- tableData.value = res.records;
156- Object.assign(page, { pageSize: page.pageSize, total })
157- } else {
158- tableData.value = [];
159- }
160-});
161- 
162-const emits = defineEmits(['changeModal']);
163-const taskClose = () => {
164- emits('changeModal',false)
165-}
166-</script>
167- 
168-<style lang="scss" scoped>
169-@import "../../../assets/style/style1.scss";
170-</style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/Index.vue+143-3
@@ -13,6 +13,40 @@
13 <div class="cluster-title">{{ $t('instanceMonitor.clusterTitle') }}</div>13 <div class="cluster-title">{{ $t('instanceMonitor.clusterTitle') }}</div>
14 14 
15 <el-cascader v-model="clusterNodeId" :options="clusterList" />15 <el-cascader v-model="clusterNodeId" :options="clusterList" />
16+ <svg-icon class="info-hollow" name="info-hollow" style="margin-left: 4px" @click="showInfo" />
17+ <div style="position: relative" v-if="visible">
18+ <div class="instance-info">
19+ <div class="title-row">
20+ <div class="title">{{ $t('instanceMonitor.nodeInfo.instanceInfo') }}</div>
21+ <svg-icon class="close" name="close" style="margin-left: 4px" @click="visible = false" />
22+ </div>
23+ <div class="text" v-loading="loading">
24+ <div>{{ $t('instanceMonitor.nodeInfo.databaseVersion') }}{{ nodeInfoData?.version }}</div>
25+ <div>
26+ {{ $t('instanceMonitor.nodeInfo.databaseStartTime')
27+ }}{{ moment(nodeInfoData?.time).format('YYYY-MM-DD HH:mm:ss') as string }}
28+ </div>
29+ <div>{{ $t('instanceMonitor.nodeInfo.databaseDataDirectory') }}{{ nodeInfoData?.dbDataPath }}</div>
30+ <div>{{ $t('instanceMonitor.nodeInfo.databaseLogDirectory') }}{{ nodeInfoData?.dbLogPath }}</div>
31+ <div>
32+ {{ $t('instanceMonitor.nodeInfo.enableArchiving')
33+ }}{{
34+ nodeInfoData?.archiveMode === 'on'
35+ ? $t('instanceMonitor.nodeInfo.yes')
36+ : $t('instanceMonitor.nodeInfo.no')
37+ }}
38+ </div>
39+ <div>{{ $t('instanceMonitor.nodeInfo.operatingSystemVersion') }}{{ nodeInfoData?.osVersion }}</div>
40+ <div>{{ $t('instanceMonitor.nodeInfo.serverCPUManufacturer') }}{{ nodeInfoData?.CPUmanufacturer }}</div>
41+ <div>{{ $t('instanceMonitor.nodeInfo.serverCPUModel') }}{{ nodeInfoData?.CPUmodel }}</div>
42+ <div>
43+ {{ $t('instanceMonitor.nodeInfo.serverCPUCoreCount') }}{{ nodeInfoData?.CPUcores
44+ }}{{ $t('instanceMonitor.nodeInfo.cores') }}
45+ </div>
46+ <div>{{ $t('instanceMonitor.nodeInfo.totalMemorySize') }}{{ nodeInfoData?.TotalMemory }}</div>
47+ </div>
48+ </div>
49+ </div>
16 </div>50 </div>
17 <div style="position: absolute; left: 0px; top: 5px; z-index: 9999" @click="toggleCollapse">51 <div style="position: absolute; left: 0px; top: 5px; z-index: 9999" @click="toggleCollapse">
18 <el-icon v-if="!isCollapse" size="20px"><Fold /></el-icon>52 <el-icon v-if="!isCollapse" size="20px"><Fold /></el-icon>
@@ -36,6 +70,7 @@
36 <resource-monitor70 <resource-monitor
37 ref="refResourceMonitor"71 ref="refResourceMonitor"
38 @goto="goto"72 @goto="goto"
73+ @changeCluster="toChangeCluster"
39 :tabId="tabId"74 :tabId="tabId"
40 v-if="tabKeyLoaded.indexOf(tabKeys.ResourceMonitor) >= 0 || dashboardTabKey === tabKeys.ResourceMonitor"75 v-if="tabKeyLoaded.indexOf(tabKeys.ResourceMonitor) >= 0 || dashboardTabKey === tabKeys.ResourceMonitor"
41 />76 />
@@ -75,6 +110,15 @@
75 :instanceId="instanceId"110 :instanceId="instanceId"
76 />111 />
77 </el-tab-pane>112 </el-tab-pane>
113+ <el-tab-pane class="min-height" :label="'ASP'" :name="tabKeys.ASP">
114+ <asp
115+ :tabId="tabId"
116+ @goto="goto"
117+ ref="aspComponent"
118+ v-if="tabKeyLoaded.indexOf(tabKeys.ASP) >= 0 || dashboardTabKey === tabKeys.ASP"
119+ :instanceId="instanceId"
120+ />
121+ </el-tab-pane>
78 <el-tab-pane class="min-height" :label="$t('dashboard.systemConfig.tabName')" :name="tabKeys.SystemConfig">122 <el-tab-pane class="min-height" :label="$t('dashboard.systemConfig.tabName')" :name="tabKeys.SystemConfig">
79 <systemConfiguration123 <systemConfiguration
80 :tabId="tabId"124 :tabId="tabId"
@@ -100,12 +144,15 @@ import ResourceMonitor from '@/pages/dashboardV2/resourceMonitor/Index.vue'
100import InstanceMetrics from '@/pages/dashboardV2/instanceMonitor/instanceMetrics/Index.vue'144import InstanceMetrics from '@/pages/dashboardV2/instanceMonitor/instanceMetrics/Index.vue'
101import TOPSQL from '@/pages/dashboardV2/instanceMonitor/topSQL/Index.vue'145import TOPSQL from '@/pages/dashboardV2/instanceMonitor/topSQL/Index.vue'
102import Wdr from '@/pages/dashboardV2/wdr/Index.vue'146import Wdr from '@/pages/dashboardV2/wdr/Index.vue'
147+import Asp from '@/pages/dashboardV2/asp/Index.vue'
103import ogRequest from '@/request'148import ogRequest from '@/request'
104import { useRequest } from 'vue-request'149import { useRequest } from 'vue-request'
105import Install from '@/pages/dashboard/install/Index.vue'150import Install from '@/pages/dashboard/install/Index.vue'
106import SystemConfiguration from '@/pages/dashboardV2/systemConfiguration/Index.vue'151import SystemConfiguration from '@/pages/dashboardV2/systemConfiguration/Index.vue'
107import { tabKeys } from '@/pages/dashboardV2/common'152import { tabKeys } from '@/pages/dashboardV2/common'
108import { uuid } from '@/shared'153import { uuid } from '@/shared'
154+import { getNodeInfo } from '@/api/observability'
155+import moment from 'moment'
109 156 
110type Res =157type Res =
111 | [158 | [
@@ -119,7 +166,7 @@ const clusterNodeId = ref()
119const clusterList = ref<Array<any>>([])166const clusterList = ref<Array<any>>([])
120const nodeVersion = ref<string>('')167const nodeVersion = ref<string>('')
121const lastNodeId = ref<string>('')168const lastNodeId = ref<string>('')
122-const wdrComponent = ref(null)169+const wdrComponent = ref<InstanceType<typeof Wdr>>()
123const paramConfigComponent = ref(null)170const paramConfigComponent = ref(null)
124const performanceLoadRef = ref<InstanceType<typeof PerformanceLoad>>()171const performanceLoadRef = ref<InstanceType<typeof PerformanceLoad>>()
125const refResourceMonitor = ref<InstanceType<typeof ResourceMonitor>>()172const refResourceMonitor = ref<InstanceType<typeof ResourceMonitor>>()
@@ -129,11 +176,37 @@ const dashboardTabKey = ref<string>('')
129const tabId = uuid()176const tabId = uuid()
130const { instanceId } = storeToRefs(useMonitorStore(tabId))177const { instanceId } = storeToRefs(useMonitorStore(tabId))
131 178 
179+const router = useRouter()
132const tabKeyLoaded = ref<Array<string>>([])180const tabKeyLoaded = ref<Array<string>>([])
133- 181+const visible = ref(false)
134onMounted(() => {182onMounted(() => {
135 dashboardTabKey.value = tabKeys.Home183 dashboardTabKey.value = tabKeys.Home
136})184})
185+const toChangeCluster = (publicIp: string, port: string) => {
186+ for (let p1 = 0; p1 < clusterList.value.length; p1++) {
187+ const clusterTemp = clusterList.value[p1]
188+ for (let p2 = 0; p2 < clusterTemp.children.length; p2++) {
189+ const node = clusterTemp.children[p2]
190+ if (node.obj.publicIp === publicIp && node.obj.dbPort.toString() === port.toString()) {
191+ clusterNodeId.value = [clusterTemp.value, node.value]
192+ return
193+ }
194+ }
195+ }
196+}
197+const toChangeClusterByNodeId = (nodeId: string) => {
198+ for (let p1 = 0; p1 < clusterList.value.length; p1++) {
199+ const clusterTemp = clusterList.value[p1]
200+ for (let p2 = 0; p2 < clusterTemp.children.length; p2++) {
201+ const node = clusterTemp.children[p2]
202+ if (node.obj.nodeId === nodeId) {
203+ clusterNodeId.value = [clusterTemp.value, node.value]
204+ return
205+ }
206+ }
207+ }
208+}
209+ 
137// tab render only once210// tab render only once
138watch(dashboardTabKey, (v) => {211watch(dashboardTabKey, (v) => {
139 if (tabKeyLoaded.value.indexOf(v) < 0) tabKeyLoaded.value.push(v)212 if (tabKeyLoaded.value.indexOf(v) < 0) tabKeyLoaded.value.push(v)
@@ -145,11 +218,19 @@ const { data: opsClusterData } = useRequest(() => ogRequest.get('/observability/
145watch(opsClusterData, (res: Res) => {218watch(opsClusterData, (res: Res) => {
146 if (res && Object.keys(res).length) {219 if (res && Object.keys(res).length) {
147 clusterList.value = treeTransform(res)220 clusterList.value = treeTransform(res)
221+ 
222+ nextTick(() => {
223+ let paramsId = router.currentRoute.value.query.nodeId as string
224+ let nodeId = window.$wujie?.props.data.nodeId as string
225+ if (nodeId) toChangeClusterByNodeId(nodeId)
226+ else toChangeClusterByNodeId(paramsId)
227+ })
148 }228 }
149})229})
150 230 
151// cluster changed231// cluster changed
152watch(clusterNodeId, (res) => {232watch(clusterNodeId, (res) => {
233+ visible.value = false
153 // getInstanceId234 // getInstanceId
154 let curInstanceId = instanceId.value235 let curInstanceId = instanceId.value
155 if (typeof res === 'string') {236 if (typeof res === 'string') {
@@ -165,17 +246,19 @@ watch(clusterNodeId, (res) => {
165 246 
166 // find clusterId247 // find clusterId
167 let clusterId = ''248 let clusterId = ''
249+ let obj = {}
168 for (let p1 = 0; p1 < clusterList.value.length; p1++) {250 for (let p1 = 0; p1 < clusterList.value.length; p1++) {
169 const clusterTemp = clusterList.value[p1]251 const clusterTemp = clusterList.value[p1]
170 for (let p2 = 0; p2 < clusterTemp.children.length; p2++) {252 for (let p2 = 0; p2 < clusterTemp.children.length; p2++) {
171 const node = clusterTemp.children[p2]253 const node = clusterTemp.children[p2]
172 if (node.value === curInstanceId) {254 if (node.value === curInstanceId) {
173 clusterId = clusterTemp.value255 clusterId = clusterTemp.value
256+ obj = node.obj
174 break257 break
175 }258 }
176 }259 }
177 }260 }
178- useMonitorStore(tabId).updateInstanceAndClusterId(curInstanceId, clusterId)261+ useMonitorStore(tabId).updateInstanceAndClusterId(curInstanceId, clusterId, obj)
179})262})
180 263 
181const isCollapse = ref(true)264const isCollapse = ref(true)
@@ -199,6 +282,11 @@ const goto = (key: string, param: object) => {
199 nextTick(() => {282 nextTick(() => {
200 refResourceMonitor.value!.outsideGoto(key)283 refResourceMonitor.value!.outsideGoto(key)
201 })284 })
285+ } else if (key === tabKeys.WDR) {
286+ dashboardTabKey.value = tabKeys.WDR
287+ nextTick(() => {
288+ wdrComponent.value!.outsideGoto(param)
289+ })
202 }290 }
203}291}
204 292 
@@ -219,12 +307,20 @@ const treeTransform = (arr: any) => {
219 item.dbPort +307 item.dbPort +
220 (item.clusterRole ? '(' + item.clusterRole + ')' : ''),308 (item.clusterRole ? '(' + item.clusterRole + ')' : ''),
221 value: item.clusterId ? item.clusterId : item.nodeId,309 value: item.clusterId ? item.clusterId : item.nodeId,
310+ obj: item,
222 children: treeTransform(item.clusterNodes),311 children: treeTransform(item.clusterNodes),
223 })312 })
224 })313 })
225 }314 }
226 return obj315 return obj
227}316}
317+const showInfo = () => {
318+ if (!clusterNodeId.value) return
319+ visible.value = !visible.value
320+ loadNodeInfo(tabId)
321+}
322+const { data: nodeInfoData, run: loadNodeInfo, loading } = useRequest(getNodeInfo, { manual: true })
323+watch(nodeInfoData, () => {}, { deep: true })
228</script>324</script>
229 325 
230<style scoped lang="scss">326<style scoped lang="scss">
@@ -238,4 +334,48 @@ const treeTransform = (arr: any) => {
238.min-height {334.min-height {
239 min-height: 500px !important;335 min-height: 500px !important;
240}336}
337+ 
338+.instance-info {
339+ position: absolute;
340+ z-index: 900;
341+ top: -18px;
342+ left: 0px;
343+ width: 406px;
344+ border-radius: 4px;
345+ background: #fff;
346+ box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.2);
347+ .title-row {
348+ display: flex;
349+ flex-direction: row;
350+ align-items: center;
351+ width: 406px;
352+ height: 36px;
353+ flex-shrink: 0;
354+ border-radius: 2px 2px 0px 0px;
355+ border-bottom: 1px solid var(--unnamed, #d9d9d9);
356+ background: var(--fill, #f7f7f7);
357+ .title {
358+ font-size: 14px;
359+ font-weight: 500;
360+ line-height: 24px;
361+ flex-grow: 1;
362+ text-align: left;
363+ padding-left: 24px;
364+ }
365+ .close {
366+ width: 16px;
367+ height: 16px;
368+ flex-shrink: 0;
369+ margin-right: 10px;
370+ }
371+ }
372+ 
373+ .text {
374+ font-size: 12px;
375+ font-style: normal;
376+ font-weight: 400;
377+ line-height: 29px;
378+ padding: 12px 24px;
379+ }
380+}
241</style>381</style>
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/asp/Index.vue+528-0
@@ -0,0 +1,528 @@
1+<template>
2+ <IndexBar :tabId="props.tabId"></IndexBar>
3+ <div style="margin-bottom: 38px"></div>
4+ <my-card :title="$t('instanceMonitor.asp.sampleActiveSessionCount')" height="200" :bodyPadding="false">
5+ <LazyLine
6+ :rangeSelect="true"
7+ :tabId="props.tabId"
8+ :formatter="toFixed"
9+ :data="metricsData.sessionCount"
10+ :xData="metricsData.time"
11+ />
12+ </my-card>
13+ 
14+ <div class="gap-row"></div>
15+ 
16+ <my-card :title="$t('instanceMonitor.asp.aspAnalysis')" height="300" :bodyPadding="false">
17+ <div class="asp-row">
18+ <div class="title">{{ $t('instanceMonitor.asp.analysisMetrics') }}</div>
19+ <el-select class="category" v-model="optionValue" @change="selectChange">
20+ <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
21+ </el-select>
22+ <div class="title">{{ $t('instanceMonitor.asp.filterConditions') }}</div>
23+ <div class="filter">
24+ <div class="item" v-for="item in filter" :key="item">
25+ {{ item.label }}={{ item.value }}
26+ <svg-icon class="close" name="close" style="margin-left: 4px" @click="removeFilter(item)" />
27+ </div>
28+ </div>
29+ <div style="flex-grow: 1"></div>
30+ <div class="line-tips">
31+ <div><svg-icon name="info" />{{ $t('instanceMonitor.asp.clickLegendToAddFilter') }}</div>
32+ </div>
33+ </div>
34+ <div style="height: 200px">
35+ <LazyLine
36+ :tabId="props.tabId"
37+ :formatter="toFixed"
38+ :data="analyzeData.data"
39+ :xData="analyzeData.time"
40+ @legendSelected="legendSelected"
41+ :bar="true"
42+ />
43+ </div>
44+ </my-card>
45+ 
46+ <div class="gap-row"></div>
47+ 
48+ <el-row :gutter="12" style="min-height: 420px;">
49+ <el-col :span="12">
50+ <div class="table-column">
51+ <div class="header">
52+ <el-select class="category" v-model="tableOption1" @change="selectChangeTable1">
53+ <el-option v-for="item in tableOptions" :key="item.value" :label="item.label" :value="item.value" />
54+ </el-select>
55+ </div>
56+ <el-table class="table" :table-layout="'auto'" :data="table1" style="width: 100%" :border="true">
57+ <el-table-column :label="tableOptions.find((item) => item.value == tableOption1)?.label" width="150">
58+ <template #default="scope">
59+ <el-link type="primary" @click="gotoTopsqlDetail(scope.row.key)">
60+ {{ scope.row.key }}
61+ </el-link>
62+ </template>
63+ </el-table-column>
64+ <el-table-column :label="$t('instanceMonitor.asp.sampleCount')">
65+ <template #default="scope">
66+ <div
67+ style="
68+ width: 100%;
69+ display: flex;
70+ flex-direction: row;
71+ align-items: center;
72+ justify-content: flex-start;
73+ "
74+ >
75+ <div class="persent-row">
76+ <div
77+ class="persent"
78+ :style="{ width: scope.row.persent ? scope.row.persent * 100 * 0.8 + '%' : 'auto' }"
79+ ></div>
80+ <div class="persent-text">
81+ {{ scope.row.count }}({{ Number(scope.row.persent * 100).toFixed(2) }}%)
82+ </div>
83+ </div>
84+ </div>
85+ </template>
86+ </el-table-column>
87+ </el-table>
88+ </div>
89+ </el-col>
90+ <el-col :span="12">
91+ <div class="table-column">
92+ <div class="header">
93+ <el-select class="category" v-model="tableOption2" @change="selectChangeTable2">
94+ <el-option v-for="item in tableOptions" :key="item.value" :label="item.label" :value="item.value" />
95+ </el-select>
96+ </div>
97+ <el-table class="table" :table-layout="'auto'" :data="table2" style="width: 100%" :border="true">
98+ <el-table-column :label="tableOptions.find((item) => item.value == tableOption2)?.label" width="150">
99+ <template #default="scope">
100+ <el-link v-if="tableOption2 == 'sessionid'" type="primary" @click="gotoSessionDetail(scope.row.key)">
101+ {{ scope.row.key }}
102+ </el-link>
103+ <el-link v-if="tableOption2 == 'queryId'" type="primary" @click="gotoTopsqlDetail(scope.row.key)">
104+ {{ scope.row.key }}
105+ </el-link>
106+ </template>
107+ </el-table-column>
108+ <el-table-column :label="$t('instanceMonitor.asp.sampleCount')">
109+ <template #default="scope">
110+ <div
111+ style="
112+ width: 100%;
113+ display: flex;
114+ flex-direction: row;
115+ align-items: center;
116+ justify-content: flex-start;
117+ "
118+ >
119+ <div class="persent-row">
120+ <div
121+ class="persent"
122+ :style="{ width: scope.row.persent ? scope.row.persent * 100 * 0.8 + '%' : 'auto' }"
123+ ></div>
124+ <div class="persent-text">
125+ {{ scope.row.count }}({{ Number(scope.row.persent * 100).toFixed(2) }}%)
126+ </div>
127+ </div>
128+ </div>
129+ </template>
130+ </el-table-column>
131+ </el-table>
132+ </div>
133+ </el-col>
134+ </el-row>
135+</template>
136+ 
137+<script setup lang="ts">
138+import { useI18n } from 'vue-i18n'
139+import LazyLine from '@/components/echarts/LazyLine.vue'
140+import { useMonitorStore } from '@/store/monitor'
141+import { toFixed } from '@/shared'
142+import { storeToRefs } from 'pinia'
143+import { getAspCount, getAspAnalysis } from '@/api/asp'
144+import { useIntervalTime } from '@/hooks/time'
145+import { tabKeys } from '@/pages/dashboardV2/common'
146+import { useRequest } from 'vue-request'
147+import router from '@/router'
148+ 
149+const { t } = useI18n()
150+ 
151+const props = withDefaults(defineProps<{ tabId: string }>(), {})
152+ 
153+const optionValue = ref('applicationName')
154+const options = [
155+ {
156+ value: 'waitStatus',
157+ label: 'Wait Status',
158+ },
159+ {
160+ value: 'event',
161+ label: 'Event',
162+ },
163+ {
164+ value: 'databaseid',
165+ label: 'Database ID',
166+ },
167+ {
168+ value: 'applicationName',
169+ label: 'Application Name',
170+ },
171+]
172+const tableOption1 = ref('queryId')
173+const tableOption2 = ref('sessionid')
174+const tableOptions = [
175+ {
176+ value: 'queryId',
177+ label: 'SQL ID',
178+ },
179+ {
180+ value: 'sessionid',
181+ label: 'Session ID',
182+ },
183+]
184+ 
185+interface LineData {
186+ name: string
187+ data: any[]
188+ [other: string]: any
189+}
190+interface ActiveSessionData {
191+ sessionCount: LineData[]
192+ time: string[]
193+}
194+const metricsData = ref<ActiveSessionData>({
195+ sessionCount: [],
196+ time: [],
197+})
198+interface AnalyzeData {
199+ data: LineData[]
200+ time: string[]
201+}
202+const analyzeData = ref<AnalyzeData>({
203+ data: [],
204+ time: [],
205+})
206+const filter = ref<any[]>([])
207+const filterValue = ref<string[]>([])
208+const table1 = ref<any[]>([])
209+const table2 = ref<any[]>([])
210+ 
211+const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId } = storeToRefs(useMonitorStore(props.tabId))
212+ 
213+// same for every page in index
214+const timer = ref<number>()
215+onMounted(() => {
216+ load()
217+})
218+watch(
219+ updateCounter,
220+ () => {
221+ clearInterval(timer.value)
222+ if (tabNow.value === tabKeys.ASP) {
223+ if (updateCounter.value.source === sourceType.value.INSTANCE) {
224+ load()
225+ }
226+ if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()
227+ if (updateCounter.value.source === sourceType.value.TIMETYPE) load()
228+ if (updateCounter.value.source === sourceType.value.TIMERANGE) load()
229+ if (updateCounter.value.source === sourceType.value.TABCHANGE) load()
230+ const time = autoRefreshTime.value
231+ timer.value = useIntervalTime(
232+ () => {
233+ load()
234+ },
235+ computed(() => time * 1000)
236+ )
237+ }
238+ },
239+ { immediate: false }
240+)
241+const load = (checkTab?: boolean, checkRange?: boolean) => {
242+ if (!instanceId.value) return
243+ requestData(props.tabId)
244+ requestData2(props.tabId)
245+}
246+ 
247+// load data
248+const legendSelected = (key: string) => {
249+ if (!filter.value.some((item) => item.category === optionValue.value)) {
250+ filter.value.push({
251+ category: optionValue.value,
252+ label: options.find((item) => item.value === optionValue.value)?.label,
253+ value: key,
254+ })
255+ }
256+ filterValue.value = []
257+ filter.value.forEach((filterItem) => {
258+ filterValue.value.push(filterItem.label + '=' + filterItem.value)
259+ })
260+ analyzeData.value = getAnalyzeData()
261+}
262+const removeFilter = (item: any) => {
263+ let index = filter.value.indexOf(item)
264+ if (index >= 0) {
265+ filter.value.splice(index, 1)
266+ filterValue.value.splice(index, 1)
267+ }
268+ analyzeData.value = getAnalyzeData()
269+}
270+const { data: indexData, run: requestData } = useRequest(getAspCount, { manual: true })
271+watch(
272+ indexData,
273+ () => {
274+ // clear data
275+ metricsData.value.sessionCount = []
276+ const baseData = indexData.value
277+ if (!baseData) return
278+ 
279+ // TPS
280+ if (baseData) {
281+ metricsData.value.sessionCount.push({
282+ data: baseData.sessionCount,
283+ name: t('instanceMonitor.asp.activeSessionCount'),
284+ })
285+ metricsData.value.time = baseData.sampleTime
286+ }
287+ 
288+ // time
289+ },
290+ { deep: true }
291+)
292+const selectChange = () => {
293+ analyzeData.value = getAnalyzeData()
294+}
295+const selectChangeTable1 = () => {
296+ table1.value = getTopData(tableOption1.value)
297+}
298+const selectChangeTable2 = () => {
299+ table2.value = getTopData(tableOption2.value)
300+}
301+const { data: indexData2, run: requestData2 } = useRequest(getAspAnalysis, { manual: true })
302+watch(
303+ indexData2,
304+ () => {
305+ analyzeData.value = getAnalyzeData()
306+ // time
307+ },
308+ { deep: true }
309+)
310+const getAnalyzeData = () => {
311+ analyzeData.value = {
312+ data: [],
313+ time: [],
314+ }
315+ // clear data
316+ let dataTemp: any[] = []
317+ let timeTemp: Set<any> = new Set()
318+ if (indexData2.value) {
319+ table1.value = getTopData(tableOption1.value)
320+ table2.value = getTopData(tableOption2.value)
321+ 
322+ let data = indexData2.value
323+ 
324+ let categoryField = optionValue.value
325+ 
326+ // output sampleTime Array
327+ timeTemp = new Set(data.map((obj) => obj.sampleTime))
328+ 
329+ let filterData = data.filter((obj) => {
330+ let match = true
331+ filter.value.forEach((filterItem) => {
332+ if (obj[filterItem.category] !== filterItem.value) match = false
333+ })
334+ return match
335+ })
336+ 
337+ // Count the number of categoryField
338+ const categorySet = new Set(filterData.map((obj) => obj[categoryField]))
339+ 
340+ dataTemp.push({ data: [], name: 'Total', type: 'line', step: 'middle' })
341+ categorySet.forEach((categoryItem) => {
342+ dataTemp.push({ data: [], name: categoryItem, stack: 'Total', areaStyle: {} })
343+ })
344+ 
345+ let lastDate = ''
346+ let indexData = -1
347+ for (let index = 0; index < data.length; index++) {
348+ const element = data[index]
349+ if (element.sampleTime !== lastDate) {
350+ dataTemp.forEach((obj) => {
351+ obj.data.push(0)
352+ })
353+ indexData++
354+ lastDate = element.sampleTime
355+ }
356+ 
357+ let match = true
358+ filter.value.forEach((filterItem) => {
359+ if (element[filterItem.category] !== filterItem.value) match = false
360+ })
361+ if (match) {
362+ dataTemp.forEach((dateItem) => {
363+ if (dateItem.name === element[categoryField]) {
364+ dateItem.data[indexData]++
365+ }
366+ })
367+ }
368+ dataTemp[0].data[indexData]++
369+ }
370+ }
371+ 
372+ // time
373+ return {
374+ data: dataTemp,
375+ time: [...timeTemp],
376+ }
377+}
378+const getTopData = (analyzeField: any) => {
379+ let data = indexData2.value
380+ if (!data) return []
381+ 
382+ let filterData = data.filter((obj) => {
383+ let match = true
384+ filter.value.forEach((filterItem) => {
385+ if (obj[filterItem.category] !== filterItem.value) match = false
386+ })
387+ return match
388+ })
389+ 
390+ const sum = filterData.reduce((accumulator: number) => accumulator + 1, 0)
391+ 
392+ // Using the reduce method for grouping and counting.
393+ const countByQueryId = filterData.reduce((result, obj) => {
394+ const analyzeFiledValue = obj[analyzeField]
395+ result[analyzeFiledValue] = (result[analyzeFiledValue] || 0) + 1
396+ return result
397+ }, {})
398+ 
399+ // Convert to an array and sort by occurrence count.
400+ const sortedArray = Object.entries(countByQueryId)
401+ .map(([key, count]) => ({ key, count, persent: ((count as number) / sum).toFixed(2) }))
402+ .sort((a, b) => (b.count as number) - (a.count as number))
403+ 
404+ return sortedArray.slice(0, 10)
405+}
406+ 
407+const getParam = () => {
408+ return {
409+ dbid: instanceId,
410+ }
411+}
412+const gotoTopsqlDetail = (id: string) => {
413+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
414+ if (curMode === 'wujie') {
415+ // @ts-ignore plug-in components
416+ window.$wujie?.props.methods.jump({
417+ name: `Static-pluginObservability-instanceVemSql_detail`,
418+ query: {
419+ dbid: getParam().dbid.value,
420+ id,
421+ },
422+ })
423+ } else {
424+ // local
425+ window.sessionStorage.setItem('sqlId', id)
426+ router.push(`/vem/sql_detail/${getParam().dbid.value}/${id}`)
427+ }
428+}
429+const gotoSessionDetail = (id: string) => {
430+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
431+ if (curMode === 'wujie') {
432+ // @ts-ignore plug-in components
433+ window.$wujie?.props.methods.jump({
434+ name: `Static-pluginObservability-instanceVemSessionDetail`,
435+ query: {
436+ dbid: instanceId.value,
437+ id,
438+ },
439+ })
440+ } else {
441+ // local
442+ window.sessionStorage.setItem('sqlId', id)
443+ router.push(`/vem/sessionDetail/${instanceId.value}/${id}`)
444+ }
445+}
446+</script>
447+ 
448+<style scoped lang="scss">
449+.asp-row {
450+ font-size: 12px;
451+ display: flex;
452+ flex-direction: row;
453+ align-items: center;
454+ padding: 16px 16px 0px 16px;
455+ .title {
456+ flex-shrink: 0;
457+ margin-right: 6px;
458+ &:not(:first-child) {
459+ margin-left: 16px;
460+ }
461+ }
462+ 
463+ .category {
464+ width: 150px;
465+ flex-shrink: 0;
466+ }
467+ .filter {
468+ min-width: 200px;
469+ padding: 0px 4px;
470+ height: 30px;
471+ align-items: center;
472+ border-radius: 2px;
473+ border: 1px solid var(--unnamed, #d9d9d9);
474+ display: flex;
475+ flex-direction: row;
476+ gap: 4px;
477+ overflow-x: auto;
478+ .item {
479+ height: 20px;
480+ padding: 0px 4px 0px 8px;
481+ border-radius: 2px;
482+ border: 1px solid var(--unnamed, #d9d9d9);
483+ background: var(--fill, #f7f7f7);
484+ flex-shrink: 0;
485+ }
486+ }
487+ .line-tips {
488+ position: inherit;
489+ margin-left: 16px;
490+ flex-shrink: 0;
491+ }
492+}
493+::-webkit-scrollbar {
494+ width: 1px;
495+ height: 5px;
496+ background-color: skyblue;
497+}
498+::-webkit-scrollbar-thumb {
499+ background-color: orange;
500+}
501+.table-column {
502+ .header {
503+ display: flex;
504+ padding: 4px;
505+ align-items: center;
506+ border-top: 1px solid var(--dividers, #f0f0f0);
507+ border-right: 1px solid var(--dividers, #f0f0f0);
508+ background: var(--fill, #f7f7f7);
509+ }
510+}
511+ 
512+.persent-row {
513+ flex-grow: 1;
514+ position: relative;
515+ display: flex;
516+ flex-direction: row;
517+ align-items: center;
518+ .persent {
519+ min-width: 2px;
520+ height: 8px;
521+ border-radius: 1px;
522+ background: #246cff;
523+ }
524+ .persent-text {
525+ margin-left: 2px;
526+ }
527+}
528+</style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/common.ts+34-30
@@ -3,37 +3,41 @@
3///3///
4 4 
5export type keyObject = {5export type keyObject = {
6- Home: string6+ Home: string
7- WDR: string7+ WDR: string
8- SystemConfig: string8+ ASP: string
9- ResourceMonitor: string9+ SystemConfig: string
10- ResourceMonitorCPU: string10+ ResourceMonitor: string
11- ResourceMonitorMemory: string11+ ResourceMonitorCPU: string
12- ResourceMonitorIO: string12+ ResourceMonitorMemory: string
13- ResourceMonitorNetwork: string13+ ResourceMonitorIO: string
14- InstanceMonitor: string14+ ResourceMonitorNetwork: string
15- InstanceMonitorInstance: string15+ InstanceMonitor: string
16- InstanceMonitorSession: string16+ InstanceMonitorInstance: string
17- InstanceMonitorTOPSQL: string17+ InstanceMonitorSession: string
18- InstanceMonitorTOPSQLDBTime: string18+ InstanceMonitorTOPSQL: string
19- InstanceMonitorTOPSQLCPUTime: string19+ InstanceMonitorTOPSQLDBTime: string
20- InstanceMonitorTOPSQLEXECTime: string20+ InstanceMonitorTOPSQLCPUTime: string
21+ InstanceMonitorTOPSQLEXECTime: string
22+ InstanceMonitorTOPSQLIOTime: string
21}23}
22 24 
23export const tabKeys: keyObject = {25export const tabKeys: keyObject = {
24- Home: 'Home',26+ Home: 'Home',
25- WDR: 'WDR',27+ WDR: 'WDR',
26- SystemConfig: 'SystemConfig',28+ ASP: 'ASP',
27- ResourceMonitor: 'ResourceMonitor',29+ SystemConfig: 'SystemConfig',
28- ResourceMonitorCPU: 'ResourceMonitorCPU',30+ ResourceMonitor: 'ResourceMonitor',
29- ResourceMonitorMemory: 'ResourceMonitorMemory',31+ ResourceMonitorCPU: 'ResourceMonitorCPU',
30- ResourceMonitorIO: 'ResourceMonitorIO',32+ ResourceMonitorMemory: 'ResourceMonitorMemory',
31- ResourceMonitorNetwork: 'ResourceMonitorNetwork',33+ ResourceMonitorIO: 'ResourceMonitorIO',
32- InstanceMonitor: 'InstanceMonitor',34+ ResourceMonitorNetwork: 'ResourceMonitorNetwork',
33- InstanceMonitorInstance: 'InstanceMonitorInstance',35+ InstanceMonitor: 'InstanceMonitor',
34- InstanceMonitorSession: 'InstanceMonitorSession',36+ InstanceMonitorInstance: 'InstanceMonitorInstance',
35- InstanceMonitorTOPSQL: 'InstanceMonitorTOPSQL',37+ InstanceMonitorSession: 'InstanceMonitorSession',
36- InstanceMonitorTOPSQLDBTime: 'InstanceMonitorTOPSQLDBTime',38+ InstanceMonitorTOPSQL: 'InstanceMonitorTOPSQL',
37- InstanceMonitorTOPSQLCPUTime: 'InstanceMonitorTOPSQLCPUTime',39+ InstanceMonitorTOPSQLDBTime: 'InstanceMonitorTOPSQLDBTime',
38- InstanceMonitorTOPSQLEXECTime: 'InstanceMonitorTOPSQLEXECTime',40+ InstanceMonitorTOPSQLCPUTime: 'InstanceMonitorTOPSQLCPUTime',
41+ InstanceMonitorTOPSQLEXECTime: 'InstanceMonitorTOPSQLEXECTime',
42+ InstanceMonitorTOPSQLIOTime: 'InstanceMonitorTOPSQLIOTime',
39}43}
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue+428-492
@@ -1,407 +1,343 @@
1<template>1<template>
2- <div class="tab-wrapper">2+ <div class="tab-wrapper">
3- <el-container>3+ <el-container>
4- <el-main style="position: relative; padding-top: 0px">4+ <el-main style="position: relative; padding-top: 0px">
5- <div class="page-header">5+ <div class="page-header">
6- <div class="icon"></div>6+ <div class="icon"></div>
7- <div class="title">7+ <div class="title">
8- {{ $t('session.detail.tabTitle') }}({{ $t('session.detail.sessionID') }}{{ sessionId }})8+ {{ $t('session.detail.tabTitle') }}({{ $t('session.detail.sessionID') }}{{ sessionId }})
9+ </div>
10+ </div>
11+ <div style="position: relative">
12+ <div
13+ style="
14+ position: absolute;
15+ right: 0px;
16+ top: 2px;
17+ z-index: 1;
18+ display: flex;
19+ flex-direction: row;
20+ align-items: center;
21+ font-size: 12px;
22+ "
23+ >
24+ <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>
25+ 
26+ <div>{{ $t('app.autoRefreshFor') }}</div>
27+ <el-select v-model="innerRefreshTime" style="width: 100px; margin: 0 4px" @change="updateTimerInner">
28+ <el-option :value="99999999" label="NO-AUTO" />
29+ <el-option :value="1" label="1s" />
30+ <el-option :value="15" label="15s" />
31+ <el-option :value="30" label="30s" />
32+ <el-option :value="60" label="60s" />
33+ </el-select>
34+ <el-button
35+ class="refresh-button"
36+ type="primary"
37+ :icon="Refresh"
38+ style="margin-left: 8px"
39+ @click="loadSessionData(instanceId, sessionId)"
40+ />
41+ </div>
42+ <el-tabs v-model="dashboardTabKey" class="tab2">
43+ <el-tab-pane :label="$t('session.detail.info.tabTitle')" :name="tabKeys[0]">
44+ <div class="line-tips center" v-if="tips">
45+ <div class=""><svg-icon name="info" />{{ tips }}</div>
46+ </div>
47+ <el-row v-if="!tips" :gutter="12">
48+ <el-col :span="12">
49+ <my-card
50+ :title="$t('session.detail.info.server')"
51+ height="210"
52+ :bodyPadding="false"
53+ class="card-margin-bottom"
54+ >
55+ <div class="text-in-card">
56+ <div class="text-row">
57+ <div class="label">{{ $t('session.detail.info.sessionStatus') }}</div>
58+ <div class="value">{{ sessionData.general.state }}</div>
59+ </div>
60+ <div class="text-row">
61+ <div class="label">{{ $t('session.detail.info.sessionID') }}</div>
62+ <div class="value">{{ sessionData.general.sessionid }}</div>
63+ </div>
64+ <div class="text-row">
65+ <div class="label">{{ $t('session.detail.info.osThreadID') }}</div>
66+ <div class="value">{{ sessionData.general.lwtid }}</div>
67+ </div>
68+ <div class="text-row">
69+ <div class="label">{{ $t('session.detail.info.dbUserName') }}</div>
70+ <div class="value">{{ sessionData.general.usename }}</div>
71+ </div>
72+ <div class="text-row">
73+ <div class="label">{{ $t('session.detail.info.loginTime') }}</div>
74+ <div class="value">{{ sessionData.general.backend_start }}</div>
75+ </div>
76+ <div class="text-row">
77+ <div class="label">{{ $t('session.detail.info.loginDuration') }}</div>
78+ <div class="value">{{ sessionData.general.backend_runtime }}</div>
79+ </div>
80+ 
81+ <div class="text-row">
82+ <div class="label">{{ $t('session.detail.info.resourcePool') }}</div>
83+ <div class="value">{{ sessionData.general.resource_pool }}</div>
84+ </div>
9 </div>85 </div>
10- </div>86+ </my-card>
11- <div style="position: relative">87+ </el-col>
12- <div88+ <el-col :span="12">
13- style="89+ <my-card :title="$t('session.detail.client.tabTitle')" height="210" :bodyPadding="false">
14- position: absolute;90+ <div class="text-in-card">
15- right: 0px;91+ <div class="text-row">
16- top: 2px;92+ <div class="label">{{ $t('session.detail.client.clientIP') }}</div>
17- z-index: 1;93+ <div class="value">{{ sessionData.general.client_addr }}</div>
18- display: flex;94+ </div>
19- flex-direction: row;95+ <div class="text-row">
20- align-items: center;96+ <div class="label">
21- font-size: 12px;97+ {{ $t('session.detail.client.clientHostName') }}
22- "98+ </div>
99+ <div class="value">{{ sessionData.general.client_hostname }}</div>
100+ </div>
101+ <div class="text-row">
102+ <div class="label">{{ $t('session.detail.client.clientTCPPort') }}</div>
103+ <div class="value">{{ sessionData.general.client_port }}</div>
104+ </div>
105+ <div class="text-row">
106+ <div class="label">{{ $t('session.detail.client.appName') }}</div>
107+ <div class="value">{{ sessionData.general.application_name }}</div>
108+ </div>
109+ <div class="text-row">
110+ <div class="label">{{ $t('session.detail.client.connectDBName') }}</div>
111+ <div class="value">{{ sessionData.general.datname }}</div>
112+ </div>
113+ <div class="text-row">
114+ <div class="label">{{ $t('session.detail.client.txStartTime') }}</div>
115+ <div class="value">{{ sessionData.general.xact_start }}</div>
116+ </div>
117+ <div class="text-row">
118+ <div class="label">
119+ {{ $t('session.detail.client.queryStartTime') }}
120+ </div>
121+ <div class="value">{{ sessionData.general.query_start }}</div>
122+ </div>
123+ <div class="text-row">
124+ <div class="label">{{ $t('session.detail.client.queryID') }}</div>
125+ <div class="value">{{ sessionData.general.query_id }}</div>
126+ </div>
127+ </div>
128+ </my-card>
129+ </el-col>
130+ </el-row>
131+ <el-row v-if="!tips" :gutter="12">
132+ <el-col :span="12">
133+ <my-card
134+ :title="$t('session.detail.block.tabTitle')"
135+ height="160"
136+ :bodyPadding="false"
137+ class="card-margin-bottom"
138+ >
139+ <div class="text-in-card">
140+ <div class="text-row">
141+ <div class="label" style="width: 90px">
142+ {{ $t('session.detail.block.blockedSessionID') }}
143+ </div>
144+ <div class="value">{{ sessionData.general.block_sessionid }}</div>
145+ </div>
146+ <div class="text-row">
147+ <div class="label" style="width: 90px">
148+ {{ $t('session.detail.block.file') }}
149+ </div>
150+ <div class="value">{{ sessionData.general.filepath }}</div>
151+ </div>
152+ <div class="text-row">
153+ <div class="label" style="width: 90px">
154+ {{ $t('session.detail.block.pageNumber') }}
155+ </div>
156+ <div class="value">{{ sessionData.general.page }}</div>
157+ </div>
158+ <div class="text-row">
159+ <div class="label" style="width: 90px">
160+ {{ $t('session.detail.block.lineNumber') }}
161+ </div>
162+ <div class="value">{{ sessionData.general.tuple }}</div>
163+ </div>
164+ <div class="text-row">
165+ <div class="label" style="width: 90px">
166+ {{ $t('session.detail.block.bucketNumber') }}
167+ </div>
168+ <div class="value">{{ sessionData.general.bucket }}</div>
169+ </div>
170+ </div>
171+ </my-card>
172+ </el-col>
173+ <el-col :span="12">
174+ <my-card :title="$t('session.detail.wait.tabTitle')" height="160" :bodyPadding="false">
175+ <div class="text-in-card">
176+ <div class="text-row">
177+ <div class="label" style="width: 130px">
178+ {{ $t('session.detail.wait.waitState') }}
179+ </div>
180+ <div class="value">{{ sessionData.general.wait_status }}</div>
181+ </div>
182+ <div class="text-row">
183+ <div class="label" style="width: 130px">
184+ {{ $t('session.detail.wait.waitEventType') }}
185+ </div>
186+ <div class="value">{{ sessionData.general.wait_event }}</div>
187+ </div>
188+ <div class="text-row">
189+ <div class="label" style="width: 130px">
190+ {{ $t('session.detail.wait.waitLockMode') }}
191+ </div>
192+ <div class="value">{{ sessionData.general.lockmode }}</div>
193+ </div>
194+ <div class="text-row">
195+ <div class="label" style="width: 130px">
196+ {{ $t('session.detail.wait.waitObject') }}
197+ </div>
198+ <div class="value">{{ sessionData.general.namespace_relation }}</div>
199+ </div>
200+ </div>
201+ </my-card>
202+ </el-col>
203+ </el-row>
204+ <el-row v-if="!tips" :gutter="12">
205+ <el-col :span="24">
206+ <my-card :title="$t('session.detail.currentQuerySQL')" height="200" :bodyPadding="false">
207+ <div class="text-in-card">
208+ <div class="text-row">
209+ <div class="value">{{ sessionData.general.query }}</div>
210+ </div>
211+ </div>
212+ </my-card>
213+ </el-col>
214+ </el-row>
215+ </el-tab-pane>
216+ <el-tab-pane :label="$t('session.detail.statistic.tabTitle')" :name="tabKeys[1]">
217+ <div class="line-tips center" v-if="tips">
218+ <div class=""><svg-icon name="info" />{{ tips }}</div>
219+ </div>
220+ <el-row v-if="!tips" :gutter="12">
221+ <el-col :span="12">
222+ <my-card
223+ :title="$t('session.detail.statistic.sessionStatusStatistics')"
224+ height="600"
225+ :bodyPadding="false"
226+ skipBodyHeight
227+ >
228+ <el-table
229+ :data="sessionData.statisticStatus"
230+ style="width: 100%; height: 560px"
231+ border
232+ :header-cell-class-name="
233+ () => {
234+ return 'grid-header'
235+ }
236+ "
23 >237 >
24- <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>238+ <el-table-column prop="name" :label="$t('session.detail.statistic.name')" />
25- 239+ <el-table-column prop="value" :label="$t('session.detail.statistic.value')" />
26- <div>{{ $t('app.autoRefreshFor') }}</div>240+ </el-table>
27- <el-select241+ </my-card>
28- v-model="innerRefreshTime"242+ </el-col>
29- style="width: 60px; margin: 0 4px"243+ <el-col :span="12">
30- @change="updateTimerInner"244+ <my-card
31- >245+ :title="$t('session.detail.statistic.sessionRuntimeInformation')"
32- <el-option :value="1" label="1s" />246+ height="600"
33- <el-option :value="15" label="15s" />247+ :bodyPadding="false"
34- <el-option :value="30" label="30s" />248+ skipBodyHeight
35- <el-option :value="60" label="60s" />249+ >
36- </el-select>250+ <el-table
37- <el-button251+ :data="sessionData.statisticRuntime"
38- class="refresh-button"252+ style="width: 100%; height: 560px"
39- type="primary"253+ border
40- :icon="Refresh"254+ :header-cell-class-name="
41- style="margin-left: 8px"255+ () => {
42- @click="loadSessionData(instanceId, sessionId)"256+ return 'grid-header'
43- />257+ }
44- </div>258+ "
45- <el-tabs v-model="dashboardTabKey" class="tab2">259+ >
46- <el-tab-pane :label="$t('session.detail.info.tabTitle')" :name="tabKeys[0]">260+ <el-table-column prop="name" :label="$t('session.detail.statistic.name')" />
47- <div class="line-tips center" v-if="tips">261+ <el-table-column prop="value" :label="$t('session.detail.statistic.value')" />
48- <div class=""><svg-icon name="info" />{{ tips }}</div>262+ </el-table>
49- </div>263+ </my-card>
50- <el-row v-if="!tips" :gutter="12">264+ </el-col>
51- <el-col :span="12">265+ </el-row>
52- <my-card266+ </el-tab-pane>
53- :title="$t('session.detail.info.server')"267+ <el-tab-pane :label="$t('session.detail.blockTree.tabTitle')" :name="tabKeys[2]">
54- height="210"268+ <div class="line-tips center" v-if="tips">
55- :bodyPadding="false"269+ <div class=""><svg-icon name="info" />{{ tips }}</div>
56- class="card-margin-bottom"270+ </div>
57- >271+ <el-table
58- <div class="text-in-card">272+ v-if="!tips"
59- <div class="text-row">273+ :table-layout="'auto'"
60- <div class="label">{{ $t('session.detail.info.sessionStatus') }}</div>274+ :data="sessionData.blockTree"
61- <div class="value">{{ sessionData.general.state }}</div>275+ style="width: 100%"
62- </div>276+ border
63- <div class="text-row">277+ :header-cell-class-name="
64- <div class="label">{{ $t('session.detail.info.sessionID') }}</div>278+ () => {
65- <div class="value">{{ sessionData.general.sessionid }}</div>279+ return 'grid-header'
66- </div>280+ }
67- <div class="text-row">281+ "
68- <div class="label">{{ $t('session.detail.info.osThreadID') }}</div>282+ row-key="id"
69- <div class="value">{{ sessionData.general.lwtid }}</div>283+ default-expand-all
70- </div>284+ >
71- <div class="text-row">285+ <el-table-column :label="$t('session.detail.blockTree.sessionID')" width="120">
72- <div class="label">{{ $t('session.detail.info.dbUserName') }}</div>286+ <template #default="scope">
73- <div class="value">{{ sessionData.general.usename }}</div>287+ <el-link type="primary" @click="gotoSessionDetail(scope.row.id)">
74- </div>288+ {{ scope.row.id === '0' ? '' : scope.row.id }}
75- <div class="text-row">289+ </el-link>
76- <div class="label">{{ $t('session.detail.info.loginTime') }}</div>290+ </template>
77- <div class="value">{{ sessionData.general.backend_start }}</div>291+ </el-table-column>
78- </div>292+ <el-table-column :label="$t('session.detail.blockTree.blockedSessionID')" width="100">
79- <div class="text-row">293+ <template #default="scope">
80- <div class="label">{{ $t('session.detail.info.loginDuration') }}</div>294+ <el-link type="primary" @click="gotoSessionDetail(scope.row.parentid)">
81- <div class="value">{{ sessionData.general.backend_runtime }}</div>295+ {{ scope.row.parentid === '0' ? '' : scope.row.parentid }}
82- </div>296+ </el-link>
83- 297+ </template>
84- <div class="text-row">298+ </el-table-column>
85- <div class="label">{{ $t('session.detail.info.resourcePool') }}</div>299+ <el-table-column
86- <div class="value">{{ sessionData.general.resource_pool }}</div>300+ prop="backend_start"
87- </div>301+ :label="$t('session.detail.blockTree.sessionStartTime')"
88- </div>302+ :formatter="(r) => dateFormat(r.backend_start, 'MM-DD HH:mm:ss')"
89- </my-card>303+ width="110"
90- </el-col>304+ />
91- <el-col :span="12">305+ <el-table-column prop="wait_status" :label="$t('session.detail.blockTree.waitState')" width="100" />
92- <my-card306+ <el-table-column prop="wait_event" :label="$t('session.detail.blockTree.waitEvent')" width="100" />
93- :title="$t('session.detail.client.tabTitle')"307+ <el-table-column prop="lockmode" :label="$t('session.detail.blockTree.waitLockMode')" width="120" />
94- height="210"308+ <el-table-column prop="datname" :label="$t('session.detail.blockTree.dbName')" width="110" />
95- :bodyPadding="false"309+ <el-table-column prop="usename" :label="$t('session.detail.blockTree.userName')" width="90" />
96- >310+ <el-table-column prop="client_addr" :label="$t('session.detail.blockTree.clientIP')" width="120" />
97- <div class="text-in-card">311+ <el-table-column prop="application_name" :label="$t('session.detail.blockTree.appName')" />
98- <div class="text-row">312+ </el-table>
99- <div class="label">{{ $t('session.detail.client.clientIP') }}</div>313+ </el-tab-pane>
100- <div class="value">{{ sessionData.general.client_addr }}</div>314+ <el-tab-pane :label="$t('session.detail.waitRecord.tabTitle')" :name="tabKeys[3]">
101- </div>315+ <div class="line-tips center" v-if="tips">
102- <div class="text-row">316+ <div class=""><svg-icon name="info" />{{ tips }}</div>
103- <div class="label">317+ </div>
104- {{ $t('session.detail.client.clientHostName') }}318+ <el-table
105- </div>319+ v-if="!tips"
106- <div class="value">{{ sessionData.general.client_hostname }}</div>320+ :data="sessionData.waiting"
107- </div>321+ style="width: 100%; height: 560px"
108- <div class="text-row">322+ border
109- <div class="label">{{ $t('session.detail.client.clientTCPPort') }}</div>323+ :header-cell-class-name="
110- <div class="value">{{ sessionData.general.client_port }}</div>324+ () => {
111- </div>325+ return 'grid-header'
112- <div class="text-row">326+ }
113- <div class="label">{{ $t('session.detail.client.appName') }}</div>327+ "
114- <div class="value">{{ sessionData.general.application_name }}</div>328+ >
115- </div>329+ <el-table-column prop="sample_time" :label="$t('session.detail.waitRecord.sampleTime')" width="140" />
116- <div class="text-row">330+ <el-table-column prop="wait_status" :label="$t('session.detail.waitRecord.waitState')" width="120" />
117- <div class="label">{{ $t('session.detail.client.connectDBName') }}</div>331+ <el-table-column prop="event" :label="$t('session.detail.waitRecord.waitEvent')" width="120" />
118- <div class="value">{{ sessionData.general.datname }}</div>332+ <el-table-column prop="lockmode" :label="$t('session.detail.waitRecord.waitLockMode')" width="160" />
119- </div>333+ <el-table-column prop="locktag" :label="$t('session.detail.waitRecord.lockInfo')" />
120- <div class="text-row">334+ </el-table>
121- <div class="label">{{ $t('session.detail.client.txStartTime') }}</div>335+ </el-tab-pane>
122- <div class="value">{{ sessionData.general.xact_start }}</div>336+ </el-tabs>
123- </div>337+ </div>
124- <div class="text-row">338+ </el-main>
125- <div class="label">339+ </el-container>
126- {{ $t('session.detail.client.queryStartTime') }}340+ </div>
127- </div>
128- <div class="value">{{ sessionData.general.query_start }}</div>
129- </div>
130- <div class="text-row">
131- <div class="label">{{ $t('session.detail.client.queryID') }}</div>
132- <div class="value">{{ sessionData.general.query_id }}</div>
133- </div>
134- </div>
135- </my-card>
136- </el-col>
137- </el-row>
138- <el-row v-if="!tips" :gutter="12">
139- <el-col :span="12">
140- <my-card
141- :title="$t('session.detail.block.tabTitle')"
142- height="160"
143- :bodyPadding="false"
144- class="card-margin-bottom"
145- >
146- <div class="text-in-card">
147- <div class="text-row">
148- <div class="label" style="width: 90px">
149- {{ $t('session.detail.block.blockedSessionID') }}
150- </div>
151- <div class="value">{{ sessionData.general.block_sessionid }}</div>
152- </div>
153- <div class="text-row">
154- <div class="label" style="width: 90px">
155- {{ $t('session.detail.block.file') }}
156- </div>
157- <div class="value">{{ sessionData.general.filepath }}</div>
158- </div>
159- <div class="text-row">
160- <div class="label" style="width: 90px">
161- {{ $t('session.detail.block.pageNumber') }}
162- </div>
163- <div class="value">{{ sessionData.general.page }}</div>
164- </div>
165- <div class="text-row">
166- <div class="label" style="width: 90px">
167- {{ $t('session.detail.block.lineNumber') }}
168- </div>
169- <div class="value">{{ sessionData.general.tuple }}</div>
170- </div>
171- <div class="text-row">
172- <div class="label" style="width: 90px">
173- {{ $t('session.detail.block.bucketNumber') }}
174- </div>
175- <div class="value">{{ sessionData.general.bucket }}</div>
176- </div>
177- </div>
178- </my-card>
179- </el-col>
180- <el-col :span="12">
181- <my-card
182- :title="$t('session.detail.wait.tabTitle')"
183- height="160"
184- :bodyPadding="false"
185- >
186- <div class="text-in-card">
187- <div class="text-row">
188- <div class="label" style="width: 130px">
189- {{ $t('session.detail.wait.waitState') }}
190- </div>
191- <div class="value">{{ sessionData.general.wait_status }}</div>
192- </div>
193- <div class="text-row">
194- <div class="label" style="width: 130px">
195- {{ $t('session.detail.wait.waitEventType') }}
196- </div>
197- <div class="value">{{ sessionData.general.wait_event }}</div>
198- </div>
199- <div class="text-row">
200- <div class="label" style="width: 130px">
201- {{ $t('session.detail.wait.waitLockMode') }}
202- </div>
203- <div class="value">{{ sessionData.general.lockmode }}</div>
204- </div>
205- <div class="text-row">
206- <div class="label" style="width: 130px">
207- {{ $t('session.detail.wait.waitObject') }}
208- </div>
209- <div class="value">{{ sessionData.general.namespace_relation }}</div>
210- </div>
211- </div>
212- </my-card>
213- </el-col>
214- </el-row>
215- <el-row v-if="!tips" :gutter="12">
216- <el-col :span="24">
217- <my-card
218- :title="$t('session.detail.currentQuerySQL')"
219- height="200"
220- :bodyPadding="false"
221- >
222- <div class="text-in-card">
223- <div class="text-row">
224- <div class="value">{{ sessionData.general.query }}</div>
225- </div>
226- </div>
227- </my-card>
228- </el-col>
229- </el-row>
230- </el-tab-pane>
231- <el-tab-pane :label="$t('session.detail.statistic.tabTitle')" :name="tabKeys[1]">
232- <div class="line-tips center" v-if="tips">
233- <div class=""><svg-icon name="info" />{{ tips }}</div>
234- </div>
235- <el-row v-if="!tips" :gutter="12">
236- <el-col :span="12">
237- <my-card
238- :title="$t('session.detail.statistic.sessionStatusStatistics')"
239- height="600"
240- :bodyPadding="false"
241- skipBodyHeight
242- >
243- <el-table
244- :data="sessionData.statisticStatus"
245- style="width: 100%; height: 560px"
246- border
247- :header-cell-class-name="
248- () => {
249- return 'grid-header'
250- }
251- "
252- >
253- <el-table-column prop="name" :label="$t('session.detail.statistic.name')" />
254- <el-table-column
255- prop="value"
256- :label="$t('session.detail.statistic.value')"
257- />
258- </el-table>
259- </my-card>
260- </el-col>
261- <el-col :span="12">
262- <my-card
263- :title="$t('session.detail.statistic.sessionRuntimeInformation')"
264- height="600"
265- :bodyPadding="false"
266- skipBodyHeight
267- >
268- <el-table
269- :data="sessionData.statisticRuntime"
270- style="width: 100%; height: 560px"
271- border
272- :header-cell-class-name="
273- () => {
274- return 'grid-header'
275- }
276- "
277- >
278- <el-table-column prop="name" :label="$t('session.detail.statistic.name')" />
279- <el-table-column
280- prop="value"
281- :label="$t('session.detail.statistic.value')"
282- />
283- </el-table>
284- </my-card>
285- </el-col>
286- </el-row>
287- </el-tab-pane>
288- <el-tab-pane :label="$t('session.detail.blockTree.tabTitle')" :name="tabKeys[2]">
289- <div class="line-tips center" v-if="tips">
290- <div class=""><svg-icon name="info" />{{ tips }}</div>
291- </div>
292- <el-table
293- v-if="!tips"
294- :table-layout="'auto'"
295- :data="sessionData.blockTree"
296- style="width: 100%"
297- border
298- :header-cell-class-name="
299- () => {
300- return 'grid-header'
301- }
302- "
303- row-key="id"
304- default-expand-all
305- >
306- <el-table-column :label="$t('session.detail.blockTree.sessionID')" width="120">
307- <template #default="scope">
308- <el-link type="primary" @click="gotoSessionDetail(scope.row.id)">
309- {{ scope.row.id === '0' ? '' : scope.row.id }}
310- </el-link>
311- </template>
312- </el-table-column>
313- <el-table-column :label="$t('session.detail.blockTree.blockedSessionID')" width="100">
314- <template #default="scope">
315- <el-link type="primary" @click="gotoSessionDetail(scope.row.parentid)">
316- {{ scope.row.parentid === '0' ? '' : scope.row.parentid }}
317- </el-link>
318- </template>
319- </el-table-column>
320- <el-table-column
321- prop="backend_start"
322- :label="$t('session.detail.blockTree.sessionStartTime')"
323- :formatter="(r) => dateFormat(r.backend_start, 'MM-DD HH:mm:ss')"
324- width="110"
325- />
326- <el-table-column
327- prop="wait_status"
328- :label="$t('session.detail.blockTree.waitState')"
329- width="100"
330- />
331- <el-table-column
332- prop="wait_event"
333- :label="$t('session.detail.blockTree.waitEvent')"
334- width="100"
335- />
336- <el-table-column
337- prop="lockmode"
338- :label="$t('session.detail.blockTree.waitLockMode')"
339- width="120"
340- />
341- <el-table-column
342- prop="datname"
343- :label="$t('session.detail.blockTree.dbName')"
344- width="110"
345- />
346- <el-table-column
347- prop="usename"
348- :label="$t('session.detail.blockTree.userName')"
349- width="90"
350- />
351- <el-table-column
352- prop="client_addr"
353- :label="$t('session.detail.blockTree.clientIP')"
354- width="120"
355- />
356- <el-table-column
357- prop="application_name"
358- :label="$t('session.detail.blockTree.appName')"
359- />
360- </el-table>
361- </el-tab-pane>
362- <el-tab-pane :label="$t('session.detail.waitRecord.tabTitle')" :name="tabKeys[3]">
363- <div class="line-tips center" v-if="tips">
364- <div class=""><svg-icon name="info" />{{ tips }}</div>
365- </div>
366- <el-table
367- v-if="!tips"
368- :data="sessionData.waiting"
369- style="width: 100%; height: 560px"
370- border
371- :header-cell-class-name="
372- () => {
373- return 'grid-header'
374- }
375- "
376- >
377- <el-table-column
378- prop="sample_time"
379- :label="$t('session.detail.waitRecord.sampleTime')"
380- width="140"
381- />
382- <el-table-column
383- prop="wait_status"
384- :label="$t('session.detail.waitRecord.waitState')"
385- width="120"
386- />
387- <el-table-column
388- prop="event"
389- :label="$t('session.detail.waitRecord.waitEvent')"
390- width="120"
391- />
392- <el-table-column
393- prop="lockmode"
394- :label="$t('session.detail.waitRecord.waitLockMode')"
395- width="160"
396- />
397- <el-table-column prop="locktag" :label="$t('session.detail.waitRecord.lockInfo')" />
398- </el-table>
399- </el-tab-pane>
400- </el-tabs>
401- </div>
402- </el-main>
403- </el-container>
404- </div>
405</template>341</template>
406 342 
407<script setup lang="ts">343<script setup lang="ts">
@@ -424,116 +360,116 @@ const innerRefreshTime = ref<number>(30)
424const innerRefreshDoneTime = ref<string>('')360const innerRefreshDoneTime = ref<string>('')
425 361 
426interface SessionData {362interface SessionData {
427- general: SessionGeneralDetail | any363+ general: SessionGeneralDetail | any
428- blockTree: BlockTable[]364+ blockTree: BlockTable[]
429- statisticStatus: Statistic[]365+ statisticStatus: Statistic[]
430- statisticRuntime: Statistic[]366+ statisticRuntime: Statistic[]
431- waiting: WaitingRecord[]367+ waiting: WaitingRecord[]
432}368}
433const sessionDataDefault = {369const sessionDataDefault = {
434- general: {},370+ general: {},
435- blockTree: [],371+ blockTree: [],
436- statisticStatus: [],372+ statisticStatus: [],
437- statisticRuntime: [],373+ statisticRuntime: [],
438- waiting: [],374+ waiting: [],
439}375}
440const sessionData = ref<SessionData>(sessionDataDefault)376const sessionData = ref<SessionData>(sessionDataDefault)
441 377 
442// same for every page in index378// same for every page in index
443onMounted(() => {379onMounted(() => {
444- if (380+ if (
445- typeof router.currentRoute.value.params.dbid === 'string' &&381+ typeof router.currentRoute.value.params.dbid === 'string' &&
446- typeof router.currentRoute.value.params.id === 'string'382+ typeof router.currentRoute.value.params.id === 'string'
447- ) {383+ ) {
448- instanceId.value = router.currentRoute.value.params.dbid384+ instanceId.value = router.currentRoute.value.params.dbid
449- sessionId.value = router.currentRoute.value.params.id385+ sessionId.value = router.currentRoute.value.params.id
450- } else {386+ } else {
451- // @ts-ignore387+ // @ts-ignore
452- const wujie = window.$wujie388+ const wujie = window.$wujie
453- instanceId.value = wujie?.props.data.dbid389+ instanceId.value = wujie?.props.data.dbid
454- sessionId.value = wujie?.props.data.id390+ sessionId.value = wujie?.props.data.id
455- }391+ }
456- loadSessionData(instanceId.value, sessionId.value)392+ loadSessionData(instanceId.value, sessionId.value)
457- updateTimerInner()393+ updateTimerInner()
458})394})
459 395 
460// load data396// load data
461const { data: sessionResultWrapper, run: loadSessionData } = useRequest(getSessionDetail, { manual: true })397const { data: sessionResultWrapper, run: loadSessionData } = useRequest(getSessionDetail, { manual: true })
462watch(398watch(
463- sessionResultWrapper,399+ sessionResultWrapper,
464- () => {400+ () => {
465- tips.value = ''401+ tips.value = ''
466- if (sessionResultWrapper?.value?.code !== 200 && sessionResultWrapper?.value?.code !== '200') {402+ if (sessionResultWrapper?.value?.code !== 200 && sessionResultWrapper?.value?.code !== '200') {
467- tips.value = sessionResultWrapper?.value?.msg403+ tips.value = sessionResultWrapper?.value?.msg
404+ }
405+ 
406+ console.log('DEBUG: sessionResultWrapper', sessionResultWrapper)
407+ 
408+ let sessionResult = sessionResultWrapper?.value?.data
409+ console.log('DEBUG: sessionResult', sessionResult)
410+ 
411+ innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')
412+ 
413+ // clear data
414+ sessionData.value = sessionDataDefault
415+ 
416+ if (sessionResult) {
417+ sessionData.value.general = sessionResult.general
418+ sessionData.value.statisticRuntime = sessionResult.statistic.filter((item) => {
419+ if (item.type === 'RUNTIME') {
420+ return true
468 }421 }
469- 422+ return false
470- console.log('DEBUG: sessionResultWrapper', sessionResultWrapper)423+ })
471- 424+ sessionData.value.statisticStatus = sessionResult.statistic.filter((item) => {
472- let sessionResult = sessionResultWrapper?.value?.data425+ if (item.type === 'STATUS') {
473- console.log('DEBUG: sessionResult', sessionResult)426+ return true
474- 
475- innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')
476- 
477- // clear data
478- sessionData.value = sessionDataDefault
479- 
480- if (sessionResult) {
481- sessionData.value.general = sessionResult.general
482- sessionData.value.statisticRuntime = sessionResult.statistic.filter((item) => {
483- if (item.type === 'RUNTIME') {
484- return true
485- }
486- return false
487- })
488- sessionData.value.statisticStatus = sessionResult.statistic.filter((item) => {
489- if (item.type === 'STATUS') {
490- return true
491- }
492- return false
493- })
494- 
495- // block sessions
496- if (sessionResult.blockTree) {
497- sessionData.value.blockTree = sessionResult.blockTree
498- }
499- 
500- // waiting
501- for (let index = 0; index < sessionResult.waiting.length; index++) {
502- const element = sessionResult.waiting[index]
503- element.sample_time = utcTimeFormat(element.sample_time, 'YYYY-MM-DD HH:mm:ss')
504- }
505- sessionData.value.waiting = sessionResult.waiting
506 }427 }
507- },428+ return false
508- { deep: true }429+ })
430+ 
431+ // block sessions
432+ if (sessionResult.blockTree) {
433+ sessionData.value.blockTree = sessionResult.blockTree
434+ }
435+ 
436+ // waiting
437+ for (let index = 0; index < sessionResult.waiting.length; index++) {
438+ const element = sessionResult.waiting[index]
439+ element.sample_time = utcTimeFormat(element.sample_time, 'YYYY-MM-DD HH:mm:ss')
440+ }
441+ sessionData.value.waiting = sessionResult.waiting
442+ }
443+ },
444+ { deep: true }
509)445)
510const tips = ref<string | undefined>()446const tips = ref<string | undefined>()
511const timerInner = ref<number>()447const timerInner = ref<number>()
512const updateTimerInner = () => {448const updateTimerInner = () => {
513- clearInterval(timerInner.value)449+ clearInterval(timerInner.value)
514- const timeInner = innerRefreshTime.value450+ const timeInner = innerRefreshTime.value
515- timerInner.value = useIntervalTime(451+ timerInner.value = useIntervalTime(
516- () => {452+ () => {
517- loadSessionData(instanceId.value, sessionId.value)453+ loadSessionData(instanceId.value, sessionId.value)
518- },454+ },
519- computed(() => timeInner * 1000)455+ computed(() => timeInner * 1000)
520- )456+ )
521}457}
522const gotoSessionDetail = (id: string) => {458const gotoSessionDetail = (id: string) => {
523- const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')459+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
524- if (curMode === 'wujie') {460+ if (curMode === 'wujie') {
525- // @ts-ignore plug-in components461+ // @ts-ignore plug-in components
526- window.$wujie?.props.methods.jump({462+ window.$wujie?.props.methods.jump({
527- name: `Static-pluginObservability-instanceVemSessionDetail`,463+ name: `Static-pluginObservability-instanceVemSessionDetail`,
528- query: {464+ query: {
529- dbid: instanceId.value,465+ dbid: instanceId.value,
530- id,466+ id,
531- },467+ },
532- })468+ })
533- } else {469+ } else {
534- // local470+ // local
535- window.sessionStorage.setItem('sqlId', id)471+ window.sessionStorage.setItem('sqlId', id)
536- router.push(`/vem/sessionDetail/${instanceId.value}/${id}`)472+ router.push(`/vem/sessionDetail/${instanceId.value}/${id}`)
537- }473+ }
538}474}
539</script>475</script>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/topSQL/Index.vue+126-114
@@ -1,44 +1,54 @@
1<template>1<template>
2- <IndexBar :tabId="props.tabId"></IndexBar>2+ <IndexBar :tabId="props.tabId"></IndexBar>
3- <div style="margin-bottom: 0px"></div>3+ <div style="margin-bottom: 0px"></div>
4- <div class="top-sql">4+ <div class="top-sql">
5- <el-tabs v-model="typeTab" class="tab2">5+ <el-tabs v-model="typeTab" class="tab2">
6- <el-tab-pane label="DB_TIME" name="db_time" />6+ <el-tab-pane label="DB_TIME" name="db_time" />
7- <el-tab-pane label="CPU_TIME" name="cpu_time" />7+ <el-tab-pane label="CPU_TIME" name="cpu_time" />
8- <el-tab-pane label="EXEC_TIME" name="execution_time" />8+ <el-tab-pane label="EXEC_TIME" name="execution_time" />
9- </el-tabs>9+ <el-tab-pane label="IO_TIME" name="data_io_time" />
10- <div class="top-sql-table" v-if="!errorInfo" v-loading="loading">10+ </el-tabs>
11- <el-table :data="data.tableData" border>11+ <div class="top-sql-table" v-if="!errorInfo" v-loading="loading">
12- <el-table-column label="SQLID" width="150">12+ <el-table :data="data.tableData" border>
13- <template #default="scope">13+ <el-table-column label="SQLID" width="150">
14- <el-link type="primary" @click="gotoTopsqlDetail(scope.row.debug_query_id)">14+ <template #default="scope">
15- {{ scope.row.debug_query_id }}15+ <el-link type="primary" @click="gotoTopsqlDetail(scope.row.debug_query_id)">
16- </el-link>16+ {{ scope.row.debug_query_id }}
17- </template>17+ </el-link>
18- </el-table-column>18+ </template>
19- <el-table-column :label="$t('sql.dbName')" prop="db_name" width="90"></el-table-column>19+ </el-table-column>
20- <el-table-column :label="$t('sql.schemaName')" prop="schema_name" width="140"></el-table-column>20+ <el-table-column :label="$t('sql.dbName')" prop="db_name" width="90" show-overflow-tooltip></el-table-column>
21- <el-table-column :label="$t('sql.userName')" prop="user_name" width="90"></el-table-column>21+ <el-table-column :label="$t('sql.schemaName')" prop="schema_name" width="100" show-overflow-tooltip>
22- <el-table-column :label="$t('sql.applicationName')" prop="application_name"></el-table-column>22+ </el-table-column>
23- <el-table-column23+ <el-table-column
24- :label="$t('sql.startTime')"24+ :label="$t('sql.userName')"
25- :formatter="(r: any) => moment(r.start_time).format('YYYY-MM-DD HH:mm:ss')"25+ prop="user_name"
26- width="140"26+ width="80"
27- ></el-table-column>27+ show-overflow-tooltip
28- />28+ ></el-table-column>
29- <el-table-column29+ <el-table-column :label="$t('sql.applicationName')" prop="application_name" width="100" show-overflow-tooltip>
30- :label="$t('sql.finishTime')"30+ </el-table-column>
31- :formatter="(r: any) => moment(r.finish_time).format('YYYY-MM-DD HH:mm:ss')"31+ <el-table-column prop="query" label="SQL" show-overflow-tooltip />
32- width="140"32+ <el-table-column
33- ></el-table-column>33+ :label="$t('sql.startTime')"
34- />34+ :formatter="(r: any) => moment(r.start_time).format('YYYY-MM-DD HH:mm:ss')"
35- <el-table-column :label="$t('sql.dbTime')" prop="db_time" width="110"></el-table-column>35+ width="140"
36- <el-table-column :label="$t('sql.cpuTime')" prop="cpu_time" width="115"></el-table-column>36+ >
37- <el-table-column :label="$t('sql.excutionTime')" prop="execution_time" width="120"></el-table-column>37+ </el-table-column>
38- </el-table>38+ <el-table-column
39- </div>39+ :label="$t('sql.finishTime')"
40- <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />40+ :formatter="(r: any) => moment(r.finish_time).format('YYYY-MM-DD HH:mm:ss')"
41+ width="140"
42+ >
43+ </el-table-column>
44+ <el-table-column :label="$t('sql.dbTime')" prop="db_time" width="80"></el-table-column>
45+ <el-table-column :label="$t('sql.cpuTime')" prop="cpu_time" width="90"></el-table-column>
46+ <el-table-column :label="$t('sql.excutionTime')" prop="execution_time" width="90"></el-table-column>
47+ <el-table-column label="IO_TIME(ms)" prop="data_io_time" width="80"></el-table-column>
48+ </el-table>
41 </div>49 </div>
50+ <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
51+ </div>
42</template>52</template>
43 53 
44<script setup lang="ts">54<script setup lang="ts">
@@ -64,111 +74,113 @@ const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId } = store
64const { culRangeTimeAndStep } = monitorStore74const { culRangeTimeAndStep } = monitorStore
65 75 
66const data = reactive<{76const data = reactive<{
67- tableData: Array<Record<string, string>>77+ tableData: Array<Record<string, string>>
68}>({78}>({
69- tableData: [],79+ tableData: [],
70})80})
71 81 
72const getParam = () => {82const getParam = () => {
73- return {83+ return {
74- dbid: instanceId,84+ dbid: instanceId,
75- startTime: dateFormat(new Date(culRangeTimeAndStep()[0] * 1000)),85+ startTime: dateFormat(new Date(culRangeTimeAndStep()[0] * 1000)),
76- finishTime: dateFormat(new Date(culRangeTimeAndStep()[1] * 1000)),86+ finishTime: dateFormat(new Date(culRangeTimeAndStep()[1] * 1000)),
77- }87+ }
78}88}
79 89 
80const outsideGoto = (key: string, param: any) => {90const outsideGoto = (key: string, param: any) => {
81- if (param && param.key === tabKeys.InstanceMonitorTOPSQLCPUTime) typeTab.value = 'cpu_time'91+ if (param && param.key === tabKeys.InstanceMonitorTOPSQLCPUTime) typeTab.value = 'cpu_time'
92+ if (param && param.key === tabKeys.InstanceMonitorTOPSQLIOTime) typeTab.value = 'data_io_time'
82}93}
83defineExpose({ outsideGoto })94defineExpose({ outsideGoto })
84 95 
85// same for every page in index96// same for every page in index
86const timer = ref<number>()97const timer = ref<number>()
87onMounted(() => {98onMounted(() => {
88- load()99+ load()
89})100})
90watch(101watch(
91- updateCounter,102+ updateCounter,
92- () => {103+ () => {
93- clearInterval(timer.value)104+ clearInterval(timer.value)
94- if (tabNow.value === tabKeys.InstanceMonitorTOPSQL) {105+ if (tabNow.value === tabKeys.InstanceMonitorTOPSQL) {
95- if (updateCounter.value.source === sourceType.value.INSTANCE) load()106+ if (updateCounter.value.source === sourceType.value.INSTANCE) load()
96- if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()107+ if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()
97- if (updateCounter.value.source === sourceType.value.TIMETYPE) load()108+ if (updateCounter.value.source === sourceType.value.TIMETYPE) load()
98- if (updateCounter.value.source === sourceType.value.TIMERANGE) load()109+ if (updateCounter.value.source === sourceType.value.TIMERANGE) load()
99- if (updateCounter.value.source === sourceType.value.TABCHANGE) load()110+ if (updateCounter.value.source === sourceType.value.TABCHANGE) load()
100- const time = autoRefreshTime.value111+ const time = autoRefreshTime.value
101- timer.value = useIntervalTime(112+ timer.value = useIntervalTime(
102- () => {113+ () => {
103- load()114+ load()
104- },115+ },
105- computed(() => time * 1000)116+ computed(() => time * 1000)
106- )117+ )
107- }118+ }
108- },119+ },
109- { immediate: false }120+ { immediate: false }
110)121)
111 122 
112watch(typeTab, () => {123watch(typeTab, () => {
113- load()124+ load()
114})125})
115const load = () => {126const load = () => {
116- requestData(getParam())127+ data.tableData = []
128+ requestData(getParam())
117}129}
118const { run: requestData, loading } = useRequest(130const { run: requestData, loading } = useRequest(
119- (query) => {131+ (query) => {
120- const res = new Promise((resolve, reject) => {132+ const res = new Promise((resolve, reject) => {
121- const result = ogRequest.getNative(133+ const result = ogRequest.getNative(
122- `/observability/v1/topsql/list?id=${query.dbid}&startTime=${query.startTime}&finishTime=${query.finishTime}&orderField=${typeTab.value}`134+ `/observability/v1/topsql/list?id=${query.dbid}&startTime=${query.startTime}&finishTime=${query.finishTime}&orderField=${typeTab.value}`
123- )135+ )
124- result ? resolve(result) : reject(result)136+ result ? resolve(result) : reject(result)
125- })137+ })
126- .then((r: any) => {138+ .then((r: any) => {
127- const code = r?.data.code139+ const code = r?.data.code
128- const list = r?.data.data140+ const list = r?.data.data
129- if (code === 602) {141+ if (code === 602) {
130- errorInfo.value = t('dashboard.topsqlListTip')142+ errorInfo.value = t('dashboard.topsqlListTip')
131- } else if (code === 200 && Array.isArray(list)) {143+ } else if (code === 200 && Array.isArray(list)) {
132- errorInfo.value = ''144+ errorInfo.value = ''
133- data.tableData = list145+ data.tableData = list
134- }146+ }
135- })147+ })
136- .catch((e) => {148+ .catch((e) => {
137- errorInfo.value = e149+ errorInfo.value = e
138- })150+ })
139- return res151+ return res
140- },152+ },
141- { manual: true }153+ { manual: true }
142)154)
143 155 
144const gotoTopsqlDetail = (id: string) => {156const gotoTopsqlDetail = (id: string) => {
145- const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')157+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
146- if (curMode === 'wujie') {158+ if (curMode === 'wujie') {
147- // @ts-ignore plug-in components159+ // @ts-ignore plug-in components
148- window.$wujie?.props.methods.jump({160+ window.$wujie?.props.methods.jump({
149- name: `Static-pluginObservability-instanceVemSql_detail`,161+ name: `Static-pluginObservability-instanceVemSql_detail`,
150- query: {162+ query: {
151- dbid: getParam().dbid.value,163+ dbid: getParam().dbid.value,
152- id,164+ id,
153- },165+ },
154- })166+ })
155- } else {167+ } else {
156- // local168+ // local
157- window.sessionStorage.setItem('sqlId', id)169+ window.sessionStorage.setItem('sqlId', id)
158- router.push(`/vem/sql_detail/${getParam().dbid.value}/${id}`)170+ router.push(`/vem/sql_detail/${getParam().dbid.value}/${id}`)
159- }171+ }
160}172}
161</script>173</script>
162 174 
163<style scoped lang="scss">175<style scoped lang="scss">
164.top-sql {176.top-sql {
165- &:deep(.el-tabs__header) {177+ &:deep(.el-tabs__header) {
166- width: 100%;178+ width: 100%;
167- }179+ }
168 180 
169- &-table-id {181+ &-table-id {
170- color: #0093ff;182+ color: #0093ff;
171- cursor: pointer;183+ cursor: pointer;
172- }184+ }
173}185}
174</style>186</style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/performanceLoad/Index.vue+920-724
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/CPU/Index.vue+411-266
@@ -1,132 +1,159 @@
1<template>1<template>
2- <el-row :gutter="12">2+ <el-row :gutter="12">
3- <el-col :span="12">3+ <el-col :span="12">
4- <my-card :title="$t('resourceMonitor.cpu.cpuUse')" height="300" :bodyPadding="false">4+ <my-card :title="$t('resourceMonitor.cpu.cpuUse')" height="300" :bodyPadding="false">
5- <template #headerExtend>5+ <template #headerExtend>
6- <div class="card-links">6+ <div class="card-links">
7- <el-link7+ <el-link
8- v-if="isManualRangeSelected"8+ v-if="isManualRangeSelected"
9- type="primary"9+ type="primary"
10- @click="goto(tabKeys.InstanceMonitorTOPSQL, { key: tabKeys.InstanceMonitorTOPSQLCPUTime })"10+ @click="goto(tabKeys.InstanceMonitorTOPSQL, { key: tabKeys.InstanceMonitorTOPSQLCPUTime })"
11- >11+ >
12- TOP CPU SQL12+ TOP CPU SQL
13- </el-link>13+ </el-link>
14- <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">14+ <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">
15- {{ $t('app.diagnosis') }}15+ {{ $t('app.diagnosis') }}
16- </el-link>16+ </el-link>
17- </div>17+ <el-link v-if="isManualRangeSelected" type="primary" @click="wdr(tabId)" v-loading="wdrLoading">
18- </template>18+ {{$t('instanceIndex.wdrAnalysis')}}
19- <LazyLine19+ </el-link>
20- :tips="$t('instanceIndex.activeSessionQtyTips')"20+ </div>
21- :rangeSelect="true"21+ </template>
22- :tabId="props.tabId"22+ <LazyLine
23- :formatter="toFixed"23+ :tips="$t('instanceIndex.activeSessionQtyTips')"
24- :data="metricsData.cpu"24+ :rangeSelect="true"
25- :xData="metricsData.time"25+ :tabId="props.tabId"
26- :max="100"26+ :formatter="toFixed"
27- :min="0"27+ :data="metricsData.cpu"
28- :interval="25"28+ :xData="metricsData.time"
29- :unit="'%'"29+ :max="100"
30- />30+ :min="0"
31- </my-card>31+ :interval="25"
32- </el-col>32+ :unit="'%'"
33- <el-col :span="12">33+ />
34- <my-card :title="$t('resourceMonitor.cpu.cpuLoad')" height="300" :bodyPadding="false">34+ </my-card>
35- <LazyLine35+ </el-col>
36- :tabId="props.tabId"36+ <el-col :span="12">
37- :formatter="toFixed"37+ <my-card :title="$t('resourceMonitor.cpu.cpuLoad')" height="300" :bodyPadding="false">
38- :data="metricsData.cpuPayload"38+ <LazyLine :tabId="props.tabId" :formatter="toFixed" :data="metricsData.cpuPayload" :xData="metricsData.time" />
39- :xData="metricsData.time"39+ </my-card>
40- />40+ </el-col>
41- </my-card>41+ </el-row>
42- </el-col>
43- </el-row>
44 42 
45- <div style="margin-bottom: 12px"></div>43+ <div style="margin-bottom: 12px"></div>
46- <div style="position: relative">44+ <div style="position: relative">
47- <div45+ <div
48- style="46+ style="
49- position: absolute;47+ position: absolute;
50- right: 0px;48+ right: 0px;
51- top: 2px;49+ top: 2px;
52- z-index: 1;50+ z-index: 1;
53- display: flex;51+ display: flex;
54- flex-direction: row;52+ flex-direction: row;
55- align-items: center;53+ align-items: center;
56- font-size: 12px;54+ font-size: 12px;
57- "55+ "
58- >56+ >
59- <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>57+ <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>
60- <div>{{ $t('app.autoRefreshFor') }}</div>58+ <div>{{ $t('app.autoRefreshFor') }}</div>
61- <el-select v-model="innerRefreshTime" style="width: 60px; margin: 0 4px" @change="updateTimerInner">59+ <el-select v-model="innerRefreshTime" style="width: 100px; margin: 0 4px" @change="updateTimerInner">
62- <el-option :value="1" label="1s" />60+ <el-option :value="99999999" label="NO-AUTO" />
63- <el-option :value="15" label="15s" />61+ <el-option :value="1" label="1s" />
64- <el-option :value="30" label="30s" />62+ <el-option :value="15" label="15s" />
65- <el-option :value="60" label="60s" />63+ <el-option :value="30" label="30s" />
66- </el-select>64+ <el-option :value="60" label="60s" />
67- <el-button65+ </el-select>
68- class="refresh-button"66+ <el-button
69- type="primary"67+ class="refresh-button"
70- :icon="Refresh"68+ type="primary"
71- style="margin-left: 8px"69+ :icon="Refresh"
72- @click="loadTOPCPUProcessNow(props.tabId)"70+ style="margin-left: 8px"
73- />71+ @click="loadTOPCPUProcessNow(props.tabId)"
74- </div>72+ />
75- <el-tabs v-model="tab" class="tab2">
76- <el-tab-pane :label="$t('resourceMonitor.cpu.topProcess')" :name="0">
77- <el-table
78- :table-layout="'auto'"
79- :data="topCPUProcessNowData == null ? [] : topCPUProcessNowData"
80- style="width: 100%"
81- :border="true"
82- :header-cell-class-name="
83- () => {
84- return 'grid-header'
85- }
86- "
87- >
88- <el-table-column prop="%CPU" label="%CPU" width="60" />
89- <el-table-column prop="%MEM" label="%MEM" width="60" />
90- <el-table-column prop="COMMAND" label="COMMAND" />
91- <el-table-column prop="NI" label="NI" width="40" />
92- <el-table-column prop="PID" label="PID" width="90" />
93- <el-table-column prop="PR" label="PR" width="40" />
94- <el-table-column prop="RES" label="RES" width="80" />
95- <el-table-column prop="S" label="S" width="40" />
96- <el-table-column prop="SHR" label="SHR" width="80" />
97- <el-table-column prop="TIME+" label="TIME+" width="100" />
98- <el-table-column prop="USER" label="USER" width="120" />
99- <el-table-column prop="VIRT" label="VIRT" width="120" />
100- </el-table>
101- </el-tab-pane>
102- <el-tab-pane :label="$t('resourceMonitor.cpu.topThread')" :name="1">
103- <el-table
104- :table-layout="'auto'"
105- :data="topCPUDBThreadNowData == null ? [] : topCPUDBThreadNowData"
106- style="width: 100%"
107- :border="true"
108- :header-cell-class-name="
109- () => {
110- return 'grid-header'
111- }
112- "
113- >
114- <el-table-column prop="%CPU" label="%CPU" width="60" />
115- <el-table-column prop="%MEM" label="%MEM" width="60" />
116- <el-table-column prop="COMMAND" label="COMMAND" />
117- <el-table-column prop="NI" label="NI" width="40" />
118- <el-table-column prop="PID" label="PID" width="90" />
119- <el-table-column prop="PR" label="PR" width="40" />
120- <el-table-column prop="RES" label="RES" width="80" />
121- <el-table-column prop="S" label="S" width="40" />
122- <el-table-column prop="SHR" label="SHR" width="80" />
123- <el-table-column prop="TIME+" label="TIME+" width="100" />
124- <el-table-column prop="USER" label="USER" width="120" />
125- <el-table-column prop="VIRT" label="VIRT" width="120" />
126- </el-table>
127- </el-tab-pane>
128- </el-tabs>
129 </div>73 </div>
74+ <el-tabs v-model="tab" class="tab2">
75+ <el-tab-pane :label="$t('resourceMonitor.cpu.topProcess')" :name="0">
76+ <el-table
77+ :table-layout="'auto'"
78+ :data="topCPUProcessNowData == null ? [] : topCPUProcessNowData"
79+ style="width: 100%"
80+ :border="true"
81+ :header-cell-class-name="
82+ () => {
83+ return 'grid-header'
84+ }
85+ "
86+ :row-class-name="rowClassName"
87+ >
88+ <el-table-column prop="%CPU" label="%CPU" width="60" />
89+ <el-table-column prop="%MEM" label="%MEM" width="60" />
90+ <el-table-column label="COMMAND" width="260" show-overflow-tooltip>
91+ <template #default="scope">
92+ <el-link
93+ v-if="scope.row.port && node.dbPort != scope.row.port"
94+ type="primary"
95+ class="top-sql-table-id"
96+ @click="changeCluster(scope.row)"
97+ >
98+ {{ scope.row.COMMAND }}
99+ </el-link>
100+ <span v-else>{{ scope.row.COMMAND }}</span>
101+ </template>
102+ </el-table-column>
103+ <el-table-column prop="FullCommand" label="FULL COMMAND" show-overflow-tooltip />
104+ <el-table-column prop="NI" label="NI" width="40" />
105+ <el-table-column prop="PID" label="PID" width="90" />
106+ <el-table-column prop="PR" label="PR" width="40" />
107+ <el-table-column prop="RES" label="RES" width="80" />
108+ <el-table-column prop="S" label="S" width="40" />
109+ <el-table-column prop="SHR" label="SHR" width="80" />
110+ <el-table-column prop="TIME+" label="TIME+" width="100" />
111+ <el-table-column prop="USER" label="USER" width="120" />
112+ <el-table-column prop="VIRT" label="VIRT" width="120" />
113+ </el-table>
114+ </el-tab-pane>
115+ <el-tab-pane :label="$t('resourceMonitor.cpu.topThread')" :name="1">
116+ <el-table
117+ :table-layout="'auto'"
118+ :data="topCPUDBThreadNowData == null ? [] : topCPUDBThreadNowData"
119+ style="width: 100%"
120+ :border="true"
121+ :header-cell-class-name="
122+ () => {
123+ return 'grid-header'
124+ }
125+ "
126+ >
127+ <el-table-column prop="%CPU" label="%CPU" width="60" />
128+ <el-table-column prop="%MEM" label="%MEM" width="60" />
129+ <el-table-column prop="COMMAND" label="COMMAND" show-overflow-tooltip />
130+ <el-table-column prop="NI" label="NI" width="40" />
131+ <el-table-column prop="PID" label="PID" width="90" />
132+ <el-table-column prop="PR" label="PR" width="40" />
133+ <el-table-column prop="RES" label="RES" width="80" />
134+ <el-table-column prop="S" label="S" width="40" />
135+ <el-table-column prop="SHR" label="SHR" width="80" />
136+ <el-table-column prop="TIME+" label="TIME+" width="100" />
137+ <el-table-column prop="USER" label="USER" width="120" />
138+ <el-table-column prop="VIRT" label="VIRT" width="120" />
139+ <el-table-column :label="$t('session.trans.sessionID')" width="130">
140+ <template #default="scope">
141+ <el-link type="primary" class="top-sql-table-id" @click="gotoSessionDetail(scope.row.sessionid)">
142+ {{ scope.row.sessionid }}
143+ </el-link>
144+ </template>
145+ </el-table-column>
146+ <el-table-column label="SQLID" width="150">
147+ <template #default="scope">
148+ <el-link type="primary" @click="gotoTopsqlDetail(scope.row.query_id)">
149+ {{ scope.row.query_id }}
150+ </el-link>
151+ </template>
152+ </el-table-column>
153+ </el-table>
154+ </el-tab-pane>
155+ </el-tabs>
156+ </div>
130</template>157</template>
131 158 
132<script setup lang="ts">159<script setup lang="ts">
@@ -143,6 +170,8 @@ import { Refresh } from '@element-plus/icons-vue'
143import moment from 'moment'170import moment from 'moment'
144import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'171import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'
145import { ElMessage } from 'element-plus'172import { ElMessage } from 'element-plus'
173+import router from '@/router'
174+import { getWDRSnapshot } from '@/api/wdr'
146 175 
147const { t } = useI18n()176const { t } = useI18n()
148 177 
@@ -150,184 +179,300 @@ const props = withDefaults(defineProps<{ tabId: string }>(), {})
150const tab = 0179const tab = 0
151 180 
152interface LineData {181interface LineData {
153- name: string182+ name: string
154- data: any[]183+ data: any[]
155- [other: string]: any184+ [other: string]: any
156}185}
157interface MetricsData {186interface MetricsData {
158- cpu: LineData[]187+ cpu: LineData[]
159- cpuPayload: LineData[]188+ cpuPayload: LineData[]
160- time: string[]189+ time: string[]
161}190}
162const metricsData = ref<MetricsData>({191const metricsData = ref<MetricsData>({
163- cpu: [],192+ cpu: [],
164- cpuPayload: [],193+ cpuPayload: [],
165- time: [],194+ time: [],
166})195})
167const topCPUProcessNowData = ref<void | TopCPUProcessNow[]>([])196const topCPUProcessNowData = ref<void | TopCPUProcessNow[]>([])
168const topCPUDBThreadNowData = ref<void | TopCPUProcessNow[]>([])197const topCPUDBThreadNowData = ref<void | TopCPUProcessNow[]>([])
169const innerRefreshTime = ref<number>(30)198const innerRefreshTime = ref<number>(30)
170const innerRefreshDoneTime = ref<string>('')199const innerRefreshDoneTime = ref<string>('')
171-const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange } =200+const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange, node } =
172- storeToRefs(useMonitorStore(props.tabId))201+ storeToRefs(useMonitorStore(props.tabId))
173 202 
174// same for every page in index203// same for every page in index
175const timer = ref<number>()204const timer = ref<number>()
176onMounted(() => {205onMounted(() => {
177- load()206+ load()
178- loadTOPCPUProcessNow(props.tabId)207+ loadTOPCPUProcessNow(props.tabId)
179})208})
180watch(209watch(
181- updateCounter,210+ updateCounter,
182- () => {211+ () => {
183- clearInterval(timer.value)212+ clearInterval(timer.value)
184- if (tabNow.value === tabKeys.ResourceMonitorCPU) {213+ if (tabNow.value === tabKeys.ResourceMonitorCPU) {
185- if (updateCounter.value.source === sourceType.value.INSTANCE) {214+ if (updateCounter.value.source === sourceType.value.INSTANCE) {
186- load()215+ load()
187- loadTOPCPUProcessNow(props.tabId)216+ loadTOPCPUProcessNow(props.tabId)
188- }217+ }
189- if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()218+ if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()
190- if (updateCounter.value.source === sourceType.value.TIMETYPE) load()219+ if (updateCounter.value.source === sourceType.value.TIMETYPE) load()
191- if (updateCounter.value.source === sourceType.value.TIMERANGE) load()220+ if (updateCounter.value.source === sourceType.value.TIMERANGE) load()
192- if (updateCounter.value.source === sourceType.value.TABCHANGE) load()221+ if (updateCounter.value.source === sourceType.value.TABCHANGE) load()
193- const time = autoRefreshTime.value222+ const time = autoRefreshTime.value
194- timer.value = useIntervalTime(223+ timer.value = useIntervalTime(
195- () => {224+ () => {
196- load()225+ load()
197- },226+ },
198- computed(() => time * 1000)227+ computed(() => time * 1000)
199- )228+ )
200- updateTimerInner()229+ updateTimerInner()
201- }230+ }
202- },231+ },
203- { immediate: false }232+ { immediate: false }
204)233)
205 234 
206// load data235// load data
207const load = (checkTab?: boolean, checkRange?: boolean) => {236const load = (checkTab?: boolean, checkRange?: boolean) => {
208- if (!instanceId.value) return237+ if (!instanceId.value) return
209- requestData(props.tabId)238+ requestData(props.tabId)
239+}
240+ 
241+const rowClassName = ({ row }: { row: any }) => {
242+ if (row.port) {
243+ return 'highlight-row'
244+ }
245+ return ''
210}246}
211const { data: indexData, run: requestData } = useRequest(getCPUMetrics, { manual: true })247const { data: indexData, run: requestData } = useRequest(getCPUMetrics, { manual: true })
212watch(248watch(
213- indexData,249+ indexData,
214- () => {250+ () => {
215- // clear data251+ // clear data
216- metricsData.value.cpu = []252+ metricsData.value.cpu = []
217- metricsData.value.cpuPayload = []253+ metricsData.value.cpuPayload = []
218 254 
219- const baseData = indexData.value255+ const baseData = indexData.value
220- if (!baseData) return256+ if (!baseData) return
221 257 
222- {258+ {
223- let tempData: string[] = []259+ let tempData: string[] = []
224- baseData.CPU_TOTAL.forEach((d: number) => {260+ baseData.CPU_DB.forEach((d: number) => {
225- tempData.push(toFixed(d))261+ tempData.push(toFixed(d))
226- })262+ })
227- metricsData.value.cpu.push({ data: tempData, name: 'Total' })263+ metricsData.value.cpu.push({ data: tempData, name: t('resourceMonitor.cpu.dbThread') })
228- }264+ }
229- {265+ {
230- let tempData: string[] = []266+ let tempData: string[] = []
231- baseData.CPU_IOWAIT.forEach((d: number) => {267+ baseData.CPU_TOTAL.forEach((d: number) => {
232- tempData.push(toFixed(d))268+ tempData.push(toFixed(d))
233- })269+ })
234- metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'IOWait' })270+ metricsData.value.cpu.push({ data: tempData, name: 'Total' })
235- }271+ }
236- {272+ {
237- let tempData: string[] = []273+ let tempData: string[] = []
238- baseData.CPU_SYSTEM.forEach((d: number) => {274+ baseData.CPU_USER.forEach((d: number) => {
239- tempData.push(toFixed(d))275+ tempData.push(toFixed(d))
240- })276+ })
241- metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'System' })277+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'User' })
242- }278+ }
243- {279+ {
244- let tempData: string[] = []280+ let tempData: string[] = []
245- baseData.CPU_USER.forEach((d: number) => {281+ baseData.CPU_SYSTEM.forEach((d: number) => {
246- tempData.push(toFixed(d))282+ tempData.push(toFixed(d))
247- })283+ })
248- metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'User' })284+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'System' })
249- }285+ }
250- {286+ {
251- let tempData: string[] = []287+ let tempData: string[] = []
252- baseData.CPU_DB.forEach((d: number) => {288+ baseData.CPU_IOWAIT.forEach((d: number) => {
253- tempData.push(toFixed(d))289+ tempData.push(toFixed(d))
254- })290+ })
255- metricsData.value.cpu.push({ data: tempData, name: t('resourceMonitor.cpu.dbThread') })291+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'IOWait' })
256- }292+ }
257- {293+ {
258- let tempData: string[] = []294+ let tempData: string[] = []
259- baseData.CPU_TOTAL_5M_LOAD.forEach((d: number) => {295+ baseData.CPU_NICE.forEach((d: number) => {
260- tempData.push(toFixed(d))296+ tempData.push(toFixed(d))
261- })297+ })
262- metricsData.value.cpuPayload.push({ data: tempData, name: t('resourceMonitor.cpu.total5mLoad') })298+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Nice' })
263- }299+ }
264- {300+ {
265- let tempData: string[] = []301+ let tempData: string[] = []
266- baseData.CPU_TOTAL_CORE_NUM.forEach((d: number) => {302+ baseData.CPU_IRQ.forEach((d: number) => {
267- tempData.push(toFixed(d))303+ tempData.push(toFixed(d))
268- })304+ })
269- metricsData.value.cpuPayload.push({ data: tempData, name: t('resourceMonitor.cpu.coreNum') })305+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'IRQ' })
270- }306+ }
307+ {
308+ let tempData: string[] = []
309+ baseData.CPU_SOFTIRQ.forEach((d: number) => {
310+ tempData.push(toFixed(d))
311+ })
312+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Soft IRQ' })
313+ }
314+ {
315+ let tempData: string[] = []
316+ baseData.CPU_STEAL.forEach((d: number) => {
317+ tempData.push(toFixed(d))
318+ })
319+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Steal' })
320+ }
321+ {
322+ let tempData: string[] = []
323+ baseData.CPU_IDLE.forEach((d: number) => {
324+ tempData.push(toFixed(d))
325+ })
326+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Idle' })
327+ }
328+ {
329+ let tempData: string[] = []
330+ baseData.CPU_TOTAL_5M_LOAD.forEach((d: number) => {
331+ tempData.push(toFixed(d))
332+ })
333+ metricsData.value.cpuPayload.push({ data: tempData, name: t('resourceMonitor.cpu.total5mLoad') })
334+ }
335+ {
336+ let tempData: string[] = []
337+ baseData.CPU_TOTAL_CORE_NUM.forEach((d: number) => {
338+ tempData.push(toFixed(d))
339+ })
340+ metricsData.value.cpuPayload.push({ data: tempData, name: t('resourceMonitor.cpu.coreNum') })
341+ }
271 342 
272- metricsData.value.cpu = metricsData.value.cpu.sort((a, b) => {343+ metricsData.value.cpu = metricsData.value.cpu.sort((a, b) => {
273- const sorts = ['CPU_TOTAL', 'CPU_USER', 'CPU_SYSTEM', 'CPU_IOWAIT']344+ const sorts = ['CPU_TOTAL', 'CPU_USER', 'CPU_SYSTEM', 'CPU_IOWAIT']
274- return sorts.indexOf(a.name) - sorts.indexOf(b.name)345+ return sorts.indexOf(a.name) - sorts.indexOf(b.name)
275- })346+ })
276- metricsData.value.cpuPayload = metricsData.value.cpuPayload.sort((a, b) => {347+ metricsData.value.cpuPayload = metricsData.value.cpuPayload.sort((a, b) => {
277- const sorts = ['CPU_TOTAL_5M_LOAD', 'CPU_TOTAL_CORE_NUM', 'CPU_TOTAL_AVERAGE_UTILIZATION']348+ const sorts = ['CPU_TOTAL_5M_LOAD', 'CPU_TOTAL_CORE_NUM', 'CPU_TOTAL_AVERAGE_UTILIZATION']
278- return sorts.indexOf(a.name) - sorts.indexOf(b.name)349+ return sorts.indexOf(a.name) - sorts.indexOf(b.name)
279- })350+ })
280 351 
281- // time352+ // time
282- metricsData.value.time = baseData.time353+ metricsData.value.time = baseData.time
283- },354+ },
284- { deep: true }355+ { deep: true }
285)356)
286const { data: topCPUProcessNowResult, run: loadTOPCPUProcessNow } = useRequest(getTOPCPUProcessNow, { manual: true })357const { data: topCPUProcessNowResult, run: loadTOPCPUProcessNow } = useRequest(getTOPCPUProcessNow, { manual: true })
287watch(358watch(
288- topCPUProcessNowResult,359+ topCPUProcessNowResult,
289- () => {360+ () => {
290- topCPUProcessNowData.value = topCPUProcessNowResult.value ? topCPUProcessNowResult.value[0] : []361+ topCPUProcessNowData.value = topCPUProcessNowResult.value ? topCPUProcessNowResult.value[0] : []
291- topCPUDBThreadNowData.value = topCPUProcessNowResult.value ? topCPUProcessNowResult.value[1] : []362+ topCPUDBThreadNowData.value = topCPUProcessNowResult.value ? topCPUProcessNowResult.value[1] : []
292- innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')363+ topCPUProcessNowData.value.forEach((item) => {
293- },364+ if (item.port) {
294- { deep: true }365+ item.COMMAND += '(' + node.value.publicIp + ':' + item.port
366+ if (node.value.dbPort.toString() === item.port.toString()) {
367+ item.COMMAND += t('instanceMonitor.thisInstance')
368+ }
369+ item.COMMAND += ')'
370+ item.publicIp = node.value.publicIp
371+ }
372+ })
373+ innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')
374+ },
375+ { deep: true }
295)376)
296const timerInner = ref<number>()377const timerInner = ref<number>()
297const updateTimerInner = () => {378const updateTimerInner = () => {
298- clearInterval(timerInner.value)379+ clearInterval(timerInner.value)
299- const timeInner = innerRefreshTime.value380+ const timeInner = innerRefreshTime.value
300- timerInner.value = useIntervalTime(381+ timerInner.value = useIntervalTime(
301- () => {382+ () => {
302- loadTOPCPUProcessNow(props.tabId)383+ loadTOPCPUProcessNow(props.tabId)
303- },384+ },
304- computed(() => timeInner * 1000)385+ computed(() => timeInner * 1000)
305- )386+ )
387+}
388+const emit = defineEmits(['goto', 'changeCluster'])
389+const changeCluster = (row: TopCPUProcessNow) => {
390+ emit('changeCluster', row.publicIp, row.port)
306}391}
307-const emit = defineEmits(['goto'])
308const goto = (key: string, param: object) => {392const goto = (key: string, param: object) => {
309- emit('goto', key, param)393+ emit('goto', key, param)
310}394}
311const gotoSQLDiagnosis = () => {395const gotoSQLDiagnosis = () => {
312- hasSQLDiagnosisModule()396+ hasSQLDiagnosisModule()
313- .then(() => {397+ .then(() => {
314- const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')398+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
315- if (curMode === 'wujie') {399+ if (curMode === 'wujie') {
316- // @ts-ignore plug-in components400+ // @ts-ignore plug-in components
317- window.$wujie?.props.methods.jump({401+ window.$wujie?.props.methods.jump({
318- name: `Static-pluginObservability-sql-diagnosisVemHistoryDiagnosis`,402+ name: `Static-pluginObservability-sql-diagnosisVemHistoryDiagnosis`,
319- query: {403+ query: {
320- instanceId: instanceId.value,404+ instanceId: instanceId.value,
321- startTime: timeRange.value[0],405+ startTime: timeRange.value[0],
322- endTime: timeRange.value[1],406+ endTime: timeRange.value[1],
323- },407+ },
324- })
325- } else ElMessage.error(t('app.needSQLDiagnosis'))
326- })
327- .catch(() => {
328- ElMessage.error(t('app.needSQLDiagnosis'))
329 })408 })
409+ } else ElMessage.error(t('app.needSQLDiagnosis'))
410+ })
411+ .catch(() => {
412+ ElMessage.error(t('app.needSQLDiagnosis'))
413+ })
330}414}
415+const gotoSessionDetail = (id: string) => {
416+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
417+ if (curMode === 'wujie') {
418+ // @ts-ignore plug-in components
419+ window.$wujie?.props.methods.jump({
420+ name: `Static-pluginObservability-instanceVemSessionDetail`,
421+ query: {
422+ dbid: instanceId.value,
423+ id,
424+ },
425+ })
426+ } else {
427+ // local
428+ window.sessionStorage.setItem('sqlId', id)
429+ router.push(`/vem/sessionDetail/${instanceId.value}/${id}`)
430+ }
431+}
432+const gotoTopsqlDetail = (id: string) => {
433+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
434+ if (curMode === 'wujie') {
435+ // @ts-ignore plug-in components
436+ window.$wujie?.props.methods.jump({
437+ name: `Static-pluginObservability-instanceVemSql_detail`,
438+ query: {
439+ dbid: instanceId.value,
440+ id,
441+ },
442+ })
443+ } else {
444+ // local
445+ window.sessionStorage.setItem('sqlId', id)
446+ router.push(`/vem/sql_detail/${instanceId.value}/${id}`)
447+ }
448+}
449+ 
450+const { data: wdrData, run: wdr, loading: wdrLoading } = useRequest(getWDRSnapshot, { manual: true })
451+watch(
452+ wdrData,
453+ (res: any) => {
454+ // goto wdr
455+ if (res && res.wdrId && res.wdrId.length > 0) {
456+ const { timeRange } = useMonitorStore(props.tabId)
457+ let param = {
458+ operation: 'search',
459+ startTime: timeRange == null ? '' : moment(timeRange[0]).format("YYYY-MM-DD HH:mm:ss"),
460+ endTime: timeRange == null ? '' : moment(timeRange[1]).format("YYYY-MM-DD HH:mm:ss"),
461+ }
462+ emit('goto', tabKeys.WDR, param)
463+ } else if (res && res.start && res.end) {
464+ let param = {
465+ operation: 'edit',
466+ startId: res.start,
467+ endId: res.end
468+ }
469+ emit('goto', tabKeys.WDR, param)
470+ } else {
471+ ElMessage.error(t('wdrReports.wdrErrtip'))
472+ }
473+ },
474+ { deep: true }
475+)
331</script>476</script>
332 477 
333<style scoped lang="scss"></style>478<style scoped lang="scss"></style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/IO/Index.vue+383-325
@@ -1,97 +1,122 @@
1<template>1<template>
2- <my-card :title="$t('resourceMonitor.io.deviceIO')" :bodyPadding="false" skipBodyHeight>2+ <my-card :title="$t('resourceMonitor.io.deviceIO')" :bodyPadding="false" skipBodyHeight>
3- <el-table3+ <el-table
4- :data="metricsData.table"4+ :data="metricsData.table"
5- style="width: 100%"5+ style="width: 100%"
6- border6+ border
7- :header-cell-class-name="7+ :header-cell-class-name="
8- () => {8+ () => {
9- return 'grid-header'9+ return 'grid-header'
10- }10+ }
11- "11+ "
12- @selection-change="handleSelectionChange"12+ @selection-change="handleSelectionChange"
13- >13+ >
14- <el-table-column type="selection" width="50" align="center" />14+ <el-table-column type="selection" width="50" align="center" />
15- <el-table-column prop="device" label="Device" />15+ <el-table-column prop="device" label="Device" />
16- <el-table-column prop="IO_TPS" label="TPS" />16+ <el-table-column prop="IO_TPS" label="TPS" />
17- <el-table-column prop="IO_RD" label="rd(KB)/s" />17+ <el-table-column prop="IO_RD" label="rd(KB)/s" />
18- <el-table-column prop="IO_WT" label="wt(KB)/s" />18+ <el-table-column prop="IO_WT" label="wt(KB)/s" />
19- <el-table-column prop="IO_AVGRQ_SZ" label="avgrq-sz (KB)" />19+ <el-table-column prop="IO_AVGRQ_SZ" label="avgrq-sz (KB)" />
20- <el-table-column prop="IO_AVGQU_SZ" label="avgqu-sz" />20+ <el-table-column prop="IO_AVGQU_SZ" label="avgqu-sz" />
21- <el-table-column prop="IO_AWAIT" label="await(ms)" />21+ <el-table-column prop="IO_AWAIT" label="await(ms)" />
22- <el-table-column prop="IO_UTIL" label="%util" />22+ <el-table-column prop="IO_UTIL" label="%util" />
23- </el-table>23+ </el-table>
24- </my-card>24+ </my-card>
25 25 
26- <div class="gap-row"></div>26+ <div class="gap-row"></div>
27 27 
28- <el-row :gutter="12">28+ <el-row :gutter="12">
29- <el-col :span="12">29+ <el-col :span="12">
30- <my-card :title="'IOPS'" height="300" :bodyPadding="false">30+ <my-card :title="'IOPS'" height="300" :bodyPadding="false">
31- <template #headerExtend>31+ <template #headerExtend>
32- <div class="card-links">32+ <div class="card-links">
33- <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">33+ <el-link
34- {{ $t('app.diagnosis') }}34+ v-if="isManualRangeSelected"
35- </el-link>35+ type="primary"
36- </div>36+ @click="goto(tabKeys.InstanceMonitorTOPSQL, { key: tabKeys.InstanceMonitorTOPSQLIOTime })"
37- </template>37+ >
38- <LazyLine38+ TOP CPU SQL
39- :tips="$t('instanceIndex.activeSessionQtyTips')"39+ </el-link>
40- :rangeSelect="true"40+ <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">
41- :tabId="props.tabId"41+ {{ $t('app.diagnosis') }}
42- :formatter="toFixed"42+ </el-link>
43- :data="deviceIOPS"43+ <el-link v-if="isManualRangeSelected" type="primary" @click="wdr(tabId)" v-loading="wdrLoading">
44- :xData="metricsData.time"44+ {{$t('instanceIndex.wdrAnalysis')}}
45- />45+ </el-link>
46- </my-card>46+ </div>
47- </el-col>47+ </template>
48- <el-col :span="12">48+ <LazyLine
49- <my-card :title="$t('resourceMonitor.io.rwSecond')" height="300" :bodyPadding="false">49+ :tips="$t('instanceIndex.activeSessionQtyTips')"
50- <LazyLine50+ :rangeSelect="true"
51- :tabId="props.tabId"51+ :tabId="props.tabId"
52- :formatter="toFixed"52+ :formatter="toFixed"
53- :data="deviceRW"53+ :data="deviceIOPS"
54- :xData="metricsData.time"54+ :xData="metricsData.time"
55- :unit="'B'"55+ :tool-tips-sort="'desc'"
56- />56+ :tool-tips-exclude-zero="true"
57- </my-card>57+ />
58- </el-col>58+ </my-card>
59- </el-row>59+ </el-col>
60+ <el-col :span="12">
61+ <my-card :title="$t('resourceMonitor.io.rwSecond')" height="300" :bodyPadding="false">
62+ <LazyLine
63+ :tabId="props.tabId"
64+ :formatter="toFixed"
65+ :data="deviceRW"
66+ :xData="metricsData.time"
67+ :unit="'B'"
68+ :tool-tips-sort="'desc'"
69+ :tool-tips-exclude-zero="true"
70+ />
71+ </my-card>
72+ </el-col>
73+ </el-row>
60 74 
61- <div class="gap-row"></div>75+ <div class="gap-row"></div>
62 76 
63- <el-row :gutter="12">77+ <el-row :gutter="12">
64- <el-col :span="8">78+ <el-col :span="8">
65- <my-card :title="$t('resourceMonitor.io.queueLength')" height="300" :bodyPadding="false">79+ <my-card :title="$t('resourceMonitor.io.queueLength')" height="300" :bodyPadding="false">
66- <LazyLine :tabId="props.tabId" :formatter="toFixed" :data="deviceIOQueue" :xData="metricsData.time" />80+ <LazyLine
67- </my-card>81+ :tabId="props.tabId"
68- </el-col>82+ :formatter="toFixed"
69- <el-col :span="8">83+ :data="deviceIOQueue"
70- <my-card :title="$t('resourceMonitor.io.ioUsage')" height="300" :bodyPadding="false">84+ :xData="metricsData.time"
71- <LazyLine85+ :tool-tips-sort="'desc'"
72- :tabId="props.tabId"86+ :tool-tips-exclude-zero="true"
73- :formatter="toFixed"87+ />
74- :data="deviceIOUsage"88+ </my-card>
75- :xData="metricsData.time"89+ </el-col>
76- :max="100"90+ <el-col :span="8">
77- :min="0"91+ <my-card :title="$t('resourceMonitor.io.ioUsage')" height="300" :bodyPadding="false">
78- :interval="25"92+ <LazyLine
79- :unit="'%'"93+ :tabId="props.tabId"
80- />94+ :formatter="toFixed"
81- </my-card>95+ :data="deviceIOUsage"
82- </el-col>96+ :xData="metricsData.time"
83- <el-col :span="8">97+ :max="100"
84- <my-card :title="$t('resourceMonitor.io.ioTime')" height="300" :bodyPadding="false">98+ :min="0"
85- <LazyLine99+ :interval="25"
86- :tabId="props.tabId"100+ :unit="'%'"
87- :formatter="toFixed"101+ :tool-tips-sort="'desc'"
88- :data="deviceIOTime"102+ :tool-tips-exclude-zero="true"
89- :xData="metricsData.time"103+ />
90- :unit="'ms'"104+ </my-card>
91- />105+ </el-col>
92- </my-card>106+ <el-col :span="8">
93- </el-col>107+ <my-card :title="$t('resourceMonitor.io.ioTime')" height="300" :bodyPadding="false">
94- </el-row>108+ <LazyLine
109+ :tabId="props.tabId"
110+ :formatter="toFixed"
111+ :data="deviceIOTime"
112+ :xData="metricsData.time"
113+ :unit="'ms'"
114+ :tool-tips-sort="'desc'"
115+ :tool-tips-exclude-zero="true"
116+ />
117+ </my-card>
118+ </el-col>
119+ </el-row>
95</template>120</template>
96 121 
97<script setup lang="ts">122<script setup lang="ts">
@@ -106,297 +131,330 @@ import { useRequest } from 'vue-request'
106import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'131import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'
107import { useI18n } from 'vue-i18n'132import { useI18n } from 'vue-i18n'
108import { ElMessage } from 'element-plus'133import { ElMessage } from 'element-plus'
134+import { getWDRSnapshot } from '@/api/wdr'
135+import moment from 'moment'
109 136 
110const props = withDefaults(defineProps<{ tabId: string }>(), {})137const props = withDefaults(defineProps<{ tabId: string }>(), {})
111const { t } = useI18n()138const { t } = useI18n()
112 139 
113interface LineData {140interface LineData {
114- name: string141+ name: string
115- data: any[]142+ data: any[]
116- [other: string]: any143+ [other: string]: any
117}144}
118interface MetricsData {145interface MetricsData {
119- table: any[]146+ table: any[]
120- iops: LineData[]147+ iops: LineData[]
121- rw: LineData[]148+ rw: LineData[]
122- queueLenth: LineData[]149+ queueLenth: LineData[]
123- ioUse: LineData[]150+ ioUse: LineData[]
124- ioTime: LineData[]151+ ioTime: LineData[]
125- time: string[]152+ time: string[]
126}153}
127const metricsData = ref<MetricsData>({154const metricsData = ref<MetricsData>({
128- table: [],155+ table: [],
129- iops: [],156+ iops: [],
130- rw: [],157+ rw: [],
131- queueLenth: [],158+ queueLenth: [],
132- ioUse: [],159+ ioUse: [],
133- ioTime: [],160+ ioTime: [],
134- time: [],161+ time: [],
135})162})
136const multipleSelection = ref<string[]>([])163const multipleSelection = ref<string[]>([])
137const deviceIOPS = computed(() => {164const deviceIOPS = computed(() => {
138- let baseData = metricsData.value.iops165+ let baseData = metricsData.value.iops
139- if (multipleSelection.value.length <= 0) {166+ if (multipleSelection.value.length <= 0) {
140- return baseData167+ return baseData
141- } else {168+ } else {
142- let result = []169+ let result = []
143- let selectedKeys = []170+ let selectedKeys = []
144- for (let index = 0; index < multipleSelection.value.length; index++) {171+ for (let index = 0; index < multipleSelection.value.length; index++) {
145- const element: any = multipleSelection.value[index]172+ const element: any = multipleSelection.value[index]
146- selectedKeys.push(element.device)173+ selectedKeys.push(element.device)
147- }
148- for (let index = 0; index < baseData.length; index++) {
149- const element: any = baseData[index]
150- if (selectedKeys.indexOf(element.key) >= 0) {
151- result.push(element)
152- }
153- }
154- return result
155 }174 }
175+ for (let index = 0; index < baseData.length; index++) {
176+ const element: any = baseData[index]
177+ if (selectedKeys.indexOf(element.key) >= 0) {
178+ result.push(element)
179+ }
180+ }
181+ return result
182+ }
156})183})
184+const emit = defineEmits(['goto'])
185+const goto = (key: string, param: object) => {
186+ emit('goto', key, param)
187+}
157const deviceRW = computed(() => {188const deviceRW = computed(() => {
158- let baseData = metricsData.value.rw189+ let baseData = metricsData.value.rw
159- if (multipleSelection.value.length <= 0) {190+ if (multipleSelection.value.length <= 0) {
160- return baseData191+ return baseData
161- } else {192+ } else {
162- let result = []193+ let result = []
163- let selectedKeys = []194+ let selectedKeys = []
164- for (let index = 0; index < multipleSelection.value.length; index++) {195+ for (let index = 0; index < multipleSelection.value.length; index++) {
165- const element: any = multipleSelection.value[index]196+ const element: any = multipleSelection.value[index]
166- selectedKeys.push(element.device)197+ selectedKeys.push(element.device)
167- }
168- for (let index = 0; index < baseData.length; index++) {
169- const element: any = baseData[index]
170- if (selectedKeys.indexOf(element.key) >= 0) {
171- result.push(element)
172- }
173- }
174- return result
175 }198 }
199+ for (let index = 0; index < baseData.length; index++) {
200+ const element: any = baseData[index]
201+ if (selectedKeys.indexOf(element.key) >= 0) {
202+ result.push(element)
203+ }
204+ }
205+ return result
206+ }
176})207})
177const deviceIOUsage = computed(() => {208const deviceIOUsage = computed(() => {
178- let baseData = metricsData.value.ioUse209+ let baseData = metricsData.value.ioUse
179- if (multipleSelection.value.length <= 0) {210+ if (multipleSelection.value.length <= 0) {
180- return baseData211+ return baseData
181- } else {212+ } else {
182- let result = []213+ let result = []
183- let selectedKeys = []214+ let selectedKeys = []
184- for (let index = 0; index < multipleSelection.value.length; index++) {215+ for (let index = 0; index < multipleSelection.value.length; index++) {
185- const element: any = multipleSelection.value[index]216+ const element: any = multipleSelection.value[index]
186- selectedKeys.push(element.device)217+ selectedKeys.push(element.device)
187- }
188- for (let index = 0; index < baseData.length; index++) {
189- const element: any = baseData[index]
190- if (selectedKeys.indexOf(element.key) >= 0) {
191- result.push(element)
192- }
193- }
194- return result
195 }218 }
219+ for (let index = 0; index < baseData.length; index++) {
220+ const element: any = baseData[index]
221+ if (selectedKeys.indexOf(element.key) >= 0) {
222+ result.push(element)
223+ }
224+ }
225+ return result
226+ }
196})227})
197const deviceIOQueue = computed(() => {228const deviceIOQueue = computed(() => {
198- let baseData = metricsData.value.queueLenth229+ let baseData = metricsData.value.queueLenth
199- if (multipleSelection.value.length <= 0) {230+ if (multipleSelection.value.length <= 0) {
200- return baseData231+ return baseData
201- } else {232+ } else {
202- let result = []233+ let result = []
203- let selectedKeys = []234+ let selectedKeys = []
204- for (let index = 0; index < multipleSelection.value.length; index++) {235+ for (let index = 0; index < multipleSelection.value.length; index++) {
205- const element: any = multipleSelection.value[index]236+ const element: any = multipleSelection.value[index]
206- selectedKeys.push(element.device)237+ selectedKeys.push(element.device)
207- }
208- for (let index = 0; index < baseData.length; index++) {
209- const element: any = baseData[index]
210- if (selectedKeys.indexOf(element.key) >= 0) {
211- result.push(element)
212- }
213- }
214- return result
215 }238 }
239+ for (let index = 0; index < baseData.length; index++) {
240+ const element: any = baseData[index]
241+ if (selectedKeys.indexOf(element.key) >= 0) {
242+ result.push(element)
243+ }
244+ }
245+ return result
246+ }
216})247})
217const deviceIOTime = computed(() => {248const deviceIOTime = computed(() => {
218- let baseData = metricsData.value.ioTime249+ let baseData = metricsData.value.ioTime
219- if (multipleSelection.value.length <= 0) {250+ if (multipleSelection.value.length <= 0) {
220- return baseData251+ return baseData
221- } else {252+ } else {
222- let result = []253+ let result = []
223- let selectedKeys = []254+ let selectedKeys = []
224- for (let index = 0; index < multipleSelection.value.length; index++) {255+ for (let index = 0; index < multipleSelection.value.length; index++) {
225- const element: any = multipleSelection.value[index]256+ const element: any = multipleSelection.value[index]
226- selectedKeys.push(element.device)257+ selectedKeys.push(element.device)
227- }
228- for (let index = 0; index < baseData.length; index++) {
229- const element: any = baseData[index]
230- if (selectedKeys.indexOf(element.key) >= 0) {
231- result.push(element)
232- }
233- }
234- return result
235 }258 }
259+ for (let index = 0; index < baseData.length; index++) {
260+ const element: any = baseData[index]
261+ if (selectedKeys.indexOf(element.key) >= 0) {
262+ result.push(element)
263+ }
264+ }
265+ return result
266+ }
236})267})
237 268 
238const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange } =269const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange } =
239- storeToRefs(useMonitorStore(props.tabId))270+ storeToRefs(useMonitorStore(props.tabId))
240 271 
241// same for every page in index272// same for every page in index
242const timer = ref<number>()273const timer = ref<number>()
243onMounted(() => {274onMounted(() => {
244- load()275+ load()
245})276})
246watch(277watch(
247- updateCounter,278+ updateCounter,
248- () => {279+ () => {
249- clearInterval(timer.value)280+ clearInterval(timer.value)
250- if (tabNow.value === tabKeys.ResourceMonitorIO) {281+ if (tabNow.value === tabKeys.ResourceMonitorIO) {
251- if (updateCounter.value.source === sourceType.value.INSTANCE) load()282+ if (updateCounter.value.source === sourceType.value.INSTANCE) load()
252- if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()283+ if (updateCounter.value.source === sourceType.value.MANUALREFRESH) load()
253- if (updateCounter.value.source === sourceType.value.TIMETYPE) load()284+ if (updateCounter.value.source === sourceType.value.TIMETYPE) load()
254- if (updateCounter.value.source === sourceType.value.TIMERANGE) load()285+ if (updateCounter.value.source === sourceType.value.TIMERANGE) load()
255- if (updateCounter.value.source === sourceType.value.TABCHANGE) load()286+ if (updateCounter.value.source === sourceType.value.TABCHANGE) load()
256- const time = autoRefreshTime.value287+ const time = autoRefreshTime.value
257- timer.value = useIntervalTime(288+ timer.value = useIntervalTime(
258- () => {289+ () => {
259- load()290+ load()
260- },291+ },
261- computed(() => time * 1000)292+ computed(() => time * 1000)
262- )293+ )
263- }294+ }
264- },295+ },
265- { immediate: false }296+ { immediate: false }
266)297)
267 298 
268// load data299// load data
269const load = (checkTab?: boolean, checkRange?: boolean) => {300const load = (checkTab?: boolean, checkRange?: boolean) => {
270- if (!instanceId.value) return301+ if (!instanceId.value) return
271- requestData(props.tabId)302+ requestData(props.tabId)
272}303}
273const { data: indexData, run: requestData } = useRequest(getIOMetrics, { manual: true })304const { data: indexData, run: requestData } = useRequest(getIOMetrics, { manual: true })
274watch(305watch(
275- indexData,306+ indexData,
276- () => {307+ () => {
277- // clear data308+ // clear data
278- metricsData.value.iops = []309+ metricsData.value.iops = []
279- metricsData.value.rw = []310+ metricsData.value.rw = []
280- metricsData.value.queueLenth = []311+ metricsData.value.queueLenth = []
281- metricsData.value.ioUse = []312+ metricsData.value.ioUse = []
282- metricsData.value.ioTime = []313+ metricsData.value.ioTime = []
283 314 
284- const baseData = indexData.value315+ const baseData = indexData.value
285- if (!baseData) return316+ if (!baseData) return
286 317 
287- // table318+ // table
288- for (let index = 0; index < baseData.table.length; index++) {319+ for (let index = 0; index < baseData.table.length; index++) {
289- const element = baseData.table[index]320+ const element = baseData.table[index]
290- element.IO_TPS = toFixed(element.IO_TPS)321+ element.IO_TPS = toFixed(element.IO_TPS)
291- element.IO_RD = toFixed(element.IO_RD)322+ element.IO_RD = toFixed(element.IO_RD)
292- element.IO_WT = toFixed(element.IO_WT)323+ element.IO_WT = toFixed(element.IO_WT)
293- element.IO_AVGRQ_SZ = toFixed(element.IO_AVGRQ_SZ)324+ element.IO_AVGRQ_SZ = toFixed(element.IO_AVGRQ_SZ)
294- element.IO_AVGQU_SZ = toFixed(element.IO_AVGQU_SZ)325+ element.IO_AVGQU_SZ = toFixed(element.IO_AVGQU_SZ)
295- element.IO_AWAIT = toFixed(element.IO_AWAIT)326+ element.IO_AWAIT = toFixed(element.IO_AWAIT)
296- element.IO_UTIL = toFixed(element.IO_UTIL)327+ element.IO_UTIL = toFixed(element.IO_UTIL)
297- }328+ }
298- metricsData.value.table = baseData.table329+ metricsData.value.table = baseData.table
299 330 
300- // IOPS331+ // IOPS
301- for (let key in baseData.IOPS_R) {332+ for (let key in baseData.IOPS_R) {
302- let tempData: string[] = []333+ let tempData: string[] = []
303- baseData.IOPS_R[key].forEach((element) => {334+ baseData.IOPS_R[key].forEach((element) => {
304- tempData.push(toFixed(element))335+ tempData.push(toFixed(element))
305- })336+ })
306- metricsData.value.iops.push({ data: tempData, name: key + '(读)', key })337+ metricsData.value.iops.push({ data: tempData, name: key + '(读)', key })
307- }338+ }
308- for (let key in baseData.IOPS_W) {339+ for (let key in baseData.IOPS_W) {
309- let tempData: string[] = []340+ let tempData: string[] = []
310- baseData.IOPS_W[key].forEach((element) => {341+ baseData.IOPS_W[key].forEach((element) => {
311- tempData.push(toFixed(element))342+ tempData.push(toFixed(element))
312- })343+ })
313- metricsData.value.iops.push({ data: tempData, name: key + '(写)', key })344+ metricsData.value.iops.push({ data: tempData, name: key + '(写)', key })
314- }345+ }
315 346 
316- // rw byte347+ // rw byte
317- for (let key in baseData.IO_DISK_READ_BYTES_PER_SECOND) {348+ for (let key in baseData.IO_DISK_READ_BYTES_PER_SECOND) {
318- let tempData: string[] = []349+ let tempData: string[] = []
319- baseData.IO_DISK_READ_BYTES_PER_SECOND[key].forEach((element) => {350+ baseData.IO_DISK_READ_BYTES_PER_SECOND[key].forEach((element) => {
320- tempData.push(toFixed(element))351+ tempData.push(toFixed(element))
321- })352+ })
322- metricsData.value.rw.push({ data: tempData, name: key + '(读)', key })353+ metricsData.value.rw.push({ data: tempData, name: key + '(读)', key })
323- }354+ }
324- for (let key in baseData.IO_DISK_WRITE_BYTES_PER_SECOND) {355+ for (let key in baseData.IO_DISK_WRITE_BYTES_PER_SECOND) {
325- let tempData: string[] = []356+ let tempData: string[] = []
326- baseData.IO_DISK_WRITE_BYTES_PER_SECOND[key].forEach((element) => {357+ baseData.IO_DISK_WRITE_BYTES_PER_SECOND[key].forEach((element) => {
327- tempData.push(toFixed(element))358+ tempData.push(toFixed(element))
328- })359+ })
329- metricsData.value.rw.push({ data: tempData, name: key + '(写)', key })360+ metricsData.value.rw.push({ data: tempData, name: key + '(写)', key })
330- }361+ }
331 362 
332- // queue363+ // queue
333- for (let key in baseData.IO_QUEUE_LENGTH) {364+ for (let key in baseData.IO_QUEUE_LENGTH) {
334- let tempData: string[] = []365+ let tempData: string[] = []
335- baseData.IO_QUEUE_LENGTH[key].forEach((element) => {366+ baseData.IO_QUEUE_LENGTH[key].forEach((element) => {
336- tempData.push(toFixed(element))367+ tempData.push(toFixed(element))
337- })368+ })
338- metricsData.value.queueLenth.push({ data: tempData, name: key, key })369+ metricsData.value.queueLenth.push({ data: tempData, name: key, key })
339- }370+ }
340 371 
341- // io use372+ // io use
342- for (let key in baseData.IO_UTIL) {373+ for (let key in baseData.IO_UTIL) {
343- let tempData: string[] = []374+ let tempData: string[] = []
344- baseData.IO_UTIL[key].forEach((element) => {375+ baseData.IO_UTIL[key].forEach((element) => {
345- tempData.push(toFixed(element))376+ tempData.push(toFixed(element))
346- })377+ })
347- metricsData.value.ioUse.push({ data: tempData, name: key, key })378+ metricsData.value.ioUse.push({ data: tempData, name: key, key })
348- }379+ }
349 380 
350- // io time381+ // io time
351- for (let key in baseData.IO_AVG_REPONSE_TIME_READ) {382+ for (let key in baseData.IO_AVG_REPONSE_TIME_READ) {
352- let tempData: string[] = []383+ let tempData: string[] = []
353- baseData.IO_AVG_REPONSE_TIME_READ[key].forEach((element) => {384+ baseData.IO_AVG_REPONSE_TIME_READ[key].forEach((element) => {
354- tempData.push(toFixed(element))385+ tempData.push(toFixed(element))
355- })386+ })
356- metricsData.value.ioTime.push({ data: tempData, name: key + '(读)', key })387+ metricsData.value.ioTime.push({ data: tempData, name: key + '(读)', key })
357- }388+ }
358- for (let key in baseData.IO_AVG_REPONSE_TIME_WRITE) {389+ for (let key in baseData.IO_AVG_REPONSE_TIME_WRITE) {
359- let tempData: string[] = []390+ let tempData: string[] = []
360- baseData.IO_AVG_REPONSE_TIME_WRITE[key].forEach((element) => {391+ baseData.IO_AVG_REPONSE_TIME_WRITE[key].forEach((element) => {
361- tempData.push(toFixed(element))392+ tempData.push(toFixed(element))
362- })393+ })
363- metricsData.value.ioTime.push({ data: tempData, name: key + '(写)', key })394+ metricsData.value.ioTime.push({ data: tempData, name: key + '(写)', key })
364- }395+ }
365- for (let key in baseData.IO_AVG_REPONSE_TIME_RW) {396+ for (let key in baseData.IO_AVG_REPONSE_TIME_RW) {
366- let tempData: string[] = []397+ let tempData: string[] = []
367- baseData.IO_AVG_REPONSE_TIME_RW[key].forEach((element) => {398+ baseData.IO_AVG_REPONSE_TIME_RW[key].forEach((element) => {
368- tempData.push(toFixed(element))399+ tempData.push(toFixed(element))
369- })400+ })
370- metricsData.value.ioTime.push({ data: tempData, name: key + '(读+写)', key })401+ metricsData.value.ioTime.push({ data: tempData, name: key + '(读+写)', key })
371- }402+ }
372 403 
373- metricsData.value.time = baseData.time404+ metricsData.value.time = baseData.time
374- },405+ },
375- { deep: true }406+ { deep: true }
376)407)
377const handleSelectionChange = (val: any) => {408const handleSelectionChange = (val: any) => {
378- multipleSelection.value = val409+ multipleSelection.value = val
379}410}
380const gotoSQLDiagnosis = () => {411const gotoSQLDiagnosis = () => {
381- hasSQLDiagnosisModule()412+ hasSQLDiagnosisModule()
382- .then(() => {413+ .then(() => {
383- const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')414+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
384- if (curMode === 'wujie') {415+ if (curMode === 'wujie') {
385- // @ts-ignore plug-in components416+ // @ts-ignore plug-in components
386- window.$wujie?.props.methods.jump({417+ window.$wujie?.props.methods.jump({
387- name: `Static-pluginObservability-sql-diagnosisVemHistoryDiagnosis`,418+ name: `Static-pluginObservability-sql-diagnosisVemHistoryDiagnosis`,
388- query: {419+ query: {
389- instanceId: instanceId.value,420+ instanceId: instanceId.value,
390- startTime: timeRange.value[0],421+ startTime: timeRange.value[0],
391- endTime: timeRange.value[1],422+ endTime: timeRange.value[1],
392- },423+ },
393- })
394- } else ElMessage.error(t('app.needSQLDiagnosis'))
395- })
396- .catch(() => {
397- ElMessage.error(t('app.needSQLDiagnosis'))
398 })424 })
425+ } else ElMessage.error(t('app.needSQLDiagnosis'))
426+ })
427+ .catch(() => {
428+ ElMessage.error(t('app.needSQLDiagnosis'))
429+ })
399}430}
431+ 
432+const { data: wdrData, run: wdr, loading: wdrLoading } = useRequest(getWDRSnapshot, { manual: true })
433+watch(
434+ wdrData,
435+ (res: any) => {
436+ // goto wdr
437+ if (res && res.wdrId && res.wdrId.length > 0) {
438+ const { timeRange } = useMonitorStore(props.tabId)
439+ let param = {
440+ operation: 'search',
441+ startTime: timeRange == null ? '' : moment(timeRange[0]).format("YYYY-MM-DD HH:mm:ss"),
442+ endTime: timeRange == null ? '' : moment(timeRange[1]).format("YYYY-MM-DD HH:mm:ss"),
443+ }
444+ emit('goto', tabKeys.WDR, param)
445+ } else if (res && res.start && res.end) {
446+ let param = {
447+ operation: 'edit',
448+ startId: res.start,
449+ endId: res.end
450+ }
451+ emit('goto', tabKeys.WDR, param)
452+ } else {
453+ ElMessage.error(t('wdrReports.wdrErrtip'))
454+ }
455+ },
456+ { deep: true }
457+)
400</script>458</script>
401 459 
402<style scoped lang="scss"></style>460<style scoped lang="scss"></style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/Index.vue+8-2
@@ -15,11 +15,13 @@
15 </el-tab-pane>15 </el-tab-pane>
16 <el-tab-pane :label="$t('resourceMonitor.memoryTab')" :name="tabKeys.ResourceMonitorMemory">16 <el-tab-pane :label="$t('resourceMonitor.memoryTab')" :name="tabKeys.ResourceMonitorMemory">
17 <Memory17 <Memory
18+ @changeCluster="toChangeCluster"
18 :tabId="props.tabId"19 :tabId="props.tabId"
19 v-if="20 v-if="
20 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorMemory) >= 0 ||21 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorMemory) >= 0 ||
21 resourceMonitorTabIndex === tabKeys.ResourceMonitorMemory22 resourceMonitorTabIndex === tabKeys.ResourceMonitorMemory
22 "23 "
24+ @goto="goto"
23 >25 >
24 </Memory>26 </Memory>
25 </el-tab-pane>27 </el-tab-pane>
@@ -30,6 +32,7 @@
30 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorIO) >= 0 ||32 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorIO) >= 0 ||
31 resourceMonitorTabIndex === tabKeys.ResourceMonitorIO33 resourceMonitorTabIndex === tabKeys.ResourceMonitorIO
32 "34 "
35+ @goto="goto"
33 >36 >
34 </IO>37 </IO>
35 </el-tab-pane>38 </el-tab-pane>
@@ -40,6 +43,7 @@
40 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorNetwork) >= 0 ||43 tabKeyLoaded.indexOf(tabKeys.ResourceMonitorNetwork) >= 0 ||
41 resourceMonitorTabIndex === tabKeys.ResourceMonitorNetwork44 resourceMonitorTabIndex === tabKeys.ResourceMonitorNetwork
42 "45 "
46+ @goto="goto"
43 >47 >
44 </Network>48 </Network>
45 </el-tab-pane>49 </el-tab-pane>
@@ -79,11 +83,13 @@ onMounted(() => {
79watch(resourceMonitorTabIndex, (v) => {83watch(resourceMonitorTabIndex, (v) => {
80 setNowTab()84 setNowTab()
81})85})
82-const emit = defineEmits(['goto'])86+const emit = defineEmits(['goto', 'changeCluster'])
83const goto = (key: string, param: object) => {87const goto = (key: string, param: object) => {
84- console.log('DEBUG: resourceMonitor param', param)
85 emit('goto', key, param)88 emit('goto', key, param)
86}89}
90+const toChangeCluster = (publicIp: string, port: string) => {
91+ emit('changeCluster', publicIp, port)
92+}
87 93 
88// same for every page in index94// same for every page in index
89watch(95watch(
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/memory/Index.vue+124-6
@@ -7,6 +7,9 @@
7 <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">7 <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">
8 {{ $t('app.diagnosis') }}8 {{ $t('app.diagnosis') }}
9 </el-link>9 </el-link>
10+ <el-link v-if="isManualRangeSelected" type="primary" @click="wdr(tabId)" v-loading="wdrLoading">
11+ {{$t('instanceIndex.wdrAnalysis')}}
12+ </el-link>
10 </div>13 </div>
11 </template>14 </template>
12 <div style="height: 257px">15 <div style="height: 257px">
@@ -38,6 +41,7 @@
38 <el-table-column prop="MEM_USED" :label="$t('resourceMonitor.memory.usedMemory')" />41 <el-table-column prop="MEM_USED" :label="$t('resourceMonitor.memory.usedMemory')" />
39 <el-table-column prop="MEM_FREE" :label="$t('resourceMonitor.memory.freeMemory')" />42 <el-table-column prop="MEM_FREE" :label="$t('resourceMonitor.memory.freeMemory')" />
40 <el-table-column prop="MEM_CACHE" :label="$t('resourceMonitor.memory.cachedMemory')" />43 <el-table-column prop="MEM_CACHE" :label="$t('resourceMonitor.memory.cachedMemory')" />
44+ <el-table-column prop="MEMORY_DB_USED_CURR" :label="$t('resourceMonitor.memory.dbMemory')" />
41 </el-table>45 </el-table>
42 </div>46 </div>
43 </my-card>47 </my-card>
@@ -92,7 +96,8 @@
92 >96 >
93 <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>97 <div style="margin-right: 12px">{{ $t('app.refreshOn') }} {{ innerRefreshDoneTime }}</div>
94 <div>{{ $t('app.autoRefreshFor') }}</div>98 <div>{{ $t('app.autoRefreshFor') }}</div>
95- <el-select v-model="innerRefreshTime" style="width: 60px; margin: 0 4px" @change="updateTimerInner">99+ <el-select v-model="innerRefreshTime" style="width: 100px; margin: 0 4px" @change="updateTimerInner">
100+ <el-option :value="99999999" label="NO-AUTO" />
96 <el-option :value="1" label="1s" />101 <el-option :value="1" label="1s" />
97 <el-option :value="15" label="15s" />102 <el-option :value="15" label="15s" />
98 <el-option :value="30" label="30s" />103 <el-option :value="30" label="30s" />
@@ -118,10 +123,24 @@
118 return 'grid-header'123 return 'grid-header'
119 }124 }
120 "125 "
126+ :row-class-name="rowClassName"
121 >127 >
128+ <el-table-column prop="%MEM" label="%MEM" width="70" />
122 <el-table-column prop="%CPU" label="%CPU" width="60" />129 <el-table-column prop="%CPU" label="%CPU" width="60" />
123- <el-table-column prop="%MEM" label="%MEM" width="60" />130+ <el-table-column label="COMMAND" width="260" show-overflow-tooltip>
124- <el-table-column prop="COMMAND" label="COMMAND" />131+ <template #default="scope">
132+ <el-link
133+ v-if="scope.row.port && node.dbPort != scope.row.port"
134+ type="primary"
135+ class="top-sql-table-id"
136+ @click="changeCluster(scope.row)"
137+ >
138+ {{ scope.row.COMMAND }}
139+ </el-link>
140+ <span v-else>{{ scope.row.COMMAND }}</span>
141+ </template>
142+ </el-table-column>
143+ <el-table-column prop="FullCommand" label="FULL COMMAND" show-overflow-tooltip />
125 <el-table-column prop="NI" label="NI" width="40" />144 <el-table-column prop="NI" label="NI" width="40" />
126 <el-table-column prop="PID" label="PID" width="90" />145 <el-table-column prop="PID" label="PID" width="90" />
127 <el-table-column prop="PR" label="PR" width="40" />146 <el-table-column prop="PR" label="PR" width="40" />
@@ -145,9 +164,9 @@
145 }164 }
146 "165 "
147 >166 >
167+ <el-table-column prop="%MEM" label="%MEM" width="70" />
148 <el-table-column prop="%CPU" label="%CPU" width="60" />168 <el-table-column prop="%CPU" label="%CPU" width="60" />
149- <el-table-column prop="%MEM" label="%MEM" width="60" />169+ <el-table-column prop="COMMAND" label="COMMAND" show-overflow-tooltip />
150- <el-table-column prop="COMMAND" label="COMMAND" />
151 <el-table-column prop="NI" label="NI" width="40" />170 <el-table-column prop="NI" label="NI" width="40" />
152 <el-table-column prop="PID" label="PID" width="90" />171 <el-table-column prop="PID" label="PID" width="90" />
153 <el-table-column prop="PR" label="PR" width="40" />172 <el-table-column prop="PR" label="PR" width="40" />
@@ -157,6 +176,20 @@
157 <el-table-column prop="TIME+" label="TIME+" width="100" />176 <el-table-column prop="TIME+" label="TIME+" width="100" />
158 <el-table-column prop="USER" label="USER" width="120" />177 <el-table-column prop="USER" label="USER" width="120" />
159 <el-table-column prop="VIRT" label="VIRT" width="120" />178 <el-table-column prop="VIRT" label="VIRT" width="120" />
179+ <el-table-column :label="$t('session.trans.sessionID')" width="130">
180+ <template #default="scope">
181+ <el-link type="primary" class="top-sql-table-id" @click="gotoSessionDetail(scope.row.sessionid)">
182+ {{ scope.row.sessionid }}
183+ </el-link>
184+ </template>
185+ </el-table-column>
186+ <el-table-column label="SQLID" width="150">
187+ <template #default="scope">
188+ <el-link type="primary" @click="gotoTopsqlDetail(scope.row.query_id)">
189+ {{ scope.row.query_id }}
190+ </el-link>
191+ </template>
192+ </el-table-column>
160 </el-table>193 </el-table>
161 </el-tab-pane>194 </el-tab-pane>
162 </el-tabs>195 </el-tabs>
@@ -219,6 +252,8 @@ import { Refresh } from '@element-plus/icons-vue'
219import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'252import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'
220import moment from 'moment'253import moment from 'moment'
221import { ElMessage } from 'element-plus'254import { ElMessage } from 'element-plus'
255+import router from '@/router'
256+import { getWDRSnapshot } from '@/api/wdr'
222 257 
223const { t } = useI18n()258const { t } = useI18n()
224 259 
@@ -249,11 +284,12 @@ const metricsData = ref<MetricsData>({
249 time: [],284 time: [],
250})285})
251 286 
287+const emit = defineEmits(['changeCluster', 'goto'])
252const topMemoryProcessNowData = ref<void | TopMemoryProcessNow[]>([])288const topMemoryProcessNowData = ref<void | TopMemoryProcessNow[]>([])
253const topMemoryDBThreadNowData = ref<void | TopMemoryProcessNow[]>([])289const topMemoryDBThreadNowData = ref<void | TopMemoryProcessNow[]>([])
254const innerRefreshTime = ref<number>(30)290const innerRefreshTime = ref<number>(30)
255const innerRefreshDoneTime = ref<string>('')291const innerRefreshDoneTime = ref<string>('')
256-const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange } =292+const { updateCounter, sourceType, autoRefreshTime, tabNow, instanceId, isManualRangeSelected, timeRange, node } =
257 storeToRefs(useMonitorStore(props.tabId))293 storeToRefs(useMonitorStore(props.tabId))
258 294 
259// same for every page in index295// same for every page in index
@@ -293,6 +329,12 @@ const load = (checkTab?: boolean, checkRange?: boolean) => {
293 if (!instanceId.value) return329 if (!instanceId.value) return
294 requestData(props.tabId)330 requestData(props.tabId)
295}331}
332+const rowClassName = ({ row }: { row: any }) => {
333+ if (row.port) {
334+ return 'highlight-row'
335+ }
336+ return ''
337+}
296const { data: indexData, run: requestData } = useRequest(getMemoryMetrics, { manual: true })338const { data: indexData, run: requestData } = useRequest(getMemoryMetrics, { manual: true })
297watch(339watch(
298 indexData,340 indexData,
@@ -311,6 +353,7 @@ watch(
311 MEM_FREE: byteToMB(baseData.MEM_FREE) + 'MB',353 MEM_FREE: byteToMB(baseData.MEM_FREE) + 'MB',
312 MEM_TOTAL: byteToMB(baseData.MEM_TOTAL) + 'MB',354 MEM_TOTAL: byteToMB(baseData.MEM_TOTAL) + 'MB',
313 MEM_USED: byteToMB(baseData.MEM_USED) + 'MB',355 MEM_USED: byteToMB(baseData.MEM_USED) + 'MB',
356+ MEMORY_DB_USED_CURR: byteToMB(baseData.MEMORY_DB_USED_CURR) + 'MB',
314 },357 },
315 ]358 ]
316 metricsData.value.swapInfo = [359 metricsData.value.swapInfo = [
@@ -376,6 +419,16 @@ watch(
376 () => {419 () => {
377 topMemoryProcessNowData.value = topMemoryProcessNowResult.value ? topMemoryProcessNowResult.value[0] : []420 topMemoryProcessNowData.value = topMemoryProcessNowResult.value ? topMemoryProcessNowResult.value[0] : []
378 topMemoryDBThreadNowData.value = topMemoryProcessNowResult.value ? topMemoryProcessNowResult.value[1] : []421 topMemoryDBThreadNowData.value = topMemoryProcessNowResult.value ? topMemoryProcessNowResult.value[1] : []
422+ topMemoryProcessNowData.value.forEach((item) => {
423+ if (item.port) {
424+ item.COMMAND += '(' + node.value.publicIp + ':' + item.port
425+ if (node.value.dbPort.toString() === item.port.toString()) {
426+ item.COMMAND += t('instanceMonitor.thisInstance')
427+ }
428+ item.COMMAND += ')'
429+ item.publicIp = node.value.publicIp
430+ }
431+ })
379 innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')432 innerRefreshDoneTime.value = moment(new Date()).format('HH:mm:ss')
380 },433 },
381 { deep: true }434 { deep: true }
@@ -391,6 +444,9 @@ const updateTimerInner = () => {
391 computed(() => timeInner * 1000)444 computed(() => timeInner * 1000)
392 )445 )
393}446}
447+const changeCluster = (row: TopMemoryProcessNow) => {
448+ emit('changeCluster', row.publicIp, row.port)
449+}
394 450 
395const gotoSQLDiagnosis = () => {451const gotoSQLDiagnosis = () => {
396 hasSQLDiagnosisModule()452 hasSQLDiagnosisModule()
@@ -412,4 +468,66 @@ const gotoSQLDiagnosis = () => {
412 ElMessage.error(t('app.needSQLDiagnosis'))468 ElMessage.error(t('app.needSQLDiagnosis'))
413 })469 })
414}470}
471+ 
472+const gotoSessionDetail = (id: string) => {
473+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
474+ if (curMode === 'wujie') {
475+ // @ts-ignore plug-in components
476+ window.$wujie?.props.methods.jump({
477+ name: `Static-pluginObservability-instanceVemSessionDetail`,
478+ query: {
479+ dbid: instanceId.value,
480+ id,
481+ },
482+ })
483+ } else {
484+ // local
485+ window.sessionStorage.setItem('sqlId', id)
486+ router.push(`/vem/sessionDetail/${instanceId.value}/${id}`)
487+ }
488+}
489+const gotoTopsqlDetail = (id: string) => {
490+ const curMode = localStorage.getItem('INSTANCE_CURRENT_MODE')
491+ if (curMode === 'wujie') {
492+ // @ts-ignore plug-in components
493+ window.$wujie?.props.methods.jump({
494+ name: `Static-pluginObservability-instanceVemSql_detail`,
495+ query: {
496+ dbid: instanceId.value,
497+ id,
498+ },
499+ })
500+ } else {
501+ // local
502+ window.sessionStorage.setItem('sqlId', id)
503+ router.push(`/vem/sql_detail/${instanceId.value}/${id}`)
504+ }
505+}
506+ 
507+const { data: wdrData, run: wdr, loading: wdrLoading } = useRequest(getWDRSnapshot, { manual: true })
508+watch(
509+ wdrData,
510+ (res: any) => {
511+ // goto wdr
512+ if (res && res.wdrId && res.wdrId.length > 0) {
513+ const { timeRange } = useMonitorStore(props.tabId)
514+ let param = {
515+ operation: 'search',
516+ startTime: timeRange == null ? '' : moment(timeRange[0]).format("YYYY-MM-DD HH:mm:ss"),
517+ endTime: timeRange == null ? '' : moment(timeRange[1]).format("YYYY-MM-DD HH:mm:ss"),
518+ }
519+ emit('goto', tabKeys.WDR, param)
520+ } else if (res && res.start && res.end) {
521+ let param = {
522+ operation: 'edit',
523+ startId: res.start,
524+ endId: res.end
525+ }
526+ emit('goto', tabKeys.WDR, param)
527+ } else {
528+ ElMessage.error(t('wdrReports.wdrErrtip'))
529+ }
530+ },
531+ { deep: true }
532+)
415</script>533</script>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/network/Index.vue+64-3
@@ -7,6 +7,9 @@
7 <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">7 <el-link v-if="isManualRangeSelected" type="primary" @click="gotoSQLDiagnosis()">
8 {{ $t('app.diagnosis') }}8 {{ $t('app.diagnosis') }}
9 </el-link>9 </el-link>
10+ <el-link v-if="isManualRangeSelected" type="primary" @click="wdr(tabId)" v-loading="wdrLoading">
11+ {{$t('instanceIndex.wdrAnalysis')}}
12+ </el-link>
10 </div>13 </div>
11 </template>14 </template>
12 <LazyLine15 <LazyLine
@@ -17,6 +20,8 @@
17 :data="metricsData.flowIn"20 :data="metricsData.flowIn"
18 :xData="metricsData.time"21 :xData="metricsData.time"
19 :unit="'MB'"22 :unit="'MB'"
23+ :tool-tips-sort="'desc'"
24+ :tool-tips-exclude-zero="true"
20 />25 />
21 </my-card>26 </my-card>
22 </el-col>27 </el-col>
@@ -28,12 +33,21 @@
28 :data="metricsData.flowOut"33 :data="metricsData.flowOut"
29 :xData="metricsData.time"34 :xData="metricsData.time"
30 :unit="'MB'"35 :unit="'MB'"
36+ :tool-tips-sort="'desc'"
37+ :tool-tips-exclude-zero="true"
31 />38 />
32 </my-card>39 </my-card>
33 </el-col>40 </el-col>
34 <el-col :span="8">41 <el-col :span="8">
35 <my-card :title="$t('resourceMonitor.network.lost')" height="300" :bodyPadding="false">42 <my-card :title="$t('resourceMonitor.network.lost')" height="300" :bodyPadding="false">
36- <LazyLine :tabId="props.tabId" :formatter="toFixed" :data="metricsData.lost" :xData="metricsData.time" />43+ <LazyLine
44+ :tabId="props.tabId"
45+ :formatter="toFixed"
46+ :data="metricsData.lost"
47+ :xData="metricsData.time"
48+ :tool-tips-sort="'desc'"
49+ :tool-tips-exclude-zero="true"
50+ />
37 </my-card>51 </my-card>
38 </el-col>52 </el-col>
39 </el-row>53 </el-row>
@@ -48,17 +62,33 @@
48 :formatter="toFixed"62 :formatter="toFixed"
49 :data="metricsData.networkSocket"63 :data="metricsData.networkSocket"
50 :xData="metricsData.time"64 :xData="metricsData.time"
65+ :tool-tips-sort="'desc'"
66+ :tool-tips-exclude-zero="true"
51 />67 />
52 </my-card>68 </my-card>
53 </el-col>69 </el-col>
54 <el-col :span="8">70 <el-col :span="8">
55 <my-card :title="$t('resourceMonitor.network.tcpQty')" height="300" :bodyPadding="false">71 <my-card :title="$t('resourceMonitor.network.tcpQty')" height="300" :bodyPadding="false">
56- <LazyLine :tabId="props.tabId" :formatter="toFixed" :data="metricsData.tcpSocket" :xData="metricsData.time" />72+ <LazyLine
73+ :tabId="props.tabId"
74+ :formatter="toFixed"
75+ :data="metricsData.tcpSocket"
76+ :xData="metricsData.time"
77+ :tool-tips-sort="'desc'"
78+ :tool-tips-exclude-zero="true"
79+ />
57 </my-card>80 </my-card>
58 </el-col>81 </el-col>
59 <el-col :span="8">82 <el-col :span="8">
60 <my-card :title="$t('resourceMonitor.network.UDPQty')" height="300" :bodyPadding="false">83 <my-card :title="$t('resourceMonitor.network.UDPQty')" height="300" :bodyPadding="false">
61- <LazyLine :tabId="props.tabId" :formatter="toFixed" :data="metricsData.udpSocket" :xData="metricsData.time" />84+ <LazyLine
85+ :tabId="props.tabId"
86+ :formatter="toFixed"
87+ :data="metricsData.udpSocket"
88+ :xData="metricsData.time"
89+ :tool-tips-sort="'desc'"
90+ :tool-tips-exclude-zero="true"
91+ />
62 </my-card>92 </my-card>
63 </el-col>93 </el-col>
64 </el-row>94 </el-row>
@@ -103,10 +133,14 @@ import { useRequest } from 'vue-request'
103import { ElMessage } from 'element-plus'133import { ElMessage } from 'element-plus'
104import { useI18n } from 'vue-i18n'134import { useI18n } from 'vue-i18n'
105import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'135import { hasSQLDiagnosisModule } from '@/api/sqlDiagnosis'
136+import { getWDRSnapshot } from '@/api/wdr'
137+import moment from 'moment'
106 138 
107const props = withDefaults(defineProps<{ tabId: string }>(), {})139const props = withDefaults(defineProps<{ tabId: string }>(), {})
108const { t } = useI18n()140const { t } = useI18n()
109 141 
142+const emit = defineEmits(['goto'])
143+ 
110interface LineData {144interface LineData {
111 name: string145 name: string
112 data: any[]146 data: any[]
@@ -302,6 +336,33 @@ const gotoSQLDiagnosis = () => {
302 ElMessage.error(t('app.needSQLDiagnosis'))336 ElMessage.error(t('app.needSQLDiagnosis'))
303 })337 })
304}338}
339+ 
340+const { data: wdrData, run: wdr, loading: wdrLoading } = useRequest(getWDRSnapshot, { manual: true })
341+watch(
342+ wdrData,
343+ (res: any) => {
344+ // goto wdr
345+ if (res && res.wdrId && res.wdrId.length > 0) {
346+ const { timeRange } = useMonitorStore(props.tabId)
347+ let param = {
348+ operation: 'search',
349+ startTime: timeRange == null ? '' : moment(timeRange[0]).format("YYYY-MM-DD HH:mm:ss"),
350+ endTime: timeRange == null ? '' : moment(timeRange[1]).format("YYYY-MM-DD HH:mm:ss"),
351+ }
352+ emit('goto', tabKeys.WDR, param)
353+ } else if (res && res.start && res.end) {
354+ let param = {
355+ operation: 'edit',
356+ startId: res.start,
357+ endId: res.end
358+ }
359+ emit('goto', tabKeys.WDR, param)
360+ } else {
361+ ElMessage.error(t('wdrReports.wdrErrtip'))
362+ }
363+ },
364+ { deep: true }
365+)
305</script>366</script>
306 367 
307<style scoped lang="scss"></style>368<style scoped lang="scss"></style>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/Index.vue+67-23
@@ -1,18 +1,24 @@
1<template>1<template>
2 <div class="top-sql" style="min-height: 500px">2 <div class="top-sql" style="min-height: 500px">
3 <div class="tab-wrapper-container">3 <div class="tab-wrapper-container">
4+ <div class="row" style="margin-bottom: 10px;">
5+ <el-button type="primary" @click="showSnapshotManage">{{
6+ $t('dashboard.wdrReports.snapshotManage')
7+ }}</el-button>
8+ <el-button type="primary" @click="showBuildWDR">{{ $t('dashboard.wdrReports.buildWDR') }}</el-button>
9+ </div>
4 <div class="search-form-multirow">10 <div class="search-form-multirow">
5 <div class="row">11 <div class="row">
6 <div class="filter">12 <div class="filter">
7 <span>{{ $t('dashboard.wdrReports.reportRange') }}&nbsp;</span>13 <span>{{ $t('dashboard.wdrReports.reportRange') }}&nbsp;</span>
8- <el-select v-model="formData.reportRange" style="width: 160px; margin: 0 4px">14+ <el-select v-model="formData.reportRange" style="width: 160px; margin: 0 4px" clearable>
9 <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />15 <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
10 <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />16 <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
11 </el-select>17 </el-select>
12 </div>18 </div>
13 <div class="filter">19 <div class="filter">
14 <span>{{ $t('dashboard.wdrReports.reportType') }}&nbsp;</span>20 <span>{{ $t('dashboard.wdrReports.reportType') }}&nbsp;</span>
15- <el-select v-model="formData.reportType" style="width: 160px; margin: 0 4px">21+ <el-select v-model="formData.reportType" style="width: 160px; margin: 0 4px" clearable>
16 <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />22 <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
17 <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />23 <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
18 <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />24 <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
@@ -29,16 +35,9 @@
29 style="width: 300px"35 style="width: 300px"
30 />36 />
31 </div>37 </div>
32- </div>
33- 
34- <div class="row">
35 <div class="filter">38 <div class="filter">
36 <el-button @click="handleQuery">{{ $t('app.query') }}</el-button>39 <el-button @click="handleQuery">{{ $t('app.query') }}</el-button>
37 <el-button @click="handleReset">{{ $t('app.reset') }}</el-button>40 <el-button @click="handleReset">{{ $t('app.reset') }}</el-button>
38- <el-button type="primary" @click="showSnapshotManage">{{
39- $t('dashboard.wdrReports.snapshotManage')
40- }}</el-button>
41- <el-button type="primary" @click="showBuildWDR">{{ $t('dashboard.wdrReports.buildWDR') }}</el-button>
42 </div>41 </div>
43 </div>42 </div>
44 </div>43 </div>
@@ -54,28 +53,49 @@
54 :default-sort="{ prop: 'date', order: 'descending' }"53 :default-sort="{ prop: 'date', order: 'descending' }"
55 >54 >
56 <el-table-column55 <el-table-column
57- prop="scope"56+ prop="reportName"
57+ :label="$t('dashboard.wdrReports.list.reportName')"
58+ align="center"
59+ min-width="40%"
60+ />
61+ <el-table-column
62+ prop="scope"
58 :label="$t('dashboard.wdrReports.reportRange')"63 :label="$t('dashboard.wdrReports.reportRange')"
59 min-width="10%"64 min-width="10%"
60 align="center"65 align="center"
61- />66+ >
62- <el-table-column67+ <template #default="scope">
63- prop="reportAt"68+ <div v-if="scope.row.scope === 'CLUSTER'">
64- :label="$t('dashboard.wdrReports.list.buildTime')"69+ {{ $t('dashboard.wdrReports.reportRangeSelect[0]') }}
65- min-width="20%"70+ </div>
66- align="center"71+ <div v-if="scope.row.scope === 'NODE'">
67- />72+ {{ $t('dashboard.wdrReports.reportRangeSelect[1]') }}
73+ </div>
74+ </template>
75+ </el-table-column>
68 <el-table-column76 <el-table-column
69 prop="reportType"77 prop="reportType"
70 :label="$t('dashboard.wdrReports.reportType')"78 :label="$t('dashboard.wdrReports.reportType')"
71 min-width="10%"79 min-width="10%"
72 align="center"80 align="center"
73- />81+ >
82+ <template #default="scope">
83+ <div v-if="scope.row.reportType === 'DETAIL'">
84+ {{ $t('dashboard.wdrReports.reportTypeSelect[0]') }}
85+ </div>
86+ <div v-if="scope.row.reportType === 'SUMMARY'">
87+ {{ $t('dashboard.wdrReports.reportTypeSelect[1]') }}
88+ </div>
89+ <div v-if="scope.row.reportType === 'ALL'">
90+ {{ $t('dashboard.wdrReports.reportTypeSelect[2]') }}
91+ </div>
92+ </template>
93+ </el-table-column>
74 <el-table-column94 <el-table-column
75- prop="reportName"95+ prop="reportAt"
76- :label="$t('dashboard.wdrReports.list.reportName')"96+ :label="$t('dashboard.wdrReports.list.buildTime')"
97+ min-width="20%"
77 align="center"98 align="center"
78- min-width="40%"
79 />99 />
80 <el-table-column :label="$t('app.operate')" align="center" fixed="right" min-width="20%">100 <el-table-column :label="$t('app.operate')" align="center" fixed="right" min-width="20%">
81 <template #default="scope">101 <template #default="scope">
@@ -115,6 +135,8 @@
115 <SnapshotManage :tabId="tabId" v-if="snapshotManageShown" @changeModal="changeModalSnapshotManage" />135 <SnapshotManage :tabId="tabId" v-if="snapshotManageShown" @changeModal="changeModalSnapshotManage" />
116 <BuildWdr136 <BuildWdr
117 :tabId="tabId"137 :tabId="tabId"
138+ :startId="startId"
139+ :endId="endId"
118 v-if="buildWDRShown"140 v-if="buildWDRShown"
119 @changeModal="changeModalBuildWDR"141 @changeModal="changeModalBuildWDR"
120 @conveyFlag="bandleCoveyBuildWDR"142 @conveyFlag="bandleCoveyBuildWDR"
@@ -140,6 +162,9 @@ const errorInfo = ref<string | Error>()
140const props = withDefaults(defineProps<{ tabId: string }>(), {})162const props = withDefaults(defineProps<{ tabId: string }>(), {})
141const { updateCounter, sourceType, tabNow } = storeToRefs(useMonitorStore(props.tabId))163const { updateCounter, sourceType, tabNow } = storeToRefs(useMonitorStore(props.tabId))
142 164 
165+const startId = ref<number>()
166+const endId = ref<number>()
167+ 
143// same for every page in index168// same for every page in index
144onMounted(() => {169onMounted(() => {
145 handleQuery()170 handleQuery()
@@ -156,8 +181,8 @@ watch(
156)181)
157 182 
158const initFormData = {183const initFormData = {
159- reportRange: 'CLUSTER',184+ reportRange: '',
160- reportType: 'DETAIL',185+ reportType: '',
161 dateValue: [186 dateValue: [
162 moment(new Date()).format('YYYY-MM-DD') + ' 00:00:00',187 moment(new Date()).format('YYYY-MM-DD') + ' 00:00:00',
163 moment(new Date()).format('YYYY-MM-DD') + ' 23:59:59',188 moment(new Date()).format('YYYY-MM-DD') + ' 23:59:59',
@@ -185,6 +210,8 @@ const showBuildWDR = () => {
185}210}
186const changeModalBuildWDR = (val: boolean) => {211const changeModalBuildWDR = (val: boolean) => {
187 buildWDRShown.value = val212 buildWDRShown.value = val
213+ startId.value = undefined
214+ endId.value = undefined
188}215}
189const bandleCoveyBuildWDR = (code: number) => {216const bandleCoveyBuildWDR = (code: number) => {
190 requestData()217 requestData()
@@ -336,4 +363,21 @@ function debounce(func: any, delay: any) {
336 }, delay)363 }, delay)
337 }364 }
338}365}
366+ 
367+const outsideGoto = (param: any) => {
368+ if (!param) {
369+ return;
370+ }
371+ console.log(param)
372+ if (param.operation === 'search') {
373+ formData.dateValue = [param.endTime, param.satrtTime]
374+ handleQuery()
375+ }
376+ if (param.operation === 'edit') {
377+ startId.value = param.startId
378+ endId.value = param.endId
379+ buildWDRShown.value = true
380+ }
381+}
382+defineExpose({ outsideGoto })
339</script>383</script>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/buildWdr.vue+153-171
@@ -1,71 +1,49 @@
1<template>1<template>
2- <div class="dialog">2+ <div class="dialog">
3- <el-dialog3+ <el-dialog width="400px" :title="$t('dashboard.wdrReports.buildWDR')" v-model="visible" :close-on-click-modal="false"
4- width="400px"4+ draggable @close="taskClose">
5- :title="$t('dashboard.wdrReports.buildWDR')"5+ <div class="dialog-content" v-loading="generating">
6- v-model="visible"6+ <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
7- :close-on-click-modal="false"7+ <el-form-item :label="$t('datasource.cluterTitle')" prop="hostId">
8- draggable8+ <ClusterCascader ref="clusterComponent" width="200" instanceValueKey="hostId" @loaded="loaded"
9- @close="taskClose"9+ @getCluster="handleClusterValue" notClearable />
10- >10+ </el-form-item>
11- <div class="dialog-content" v-loading="generating">11+ <el-form-item :label="$t('dashboard.wdrReports.reportRange')" prop="reportRange">
12- <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">12+ <el-select v-model="formData.scope" style="width: 200px; margin: 0 4px">
13- <el-form-item :label="$t('datasource.cluterTitle')" prop="hostId">13+ <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
14- <ClusterCascader14+ <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
15- ref="clusterComponent"15+ </el-select>
16- width="200"16+ </el-form-item>
17- instanceValueKey="hostId"17+ <el-form-item :label="$t('dashboard.wdrReports.reportType')" prop="reportType">
18- @loaded="loaded"18+ <el-select v-model="formData.type" style="width: 200px; margin: 0 4px">
19- @getCluster="handleClusterValue"19+ <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
20- notClearable20+ <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
21- />21+ <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
22- </el-form-item>22+ </el-select>
23- <el-form-item :label="$t('dashboard.wdrReports.reportRange')" prop="reportRange">23+ </el-form-item>
24- <el-select v-model="formData.scope" style="width: 200px; margin: 0 4px">24+ <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.startSnapshot')" prop="startId">
25- <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />25+ <el-select v-model="formData.startId" style="width: 200px; margin: 0 4px">
26- <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />26+ <el-option v-for="item in tableData" :key="item.snapshotId" :label="`${item.snapshotId}(${$t('dashboard.wdrReports.buildWDRDialog.startTime')}:${item.startTs})`"
27- </el-select>27+ :value="item.snapshotId" :disabled="parseInt(item.snapshotId) >= parseInt(formData.endId)" />
28- </el-form-item>28+ </el-select>
29- <el-form-item :label="$t('dashboard.wdrReports.reportType')" prop="reportType">29+ </el-form-item>
30- <el-select v-model="formData.type" style="width: 200px; margin: 0 4px">30+ <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.endSnapshot')" prop="endId">
31- <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />31+ <el-select v-model="formData.endId" style="width: 200px; margin: 0 4px">
32- <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />32+ <el-option v-for="item in tableData" :key="item.snapshotId" :label="`${item.snapshotId}(${$t('dashboard.wdrReports.buildWDRDialog.endTime')}:${item.endTs})`"
33- <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />33+ :value="item.snapshotId" :disabled="parseInt(item.snapshotId) <= parseInt(formData.startId)" />
34- </el-select>34+ </el-select>
35- </el-form-item>35+ </el-form-item>
36- <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.startSnapshot')" prop="startId">36+ </el-form>
37- <el-select v-model="formData.startId" style="width: 200px; margin: 0 4px">37+ </div>
38- <el-option
39- v-for="item in tableData"
40- :key="item.snapshotId"
41- :label="item.snapshotId"
42- :value="item.snapshotId"
43- :disabled="parseInt(item.snapshotId) >= parseInt(formData.endId)"
44- />
45- </el-select>
46- </el-form-item>
47- <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.endSnapshot')" prop="endId">
48- <el-select v-model="formData.endId" style="width: 200px; margin: 0 4px">
49- <el-option
50- v-for="item in tableData"
51- :key="item.snapshotId"
52- :label="item.snapshotId"
53- :value="item.snapshotId"
54- :disabled="parseInt(item.snapshotId) <= parseInt(formData.startId)"
55- />
56- </el-select>
57- </el-form-item>
58- </el-form>
59- </div>
60 38 
61- <template #footer>39+ <template #footer>
62- <el-button style="padding: 5px 20px" :loading="generating" type="primary" @click="handleconfirmModel">{{40+ <el-button style="padding: 5px 20px" :loading="generating" type="primary" @click="handleconfirmModel">{{
63- $t('dashboard.wdrReports.buildWDRDialog.build')41+ $t('dashboard.wdrReports.buildWDRDialog.build')
64- }}</el-button>42+ }}</el-button>
65- <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t('app.cancel') }}</el-button>43+ <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t('app.cancel') }}</el-button>
66- </template>44+ </template>
67- </el-dialog>45+ </el-dialog>
68- </div>46+ </div>
69</template>47</template>
70 48 
71<script lang="ts" setup>49<script lang="ts" setup>
@@ -78,157 +56,161 @@ import { useMonitorStore } from '@/store/monitor'
78const { t } = useI18n()56const { t } = useI18n()
79 57 
80const visible = ref(true)58const visible = ref(true)
81-const props = withDefaults(defineProps<{ tabId: string }>(), {})59+const props = withDefaults(defineProps<{
60+ tabId: string,
61+ startId: number,
62+ endId: number
63+}>(), {})
82 64 
83const clusterComponent = ref<any>(null)65const clusterComponent = ref<any>(null)
84const loaded = () => {66const loaded = () => {
85- let instanceId = useMonitorStore(props.tabId).instanceId67+ let instanceId = useMonitorStore(props.tabId).instanceId
86- if (instanceId && clusterComponent.value != null) {68+ if (instanceId && clusterComponent.value != null) {
87- clusterComponent.value.setNodeId(instanceId)69+ clusterComponent.value.setNodeId(instanceId)
88- nextTick(() => {70+ nextTick(() => {
89- requestData()71+ requestData()
90- })72+ })
91- }73+ }
92}74}
93 75 
94// form data76// form data
95const initFormData = {77const initFormData = {
96- clusterId: '',78+ clusterId: '',
97- endId: '',79+ endId: props.endId ? props.endId : '',
98- hostId: '',80+ hostId: '',
99- scope: 'CLUSTER',81+ scope: 'CLUSTER',
100- startId: '',82+ startId: props.startId ? props.startId : '',
101- type: 'DETAIL',83+ type: 'DETAIL',
102}84}
103const formData = reactive(cloneDeep(initFormData))85const formData = reactive(cloneDeep(initFormData))
104 86 
105// cluster component87// cluster component
106const handleClusterValue = (val: any) => {88const handleClusterValue = (val: any) => {
107- formData.clusterId = val.length ? val[0] : ''89+ formData.clusterId = val.length ? val[0] : ''
108- formData.hostId = val.length > 1 ? val[1] : ''90+ formData.hostId = val.length > 1 ? val[1] : ''
109- if (formData.hostId) requestData()91+ if (formData.hostId) requestData()
110}92}
111 93 
112// snapshotList94// snapshotList
113const tableData = ref<Array<any>>([])95const tableData = ref<Array<any>>([])
114const { data: res, run: requestData } = useRequest(96const { data: res, run: requestData } = useRequest(
115- () => {97+ () => {
116- return restRequest98+ return restRequest
117- .get('/wdr/listSnapshot', {99+ .get('/wdr/listSnapshot', {
118- clusterId: formData.clusterId,100+ clusterId: formData.clusterId,
119- hostId: formData.hostId,101+ hostId: formData.hostId,
120- orderby: 'snapshot_id desc',102+ orderby: 'snapshot_id desc',
121- pageSize: 99999,103+ pageSize: 99999,
122- pageNum: 1,104+ pageNum: 1,
123- })105+ })
124- .then(function (res) {106+ .then(function (res) {
125- return res107+ return res
126- })108+ })
127- .catch(function (res) {109+ .catch(function (res) {
128- tableData.value = []110+ tableData.value = []
129- })111+ })
130- },112+ },
131- { manual: true }113+ { manual: true }
132)114)
133watch(res, (res) => {115watch(res, (res) => {
134- if (res && res.records && res.records.length) {116+ if (res && res.records && res.records.length) {
135- tableData.value = res.records117+ tableData.value = res.records
136- if (tableData.value.length > 0) {118+ if (tableData.value.length > 0) {
137- formData.startId = tableData.value[tableData.value.length - 1].snapshotId119+ formData.startId = props.startId ? props.startId : tableData.value[tableData.value.length - 1].snapshotId
138- formData.endId = tableData.value[0].snapshotId120+ formData.endId = props.endId ? props.endId : tableData.value[0].snapshotId
139- }
140- } else {
141- tableData.value = []
142 }121 }
122+ } else {
123+ tableData.value = []
124+ }
143})125})
144 126 
145// build127// build
146const connectionFormRef = ref<FormInstance>()128const connectionFormRef = ref<FormInstance>()
147async function handleconfirmModel() {129async function handleconfirmModel() {
148- try {130+ try {
149- let result = await connectionFormRef.value?.validate()131+ let result = await connectionFormRef.value?.validate()
150- if (result) {132+ if (result) {
151- buildWDR()133+ buildWDR()
152- }134+ }
153- } catch (error) {}135+ } catch (error) { }
154}136}
155const validateStartId = (rule: any, value: any, callback: any) => {137const validateStartId = (rule: any, value: any, callback: any) => {
156- if (!value || !formData.endId) {138+ if (!value || !formData.endId) {
157- callback()139+ callback()
158- } else {140+ } else {
159- if (parseInt(value) >= parseInt(formData.endId)) {141+ if (parseInt(value) >= parseInt(formData.endId)) {
160- callback(new Error(t('datasource.trackFormRules[5]')))142+ callback(new Error(t('datasource.trackFormRules[5]')))
161- return143+ return
162- }
163- callback()
164 }144 }
145+ callback()
146+ }
165}147}
166const validateEndId = (rule: any, value: any, callback: any) => {148const validateEndId = (rule: any, value: any, callback: any) => {
167- if (!value || !formData.startId) {149+ if (!value || !formData.startId) {
168- callback()150+ callback()
169- } else {151+ } else {
170- if (parseInt(value) <= parseInt(formData.startId)) {152+ if (parseInt(value) <= parseInt(formData.startId)) {
171- callback(new Error(t('datasource.trackFormRules[6]')))153+ callback(new Error(t('datasource.trackFormRules[6]')))
172- }
173- callback()
174 }154 }
155+ callback()
156+ }
175}157}
176const connectionFormRules = reactive<FormRules>({158const connectionFormRules = reactive<FormRules>({
177- hostId: [{ required: true, message: t('datasource.trackFormRules[0]'), trigger: 'blur' }],159+ hostId: [{ required: true, message: t('datasource.trackFormRules[0]'), trigger: 'blur' }],
178- startId: [160+ startId: [
179- { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },161+ { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },
180- { validator: validateStartId, trigger: 'blur' },162+ { validator: validateStartId, trigger: 'blur' },
181- ],163+ ],
182- endId: [164+ endId: [
183- { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },165+ { required: true, message: t('datasource.trackFormRules[4]'), trigger: 'blur' },
184- { validator: validateEndId, trigger: 'blur' },166+ { validator: validateEndId, trigger: 'blur' },
185- ],167+ ],
186})168})
187const {169const {
188- data: rez,170+ data: rez,
189- run: buildWDR,171+ run: buildWDR,
190- loading: generating,172+ loading: generating,
191} = useRequest(173} = useRequest(
192- () => {174+ () => {
193- return restRequest.post('/wdr/generate', formData).then(function (res) {175+ return restRequest.post('/wdr/generate', formData).then(function (res) {
194- return res176+ return res
177+ })
178+ },
179+ {
180+ manual: true,
181+ onSuccess: (res) => {
182+ if (res && res.code === 200) {
183+ const msg = t('dashboard.wdrReports.buildWDRDialog.buildSuccess')
184+ ElMessage({
185+ showClose: true,
186+ message: msg,
187+ type: 'success',
195 })188 })
189+ } else {
190+ const msg = t('dashboard.wdrReports.buildWDRDialog.buildFail')
191+ ElMessage({
192+ showClose: true,
193+ message: msg,
194+ type: 'error',
195+ })
196+ }
196 },197 },
197- {198+ }
198- manual: true,
199- onSuccess: (res) => {
200- if (res && res.code === 200) {
201- const msg = t('dashboard.wdrReports.buildWDRDialog.buildSuccess')
202- ElMessage({
203- showClose: true,
204- message: msg,
205- type: 'success',
206- })
207- } else {
208- const msg = t('dashboard.wdrReports.buildWDRDialog.buildFail')
209- ElMessage({
210- showClose: true,
211- message: msg,
212- type: 'error',
213- })
214- }
215- },
216- }
217)199)
218watch(rez, (rez) => {200watch(rez, (rez) => {
219- emit('conveyFlag')201+ emit('conveyFlag')
220- visible.value = false202+ visible.value = false
221- emit('changeModal', visible.value)203+ emit('changeModal', visible.value)
222})204})
223 205 
224const emit = defineEmits(['changeModal', 'conveyFlag'])206const emit = defineEmits(['changeModal', 'conveyFlag'])
225const taskClose = () => {207const taskClose = () => {
226- visible.value = false208+ visible.value = false
227- emit('changeModal', visible.value)209+ emit('changeModal', visible.value)
228}210}
229const handleCancelModel = () => {211const handleCancelModel = () => {
230- visible.value = false212+ visible.value = false
231- emit('changeModal', visible.value)213+ emit('changeModal', visible.value)
232}214}
233</script>215</script>
234<style lang="scss" scoped>216<style lang="scss" scoped>
Mplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/snapshotManage.vue+5-0
@@ -43,6 +43,11 @@
43 :label="$t('dashboard.wdrReports.snapshotManageDialog.snapshotID')"43 :label="$t('dashboard.wdrReports.snapshotManageDialog.snapshotID')"
44 :align="'left'"44 :align="'left'"
45 />45 />
46+ <el-table-column
47+ prop="startTs"
48+ :label="$t('dashboard.wdrReports.snapshotManageDialog.captureTime')"
49+ :align="'left'"
50+ />
46 <el-table-column51 <el-table-column
47 prop="endTs"52 prop="endTs"
48 :label="$t('dashboard.wdrReports.snapshotManageDialog.captureTime')"53 :label="$t('dashboard.wdrReports.snapshotManageDialog.captureTime')"
Mplugins/observability-instance/web-ui/src/pages/sql_detail/Index.vue+108-121
@@ -1,69 +1,52 @@
1<template>1<template>
2- <div class="sql-detail" v-loading="statisticalInfoLoading">2+ <div class="sql-detail" v-loading="statisticalInfoLoading">
3- <my-card3+ <my-card
4- :title="$t('sql.sqlText')"4+ :title="$t('sql.sqlText')"
5- :bodyPadding="false"5+ :bodyPadding="false"
6- @resize-body-height="resizeBodyHeight"6+ @resize-body-height="resizeBodyHeight"
7- collapse7+ collapse
8- resize8+ resize
9- :maxBodyHeight="`${editorMaxHeight}px`"9+ :maxBodyHeight="`${editorMaxHeight}px`"
10- >10+ >
11- <monaco-editor11+ <monaco-editor :modelValue="data.sqlText" style="margin: 4px 0; border: 1px solid #ddd" :height="editorHeight" />
12- :modelValue="data.sqlText"12+ </my-card>
13- style="margin: 4px 0; border: 1px solid #ddd"13+ <div class="sql-detail-tab">
14- :height="editorHeight"14+ <el-tabs v-model="tab" class="tab2" @tab-change="changetTabs">
15- />15+ <el-tab-pane :label="$t('sql.statisticalInformation')" name="statisticalInformation">
16- </my-card>16+ <StatisticalInformation :data="data.statisticalInfo" :loading="statisticalInfoLoading" />
17- <div class="sql-detail-tab">17+ </el-tab-pane>
18- <el-tabs v-model="tab" class="tab2">18+ <el-tab-pane :label="$t('sql.implementationPlan')" name="implementationPlan">
19- <el-tab-pane :label="$t('sql.statisticalInformation')" name="statisticalInformation">19+ <ImplementationPlanThis v-if="tab === 'implementationPlan'" :sqlId="urlParam.sqlId" :dbid="urlParam.dbid" />
20- <StatisticalInformation :data="data.statisticalInfo" :loading="statisticalInfoLoading" />20+ </el-tab-pane>
21- </el-tab-pane>21+ <el-tab-pane :label="$t('sql.systemSource')" name="systemSource">
22- <el-tab-pane :label="$t('sql.implementationPlan')" name="implementationPlan">22+ <SystemSource v-if="tab === 'systemSource'" :dbid="urlParam.dbid" :fixedRangeTime="data.fixedRangeTime" />
23- <ImplementationPlanThis23+ </el-tab-pane>
24- v-if="tab === 'implementationPlan'"24+ <el-tab-pane :label="$t('sql.objectInformation')" name="objectInformation">
25- :sqlId="urlParam.sqlId"25+ <ObjectInformation v-if="tab === 'objectInformation'" :sqlId="urlParam.sqlId" :dbid="urlParam.dbid" />
26- :dbid="urlParam.dbid"26+ </el-tab-pane>
27- />27+ <el-tab-pane :label="$t('sql.indexSuggestions')" name="indexSuggestions">
28- </el-tab-pane>28+ <IndexSuggestionData
29- <el-tab-pane :label="$t('sql.systemSource')" name="systemSource">29+ v-if="tab === 'indexSuggestions'"
30- <SystemSource v-if="tab === 'systemSource'" :fixedRangeTime="data.fixedRangeTime" />30+ :sqlId="urlParam.sqlId"
31- </el-tab-pane>31+ :dbid="urlParam.dbid"
32- <el-tab-pane :label="$t('sql.objectInformation')" name="objectInformation">32+ :sqlText="data.sqlText"
33- <ObjectInformation33+ />
34- v-if="tab === 'objectInformation'"34+ </el-tab-pane>
35- :sqlId="urlParam.sqlId"35+ <el-tab-pane :label="$t('sql.waitEvent')" name="waitEvent">
36- :dbid="urlParam.dbid"36+ <WaitEvent v-if="tab === 'waitEvent'" :sqlId="urlParam.sqlId" :dbid="urlParam.dbid" :sqlText="data.sqlText" />
37- />37+ </el-tab-pane>
38- </el-tab-pane>38+ <el-tab-pane :label="$t('sql.sqlDiagnose')" name="diagnose">
39- <el-tab-pane :label="$t('sql.indexSuggestions')" name="indexSuggestions">39+ <SqlDiagnose
40- <IndexSuggestionData40+ v-if="tab === 'diagnose'"
41- v-if="tab === 'indexSuggestions'"41+ :sqlId="urlParam.sqlId"
42- :sqlId="urlParam.sqlId"42+ :dbid="urlParam.dbid"
43- :dbid="urlParam.dbid"43+ :dbName="dbName"
44- :sqlText="data.sqlText"44+ :sqlText="data.sqlText"
45- />45+ />
46- </el-tab-pane>46+ </el-tab-pane>
47- <el-tab-pane :label="$t('sql.waitEvent')" name="waitEvent">47+ </el-tabs>
48- <WaitEvent
49- v-if="tab === 'waitEvent'"
50- :sqlId="urlParam.sqlId"
51- :dbid="urlParam.dbid"
52- :sqlText="data.sqlText"
53- />
54- </el-tab-pane>
55- <el-tab-pane :label="$t('sql.sqlDiagnose')" name="diagnose">
56- <SqlDiagnose
57- v-if="tab === 'diagnose'"
58- :sqlId="urlParam.sqlId"
59- :dbid="urlParam.dbid"
60- :dbName="dbName"
61- :sqlText="data.sqlText"
62- />
63- </el-tab-pane>
64- </el-tabs>
65- </div>
66 </div>48 </div>
49+ </div>
67</template>50</template>
68<script setup lang="ts">51<script setup lang="ts">
69import { useRequest } from 'vue-request'52import { useRequest } from 'vue-request'
@@ -78,37 +61,38 @@ import ImplementationPlanThis from '@/pages/sql_detail/implementation_plan/Index
78import SqlDiagnose from '@/pages/sql_detail/sql_diagnose/Index.vue'61import SqlDiagnose from '@/pages/sql_detail/sql_diagnose/Index.vue'
79import WaitEvent from '@/pages/sql_detail/waitEvent/Index.vue'62import WaitEvent from '@/pages/sql_detail/waitEvent/Index.vue'
80import { getDatabaseMetrics } from '@/api/prometheus'63import { getDatabaseMetrics } from '@/api/prometheus'
64+import type { TabPanelName } from 'element-plus'
81 65 
82const router = useRouter()66const router = useRouter()
83 67 
84const tab = ref('statisticalInformation')68const tab = ref('statisticalInformation')
85 69 
86const urlParam = reactive<{70const urlParam = reactive<{
87- dbid: string | string[]71+ dbid: string | string[]
88- sqlId: string | string[]72+ sqlId: string | string[]
89}>({73}>({
90- dbid: '',74+ dbid: '',
91- sqlId: '',75+ sqlId: '',
92})76})
93 77 
94const data = reactive<{78const data = reactive<{
95- sqlText: string79+ sqlText: string
96- statisticalInfo: Record<string, string>80+ statisticalInfo: Record<string, string>
97- fixedRangeTime: Array<string>81+ fixedRangeTime: Array<string>
98}>({82}>({
99- sqlText: '',83+ sqlText: '',
100- statisticalInfo: {},84+ statisticalInfo: {},
101- fixedRangeTime: [],85+ fixedRangeTime: [],
102})86})
103 87 
104const {88const {
105- data: statisticalInfoRes,89+ data: statisticalInfoRes,
106- run: requestStatisticalInfo,90+ run: requestStatisticalInfo,
107- loading: statisticalInfoLoading,91+ loading: statisticalInfoLoading,
108} = useRequest(92} = useRequest(
109- (sqlId: string | string[], dbid: string | string[]) =>93+ (sqlId: string | string[], dbid: string | string[]) =>
110- ogRequest.get(`/observability/v1/topsql/detail?id=${dbid}&sqlId=${sqlId}`),94+ ogRequest.get(`/observability/v1/topsql/detail?id=${dbid}&sqlId=${sqlId}`),
111- { manual: true }95+ { manual: true }
112)96)
113 97 
114const editorHeight = ref(200)98const editorHeight = ref(200)
@@ -116,59 +100,62 @@ const editorMaxHeight = ref(400)
116const dbName = ref('')100const dbName = ref('')
117 101 
118const resizeBodyHeight = useDebounceFn((height: number) => {102const resizeBodyHeight = useDebounceFn((height: number) => {
119- editorHeight.value = height103+ editorHeight.value = height
120}, 100)104}, 100)
121 105 
122onMounted(() => {106onMounted(() => {
123- const { sqlId, dbid } = router.currentRoute.value.params107+ const { sqlId, dbid } = router.currentRoute.value.params
124- if (typeof sqlId === 'string' && typeof dbid === 'string') {108+ if (typeof sqlId === 'string' && typeof dbid === 'string') {
125- urlParam.dbid = dbid109+ urlParam.dbid = dbid
126- urlParam.sqlId = sqlId110+ urlParam.sqlId = sqlId
127- } else {111+ } else {
128- // @ts-ignore112+ // @ts-ignore
129- const wujie = window.$wujie113+ const wujie = window.$wujie
130- urlParam.dbid = wujie?.props.data.dbid114+ urlParam.dbid = wujie?.props.data.dbid
131- urlParam.sqlId = wujie?.props.data.id115+ urlParam.sqlId = wujie?.props.data.id
132- }116+ }
133- console.log('sql detail urlParam:', urlParam)117+ requestStatisticalInfo(urlParam.sqlId, urlParam.dbid)
134- requestStatisticalInfo(urlParam.sqlId, urlParam.dbid)118+ nextTick(() => {
135- nextTick(() => {119+ const domRect = document.querySelector('.page')?.getBoundingClientRect() as DOMRect
136- const domRect = document.querySelector('.page')?.getBoundingClientRect() as DOMRect120+ editorHeight.value = Math.floor(domRect.height / 3)
137- editorHeight.value = Math.floor(domRect.height / 3)121+ editorMaxHeight.value = editorHeight.value * 2
138- editorMaxHeight.value = editorHeight.value * 2122+ })
139- })
140})123})
141 124 
142watch(statisticalInfoRes, (res) => {125watch(statisticalInfoRes, (res) => {
143- if (res != null) {126+ if (res != null) {
144- data.statisticalInfo = res127+ data.statisticalInfo = res
145- data.sqlText = res.query128+ data.sqlText = res.query
146- data.fixedRangeTime = [res.start_time, res.finish_time]129+ data.fixedRangeTime = [res.start_time, res.finish_time]
147- dbName.value = res.db_name130+ dbName.value = res.db_name
148- getDatabaseMetrics()131+ getDatabaseMetrics()
149- }132+ }
150})133})
134+ 
135+const changetTabs = (name: TabPanelName) => {
136+ if (name === 'statisticalInformation') requestStatisticalInfo(urlParam.sqlId, urlParam.dbid)
137+}
151</script>138</script>
152 139 
153<style scoped lang="scss">140<style scoped lang="scss">
154.sql-detail {141.sql-detail {
155- /* background-color: transparent; */142+ /* background-color: transparent; */
156- padding: 0;143+ padding: 0;
157 144 
158- &-text {145+ &-text {
159- margin-top: 10px;146+ margin-top: 10px;
160- }147+ }
161- 148+ 
162- &-tab {149+ &-tab {
163- font-size: 14px;150+ font-size: 14px;
164- background-color: var(--el-bg-color);151+ background-color: var(--el-bg-color);
165- margin-top: 16px;152+ margin-top: 16px;
166- 153+ 
167- &:deep(.el-tabs__header) {154+ &:deep(.el-tabs__header) {
168- background-color: var(--el-bg-color-sub);155+ background-color: var(--el-bg-color-sub);
169- padding: 0 10px;156+ padding: 0 10px;
170- margin-bottom: 8px;157+ margin-bottom: 8px;
171- }
172 }158 }
159+ }
173}160}
174</style>161</style>
Mplugins/observability-instance/web-ui/src/pages/sql_detail/implementation_plan/Index.vue+292-283
@@ -1,334 +1,343 @@
1+<template>
2+ <div class="implementation-plan" v-if="!errorInfo">
3+ <div class="i-p-filter">
4+ <el-tooltip effect="light" placement="bottom-end" style="color: #fff">
5+ <template #content
6+ ><p :style="{ color: theme === 'dark' ? '#D4D4D4' : '#868F9C' }">{{ $t('sql.mostWidthPosi') }}</p></template
7+ >
8+ <el-button size="small" @click="getMostValueRow('planWidth')">{{ $t('sql.mostWidth') }}</el-button>
9+ </el-tooltip>
10+ <el-tooltip effect="light" placement="bottom">
11+ <template #content
12+ ><p :style="{ color: theme === 'dark' ? '#D4D4D4' : '#868F9C' }">{{ $t('sql.mostRowsPosi') }}</p></template
13+ >
14+ <el-button size="small" @click="getMostValueRow('planRows')">{{ $t('sql.mostRows') }}</el-button>
15+ </el-tooltip>
16+ <el-tooltip effect="light" placement="bottom-start">
17+ <template #content
18+ ><p :style="{ color: theme === 'dark' ? '#D4D4D4' : '#868F9C' }">{{ $t('sql.mostCostPosi') }}</p></template
19+ >
20+ <el-button size="small" @click="getMostValueRow('singleCost')">{{ $t('sql.mostCost') }}</el-button>
21+ </el-tooltip>
22+ </div>
23+ <div class="i-p-table" v-loading="planDataLoading">
24+ <el-table
25+ ref="singleTableRef"
26+ :data="data.planData"
27+ :style="{ width: '100%', marginBottom: '20px' }"
28+ row-key="id"
29+ :row-class-name="tableRowClassName"
30+ height="340"
31+ default-expand-all
32+ border
33+ >
34+ <el-table-column type="index" />
35+ <el-table-column prop="nodeType" label="operation" />
36+ <el-table-column prop="alias" label="object" width="150" />
37+ <el-table-column label="cost" width="300">
38+ <template #default="{ row }">
39+ <div class="i-p-table-cost">
40+ <div style="flex: 1; position: relative; height: 10px">
41+ <my-progress
42+ :data="[
43+ { label: $t('report.singleStepOperationCost'), value: row.singleCost, color: '#37D4D1' },
44+ { label: $t('report.totalCost'), value: totalSingleCost, total: true },
45+ ]"
46+ :style="{
47+ position: 'absolute',
48+ zIndex:
49+ row.singleCost / totalSingleCost <= row.totalCost / totalCost && row.nodeType !== 'Limit' ? 1 : 0,
50+ }"
51+ width="100%"
52+ height="10px"
53+ :fixTotal="row.totalCost"
54+ :fixColor="['#37D4D1', '#0093FF']"
55+ />
56+ <my-progress
57+ :data="[
58+ {
59+ label: $t('report.singleStepOperationCost'),
60+ value: row.totalCost,
61+ color: '#0093FF',
62+ hide: row.nodeType === 'Limit',
63+ },
64+ { label: $t('report.totalCost'), value: totalCost, total: true },
65+ ]"
66+ style="position: absolute"
67+ width="100%"
68+ height="10px"
69+ :fixTotal="row.totalCost"
70+ :fixColor="['#37D4D1', '#0093FF']"
71+ />
72+ </div>
73+ <p>{{ row.totalCost }}</p>
74+ </div>
75+ </template>
76+ </el-table-column>
77+ <el-table-column label="rows">
78+ <template #default="{ row }">
79+ <div class="i-p-table-cost">
80+ <my-progress
81+ :data="[
82+ { label: 'rows', value: row.planRows ?? 0, color: '#0093FF' },
83+ { label: '', value: data.total.totalPlanRows, total: true },
84+ ]"
85+ style="flex: 1"
86+ width="100%"
87+ height="10px"
88+ :onlyOne="true"
89+ />
90+ <p>{{ row.planRows }}</p>
91+ </div>
92+ </template>
93+ </el-table-column>
94+ <el-table-column label="width">
95+ <template #default="{ row }">
96+ <div class="i-p-table-cost">
97+ <my-progress
98+ :data="[
99+ { label: 'width', value: row.planWidth ?? 0, color: '#0093FF' },
100+ { label: '', value: data.total.totalPlanWidth, total: true },
101+ ]"
102+ style="flex: 1"
103+ width="100%"
104+ height="10px"
105+ :onlyOne="true"
106+ />
107+ <p>{{ row.planWidth }}</p>
108+ </div>
109+ </template>
110+ </el-table-column>
111+ <el-table-column prop="joinType" label="condition" />
112+ </el-table>
113+ </div>
114+ </div>
115+ <my-message
116+ v-if="errorInfo"
117+ :type="isInfoTip(errorInfo) ? 'info' : 'error'"
118+ :tip="$t(`sql.${errorInfo}`)"
119+ defaultTip=""
120+ :key="errorInfo"
121+ />
122+</template>
123+ 
1<script setup lang="ts">124<script setup lang="ts">
2-import { useDebounceFn } from "@vueuse/core";125+import { useDebounceFn } from '@vueuse/core'
3import { ElTable } from 'element-plus'126import { ElTable } from 'element-plus'
4-import { useRequest } from "vue-request";127+import { useRequest } from 'vue-request'
5-import { storeToRefs } from 'pinia';128+import { storeToRefs } from 'pinia'
6-import ogRequest from "../../../request";129+import ogRequest from '@/request'
7-import { useWindowStore } from "../../../store/window";130+import { useWindowStore } from '@/store/window'
8 131 
9const { theme } = storeToRefs(useWindowStore())132const { theme } = storeToRefs(useWindowStore())
10 133 
11-const props = withDefaults(defineProps<{134+const props = withDefaults(
12- dbid: string | string[];135+ defineProps<{
13- sqlId: string | string[];136+ dbid: string | string[]
14-}>(), {137+ sqlId: string | string[]
138+ }>(),
139+ {
15 dbid: '',140 dbid: '',
16 sqlId: '',141 sqlId: '',
17-})142+ }
143+)
18 144 
19-const errorInfo = ref<string | undefined>();145+const errorInfo = ref<string | undefined>()
20 146 
21const data = reactive<{147const data = reactive<{
22- planData: Array<any>,148+ planData: Array<any>
23- total: {149+ total: {
24- totalPlanRows: number,150+ totalPlanRows: number
25- totalPlanWidth: number151+ totalPlanWidth: number
26- }152+ }
27}>({153}>({
28- planData: [],154+ planData: [],
29- total: {155+ total: {
30- totalPlanRows: 1,156+ totalPlanRows: 1,
31- totalPlanWidth: 1157+ totalPlanWidth: 1,
32- }158+ },
33-});159+})
34 160 
35const recordMaxRowData = reactive({161const recordMaxRowData = reactive({
36- id: '',162+ id: '',
37- value: Number.MIN_VALUE163+ value: Number.MIN_VALUE,
38-});164+})
39 165 
40const maxRowData = reactive({166const maxRowData = reactive({
41- id: '',167+ id: '',
42- value: Number.MIN_VALUE168+ value: Number.MIN_VALUE,
43-});169+})
44 170 
45-const {171+const { run: requesPlanDataRes, loading: planDataLoading } = useRequest(
46- run: requesPlanDataRes,172+ (sqlId: string | string[], dbid: string | string[]) => {
47- loading: planDataLoading
48-} = useRequest((sqlId: string | string[], dbid: string | string[]) => {
49 const res = new Promise((resolve, reject) => {173 const res = new Promise((resolve, reject) => {
50- const result = ogRequest.getNative(`/observability/v1/topsql/plan?id=${dbid}&sqlId=${sqlId}`);174+ const result = ogRequest.getNative(`/observability/v1/topsql/plan?id=${dbid}&sqlId=${sqlId}`)
51- result ? resolve(result) : reject(result);175+ result ? resolve(result) : reject(result)
52- }).then((r: any) => {
53- const code = r?.data?.code;
54- const list = r?.data?.data?.data;
55- const total = r?.data?.data?.total;
56- if (code === "602") {
57- errorInfo.value = 'executionParamTip';
58- } else if (code === "200" && Array.isArray(list) && list.length > 0) {
59- calc(list)
60- totalCost.value = list[0].totalCost
61- data.planData = list;
62- data.total = total;
63- } else if (code === 500) {
64- errorInfo.value = r?.data?.msg || 'failGetExecutionPlan';
65- }
66- }).catch((e) => {
67- errorInfo.value = e;
68 })176 })
69- return res;177+ .then((r: any) => {
70-}, { manual: true }178+ const code = r?.data?.code
179+ const list = r?.data?.data?.data
180+ const total = r?.data?.data?.total
181+ if (code === 602) {
182+ errorInfo.value = 'executionParamTip'
183+ } else if (code === 200 && Array.isArray(list) && list.length > 0) {
184+ calc(list)
185+ totalCost.value = list[0].totalCost
186+ data.planData = list
187+ data.total = total
188+ } else if (code === 500) {
189+ errorInfo.value = r?.data?.msg || 'failGetExecutionPlan'
190+ }
191+ })
192+ .catch((e) => {
193+ errorInfo.value = e
194+ })
195+ return res
196+ },
197+ { manual: true }
71)198)
72 199 
73onMounted(() => {200onMounted(() => {
74- requesPlanDataRes(props.sqlId, props.dbid);201+ requesPlanDataRes(props.sqlId, props.dbid)
75-});202+})
76-const totalSingleCost = ref(0);203+const totalSingleCost = ref(0)
77-const totalCost = ref(0);204+const totalCost = ref(0)
78const calc = (nodes: any[]) => {205const calc = (nodes: any[]) => {
79- nodes.forEach(d => {206+ nodes.forEach((d) => {
80- if (d.children && d.children.length) {207+ if (d.children && d.children.length) {
81- if (d.nodeType !== 'Limit') {208+ if (d.nodeType !== 'Limit') {
82- let c = d.totalCost209+ let c = d.totalCost
83- d.children.forEach((child: any) => {210+ d.children.forEach((child: any) => {
84- c = Number.parseFloat((c - child.totalCost).toFixed(2))211+ c = Number.parseFloat((c - child.totalCost).toFixed(2))
85- })212+ })
86- d.singleCost = c213+ d.singleCost = c
87- } else {214+ } else {
88- d.singleCost = 0215+ d.singleCost = 0
89- }216+ }
90- calc(d.children)217+ calc(d.children)
91- } else {218+ } else {
92- d.singleCost = d.totalCost219+ d.singleCost = d.totalCost
93- }220+ }
94- totalSingleCost.value += d.singleCost221+ totalSingleCost.value += d.singleCost
95- })222+ })
96}223}
97watch(recordMaxRowData, () => {224watch(recordMaxRowData, () => {
98- if (recordMaxRowData.id !== '') {225+ if (recordMaxRowData.id !== '') {
99- maxRowData.value = recordMaxRowData.value;226+ maxRowData.value = recordMaxRowData.value
100- maxRowData.id = recordMaxRowData.id;227+ maxRowData.id = recordMaxRowData.id
101- }228+ }
102-});229+})
103 230 
104const getMostValueRow = (type: string) => {231const getMostValueRow = (type: string) => {
105- maxRowData.value = Number.MIN_VALUE;232+ maxRowData.value = Number.MIN_VALUE
106- maxRowData.id = '';233+ maxRowData.id = ''
107- recordMaxRowData.value = Number.MIN_VALUE;234+ recordMaxRowData.value = Number.MIN_VALUE
108- recordMaxRowData.id = '';235+ recordMaxRowData.id = ''
109- TraversalTree(data.planData, type);236+ TraversalTree(data.planData, type)
110}237}
111 238 
112const TraversalTree = useDebounceFn((treeData: Array<any>, type: string) => {239const TraversalTree = useDebounceFn((treeData: Array<any>, type: string) => {
113- treeData.forEach(item => {240+ treeData.forEach((item) => {
114- if (item[type] > recordMaxRowData.value) {241+ if (item[type] > recordMaxRowData.value) {
115- recordMaxRowData.value = item[type];242+ recordMaxRowData.value = item[type]
116- recordMaxRowData.id = item.id;243+ recordMaxRowData.id = item.id
117- }244+ }
118- if (Array.isArray(item.children) && item.children.length > 0) {245+ if (Array.isArray(item.children) && item.children.length > 0) {
119- TraversalTree(item.children, type)246+ TraversalTree(item.children, type)
120- }247+ }
121- });248+ })
122}, 0)249}, 0)
123 250 
124const isInfoTip = (value: string | undefined) => {251const isInfoTip = (value: string | undefined) => {
125- return value === 'failGetExecutionPlan' || value === 'failResolveExecutionPlan'252+ return value === 'failGetExecutionPlan' || value === 'failResolveExecutionPlan'
126}253}
127 254 
128let timer: any255let timer: any
129const tableRowClassName = ({ row }: { row: { id: string } }) => {256const tableRowClassName = ({ row }: { row: { id: string } }) => {
130- if (row.id === maxRowData.id) {257+ if (row.id === maxRowData.id) {
131- let observer: IntersectionObserver;258+ let observer: IntersectionObserver
132- let el: HTMLElement;259+ let el: HTMLElement
133- let top: number = 0;260+ let top: number = 0
134- // clear high light261+ // clear high light
135- if (timer) {262+ if (timer) {
136- clearTimeout(timer)263+ clearTimeout(timer)
137- }
138- timer = setTimeout(() => {
139- observer.unobserve(el)
140- maxRowData.value = Number.MIN_VALUE;
141- maxRowData.id = '';
142- recordMaxRowData.value = Number.MIN_VALUE;
143- recordMaxRowData.id = '';
144- if (timer) {
145- clearTimeout(timer)
146- }
147- }, 3000);
148-
149- nextTick(() => {
150- el = document.querySelector('.warning-row') as HTMLElement
151- if (el) {
152- observer = new IntersectionObserver((entries) => {
153- if (!entries[0].isIntersecting) {
154- document.querySelector('.i-p-table .el-scrollbar__wrap')?.scrollTo(0, top)
155- observer.unobserve(el)
156- }
157- }, {
158- threshold: 1,
159- root: document.querySelector('.i-p-table .el-scrollbar__wrap')
160- })
161- observer.observe(el)
162- top = el.offsetTop
163- let current = el.offsetParent as HTMLElement | null
164- while (current !== null && !current.classList.contains('el-scrollbar')) {
165- top += current.offsetTop
166- current = current.offsetParent as HTMLElement | null
167- }
168- }
169- })
170- return 'warning-row'
171 }264 }
172- return ''265+ timer = setTimeout(() => {
173-}266+ observer.unobserve(el)
267+ maxRowData.value = Number.MIN_VALUE
268+ maxRowData.id = ''
269+ recordMaxRowData.value = Number.MIN_VALUE
270+ recordMaxRowData.id = ''
271+ if (timer) {
272+ clearTimeout(timer)
273+ }
274+ }, 3000)
174 275 
276+ nextTick(() => {
277+ el = document.querySelector('.warning-row') as HTMLElement
278+ if (el) {
279+ observer = new IntersectionObserver(
280+ (entries) => {
281+ if (!entries[0].isIntersecting) {
282+ document.querySelector('.i-p-table .el-scrollbar__wrap')?.scrollTo(0, top)
283+ observer.unobserve(el)
284+ }
285+ },
286+ {
287+ threshold: 1,
288+ root: document.querySelector('.i-p-table .el-scrollbar__wrap'),
289+ }
290+ )
291+ observer.observe(el)
292+ top = el.offsetTop
293+ let current = el.offsetParent as HTMLElement | null
294+ while (current !== null && !current.classList.contains('el-scrollbar')) {
295+ top += current.offsetTop
296+ current = current.offsetParent as HTMLElement | null
297+ }
298+ }
299+ })
300+ return 'warning-row'
301+ }
302+ return ''
303+}
175</script>304</script>
176 305 
177-<template>
178- <div class="implementation-plan" v-if="!errorInfo">
179- <div class="i-p-filter">
180- <el-tooltip
181- effect="light"
182- placement="bottom-end"
183- style="color: #fff"
184- >
185- <template #content><p :style="{color: theme === 'dark' ? '#D4D4D4' : '#868F9C'}">{{ $t('sql.mostWidthPosi') }}</p></template>
186- <el-button size="small" @click="getMostValueRow('planWidth')">{{ $t('sql.mostWidth') }}</el-button>
187- </el-tooltip>
188- <el-tooltip
189- effect="light"
190- placement="bottom"
191- >
192- <template #content><p :style="{color: theme === 'dark' ? '#D4D4D4' : '#868F9C'}">{{ $t('sql.mostRowsPosi') }}</p></template>
193- <el-button size="small" @click="getMostValueRow('planRows')">{{ $t('sql.mostRows') }}</el-button>
194- </el-tooltip>
195- <el-tooltip
196- effect="light"
197- placement="bottom-start"
198- >
199- <template #content><p :style="{color: theme === 'dark' ? '#D4D4D4' : '#868F9C'}">{{ $t('sql.mostCostPosi') }}</p></template>
200- <el-button size="small" @click="getMostValueRow('singleCost')">{{ $t('sql.mostCost') }}</el-button>
201- </el-tooltip>
202- </div>
203- <div class="i-p-table" v-loading="planDataLoading">
204- <el-table
205- ref="singleTableRef"
206- :data="data.planData"
207- :style="{ width: '100%', marginBottom: '20px' }"
208- row-key="id"
209- :row-class-name="tableRowClassName"
210- height="340"
211- default-expand-all
212- border
213- >
214- <el-table-column type="index" />
215- <el-table-column prop="nodeType" label="operation" />
216- <el-table-column prop="alias" label="object" width="150" />
217- <el-table-column label="cost" width="300">
218- <template #default="{row}">
219- <div class="i-p-table-cost">
220- <div style="flex: 1;position: relative;height: 10px;">
221- <my-progress :data="[
222- { label: $t('report.singleStepOperationCost'), value: row.singleCost, color: '#37D4D1' },
223- { label: $t('report.totalCost'), value: totalSingleCost, total: true }
224- ]"
225- :style="{position: 'absolute', zIndex: ((row.singleCost / totalSingleCost) <= (row.totalCost / totalCost)) && row.nodeType !== 'Limit' ? 1 : 0}"
226- width="100%"
227- height="10px"
228- :fixTotal="row.totalCost"
229- :fixColor="['#37D4D1', '#0093FF']"
230- />
231- <my-progress :data="[
232- { label: $t('report.singleStepOperationCost'), value: row.totalCost, color: '#0093FF', hide: row.nodeType === 'Limit' },
233- { label: $t('report.totalCost'), value: totalCost, total: true }
234- ]"
235- style="position: absolute;"
236- width="100%"
237- height="10px"
238- :fixTotal="row.totalCost"
239- :fixColor="['#37D4D1', '#0093FF']"
240- />
241- </div>
242- <p>{{ row.totalCost }}</p>
243- </div>
244- </template>
245- </el-table-column>
246- <el-table-column label="rows">
247- <template #default="{row}">
248- <div class="i-p-table-cost">
249- <my-progress
250- :data="[
251- { label: 'rows', value: row.planRows ?? 0, color: '#0093FF' },
252- { label: '', value: data.total.totalPlanRows, total: true }
253- ]"
254- style="flex: 1;"
255- width="100%"
256- height="10px"
257- :onlyOne="true"
258- />
259- <p>{{ row.planRows }}</p>
260- </div>
261- </template>
262- </el-table-column>
263- <el-table-column label="width">
264- <template #default="{row}">
265- <div class="i-p-table-cost">
266- <my-progress
267- :data="[
268- { label: 'width', value: row.planWidth ?? 0, color: '#0093FF' },
269- { label: '', value: data.total.totalPlanWidth, total: true }
270- ]"
271- style="flex: 1;"
272- width="100%"
273- height="10px"
274- :onlyOne="true"
275- />
276- <p>{{ row.planWidth }}</p>
277- </div>
278- </template>
279- </el-table-column>
280- <el-table-column prop="joinType" label="condition" />
281- </el-table>
282- </div>
283- </div>
284- <my-message
285- v-if="errorInfo"
286- :type="isInfoTip(errorInfo) ? 'info' : 'error'"
287- :tip="$t(`sql.${errorInfo}`)"
288- defaultTip=""
289- :key="errorInfo"
290- />
291-</template>
292- 
293<style scoped lang="scss">306<style scoped lang="scss">
294- 
295.implementation-plan {307.implementation-plan {
296- 308+ .i-p-filter {
297- .i-p-filter {309+ display: flex;
298- display: flex;310+ flex-direction: row-reverse;
299- flex-direction: row-reverse;311+ margin-bottom: 8px;
300- margin-bottom: 8px;
301 312 
302- &:deep(.el-button) {313+ &:deep(.el-button) {
303- margin: 0 0 0 8px;314+ margin: 0 0 0 8px;
304- background-color: var(--el-button-color-small);315+ background-color: var(--el-button-color-small);
305- border: 1px solid #353535;316+ border: 1px solid #353535;
306- color: var(--el-text-color-og);317+ color: var(--el-text-color-og);
307- }
308- 
309- &:deep(.el-button:hover) {
310- color: var(--el-text-color-og);
311- }
312 }318 }
313 319 
314- .i-p-table {320+ &:deep(.el-button:hover) {
315- 321+ color: var(--el-text-color-og);
316- overflow-y: auto;322+ }
323+ }
317 324 
318- &-cost {325+ .i-p-table {
319- height: 20px;326+ overflow-y: auto;
320- display: flex;
321- align-items: center;
322- justify-content: space-between;
323- > p {
324- margin-left: 8px;
325- }
326- }
327 327 
328- &:deep(.el-table .warning-row) {328+ &-cost {
329- --el-table-tr-bg-color: var(--el-color-table-row-bg-color);329+ height: 20px;
330- }330+ display: flex;
331+ align-items: center;
332+ justify-content: space-between;
333+ > p {
334+ margin-left: 8px;
335+ }
331 }336 }
332 337 
338+ &:deep(.el-table .warning-row) {
339+ --el-table-tr-bg-color: var(--el-color-table-row-bg-color);
340+ }
341+ }
333}342}
334</style>343</style>
Mplugins/observability-instance/web-ui/src/pages/sql_detail/statistical_information/Index.vue+284-272
@@ -1,307 +1,319 @@
1+<template>
2+ <div class="statistical-information" v-loading="props.loading">
3+ <div class="s-i-row">
4+ <div class="s-i-col-left">
5+ <my-card height="280" :title="$t('sql.baseInfoTitle')" :bodyPadding="false">
6+ <div class="s-i-base">
7+ <p class="s-i-base-item" v-for="item in baseInfoOption" :key="item.value">
8+ {{ $t(`sql.baseInfoOption.${item.label}`) }}
9+ {{ `:${getValue(item.value, props.data)}` }}
10+ </p>
11+ </div>
12+ </my-card>
13+ </div>
14+ <div class="s-i-col-right">
15+ <my-card height="280" :title="$t('sql.executionStatisticTitle')" :bodyPadding="false">
16+ <div class="s-i-execute">
17+ <el-row v-for="(item, index) in executeOption" :key="index">
18+ <el-col class="s-i-el-col" :span="5">{{ $t(`sql.executeOption.${item[0].label}`) }}</el-col>
19+ <el-col :span="5">{{ getValue(item[0].value, props.data) }}</el-col>
20+ <el-col class="s-i-el-col" :span="8">{{ $t(`sql.executeOption.${item[1].label}`) }}</el-col>
21+ <el-col :span="6">{{ getValue(item[1].value, props.data) }}</el-col>
22+ </el-row>
23+ </div>
24+ </my-card>
25+ </div>
26+ </div>
27+ <div class="s-i-row">
28+ <div class="s-i-col-left">
29+ <my-card height="300" :title="$t('sql.consumptionStatisticTitle')" :bodyPadding="false">
30+ <div class="s-i-statistics">
31+ <div class="s-i-statistics-box">
32+ <div class="s-i-statistics-pie">
33+ <my-pie
34+ :showLegend="false"
35+ :center="['50%', '50%']"
36+ :data="data.useTimeStatistical"
37+ :color="['#3DC94C', '#DA864C']"
38+ :theme="theme"
39+ :key="theme"
40+ />
41+ </div>
42+ <div class="s-i-statistics-legend">
43+ <div class="s-i-statistics-item" v-for="item in data.useTimeStatistical" :key="item.name">
44+ <span :style="`background-color: ${item.color}`" class="s-i-statistics-item-block"></span>
45+ <span>{{ $t(`sql.${item.name}`) }} {{ `(${item.value.toFixed(2)}%)` }}</span>
46+ </div>
47+ </div>
48+ </div>
49+ <div class="s-i-statistics-list">
50+ <p class="s-i-statistics-text">{{ `${$t('sql.dbTimeLabel')}:${props.data['db_time']} ms` }}</p>
51+ <p class="s-i-statistics-text">{{ `${$t('sql.cpuTimeLabel')}:${props.data['cpu_time']} ms` }}</p>
52+ <p class="s-i-statistics-text">{{ `${$t('sql.waitTimeLabel')}:${props.data['wait_time']} ms` }}</p>
53+ </div>
54+ </div>
55+ </my-card>
56+ </div>
57+ <div class="s-i-col-right">
58+ <my-card height="300" :title="$t('sql.consumingBreakdownTitle')" :bodyPadding="false">
59+ <div class="s-i-consuming">
60+ <div class="s-i-consuming-pie">
61+ <my-pie
62+ :showLegend="false"
63+ :center="['50%', '50%']"
64+ :data="data.consumingPieData"
65+ :color="data.consumingPieColor"
66+ :theme="theme"
67+ :key="theme"
68+ />
69+ </div>
70+ <div class="s-i-consuming-legend">
71+ <div class="s-i-consuming-item" v-for="item in consumingOption" :key="item.value">
72+ <span :style="`background-color: ${item.color}`" class="s-i-consuming-item-block"></span>
73+ <span class="s-i-consuming-item-label">{{ $t(`sql.consumingOption.${item.label}`) }}:</span>
74+ <span class="s-i-consuming-item-value">{{ `${getValue(item.value, props.data)} ms` }}</span>
75+ </div>
76+ </div>
77+ </div>
78+ </my-card>
79+ </div>
80+ </div>
81+ </div>
82+</template>
83+ 
1<script setup lang="ts">84<script setup lang="ts">
2-import { storeToRefs } from 'pinia';85+import { storeToRefs } from 'pinia'
3-import { baseInfoOption, executeOption, consumingOption } from './common';86+import { baseInfoOption, executeOption, consumingOption } from '@/pages/sql_detail/statistical_information/common'
4-import { useWindowStore } from "../../../store/window";87+import { useWindowStore } from '@/store/window'
5 88 
6const { theme } = storeToRefs(useWindowStore())89const { theme } = storeToRefs(useWindowStore())
7 90 
8-const props = withDefaults(defineProps<{91+const props = withDefaults(
9- data: Record<string, string>,92+ defineProps<{
93+ data: Record<string, string>
10 loading: boolean94 loading: boolean
11-}>(), {95+ }>(),
12- loading: false96+ {
13-})97+ loading: false,
98+ }
99+)
14const data = reactive<{100const data = reactive<{
15- useTimeStatistical: Array<{ name: string, value: number, color?: string }>,101+ useTimeStatistical: Array<{ name: string; value: number; color?: string }>
16- consumingPieData: Array<{ name: string, value: number }>,102+ consumingPieData: Array<{ name: string; value: number }>
17- consumingPieColor: Array<string>103+ consumingPieColor: Array<string>
18}>({104}>({
19- useTimeStatistical: [],105+ useTimeStatistical: [],
20- consumingPieData: [],106+ consumingPieData: [],
21- consumingPieColor: []107+ consumingPieColor: [],
22-});108+})
23 109 
24const getValue = (key: string, curData: Record<string, string>) => {110const getValue = (key: string, curData: Record<string, string>) => {
25- if (curData != null) {111+ if (curData != null) {
26- return curData[key] || '-';112+ if (typeof curData[key] === 'object') return curData[key].value
27- }113+ else return curData[key] || '-'
28- return '-';114+ }
115+ return '-'
29}116}
30 117 
31-watch(() => props.data, (res) => {118+watch(
32- // eslint-disable-next-line camelcase119+ () => props.data,
33- const { cpu_time, wait_time, db_time } = res;120+ (res) => {
34- try {121+ // clean data
35- const cpuTimeDb = Number.parseInt(db_time) !== 0 ? Number.parseInt(cpu_time) / Number.parseInt(db_time) * 100 : 0;122+ data.useTimeStatistical = []
36- const waitTimeDb = Number.parseInt(db_time) !== 0 ? Number.parseInt(wait_time) / Number.parseInt(db_time) * 100 : 0;
37- data.useTimeStatistical = [{
38- name: 'waitTimeLabel',
39- value: waitTimeDb,
40- color: '#3DC94C'
41- }, {
42- name: 'cpuTimeLabel',
43- value: cpuTimeDb,
44- color: '#DA864C'
45- }]
46- consumingOption.forEach(item => {
47- const value = Number.parseInt(getValue(item.value, props.data));
48- if (value > 0) {
49- data.consumingPieColor.push(item.color as string);
50- data.consumingPieData.push({
51- name: item.label,
52- value: Number.parseInt(getValue(item.value, props.data))
53- });
54- }
55- })
56- } catch (e) {
57- data.useTimeStatistical = [];
58- };
59-}, { deep: true })
60 123 
124+ // eslint-disable-next-line camelcase
125+ const { cpu_time, wait_time, db_time } = res
126+ try {
127+ const cpuTimeDb =
128+ Number.parseInt(db_time) !== 0 ? (Number.parseInt(cpu_time) / Number.parseInt(db_time)) * 100 : 0
129+ const waitTimeDb =
130+ Number.parseInt(db_time) !== 0 ? (Number.parseInt(wait_time) / Number.parseInt(db_time)) * 100 : 0
131+ data.useTimeStatistical = [
132+ {
133+ name: 'waitTimeLabel',
134+ value: waitTimeDb,
135+ color: '#3DC94C',
136+ },
137+ {
138+ name: 'cpuTimeLabel',
139+ value: cpuTimeDb,
140+ color: '#DA864C',
141+ },
142+ ]
143+ consumingOption.forEach((item: any) => {
144+ const value = Number.parseInt(getValue(item.value, props.data))
145+ if (value > 0) {
146+ data.consumingPieColor.push(item.color as string)
147+ data.consumingPieData.push({
148+ name: item.label,
149+ value: Number.parseInt(getValue(item.value, props.data)),
150+ })
151+ }
152+ })
153+ } catch (e) {
154+ data.useTimeStatistical = []
155+ }
156+ },
157+ { deep: true }
158+)
61</script>159</script>
62 160 
63-<template>
64- <div class="statistical-information" v-loading="props.loading">
65- <div class="s-i-row">
66- <div class="s-i-col-left">
67- <my-card height="280" :title="$t('sql.baseInfoTitle')" :bodyPadding="false">
68- <div class="s-i-base">
69- <p class="s-i-base-item" v-for="item in baseInfoOption" :key="item.value">
70- {{ $t(`sql.baseInfoOption.${item.label}`) }}
71- {{ `:${ getValue(item.value, props.data) }` }}
72- </p>
73- </div>
74- </my-card>
75- </div>
76- <div class="s-i-col-right">
77- <my-card height="280" :title="$t('sql.executionStatisticTitle')" :bodyPadding="false">
78- <div class="s-i-execute">
79- <el-row v-for="(item, index) in executeOption" :key="index">
80- <el-col class="s-i-el-col" :span="5">{{ $t(`sql.executeOption.${item[0].label}`) }}</el-col>
81- <el-col :span="5">{{ getValue(item[0].value, props.data) }}</el-col>
82- <el-col class="s-i-el-col" :span="8">{{ $t(`sql.executeOption.${item[1].label}`) }}</el-col>
83- <el-col :span="6">{{ getValue(item[1].value, props.data) }}</el-col>
84- </el-row>
85- </div>
86- </my-card>
87- </div>
88- </div>
89- <div class="s-i-row">
90- <div class="s-i-col-left">
91- <my-card height="300" :title="$t('sql.consumptionStatisticTitle')" :bodyPadding="false">
92- <div class="s-i-statistics">
93- <div class="s-i-statistics-box">
94- <div class="s-i-statistics-pie">
95- <my-pie
96- :showLegend="false"
97- :center="['50%', '50%']"
98- :data="data.useTimeStatistical"
99- :color="['#3DC94C', '#DA864C']"
100- :theme="theme"
101- :key="theme"
102- />
103- </div>
104- <div class="s-i-statistics-legend">
105- <div class="s-i-statistics-item" v-for="item in data.useTimeStatistical" :key="item.value">
106- <span :style="`background-color: ${item.color}`" class="s-i-statistics-item-block"></span>
107- <span>{{ $t(`sql.${item.name}`) }} {{ `(${item.value.toFixed(2)}%)` }}</span>
108- </div>
109- </div>
110- </div>
111- <div class="s-i-statistics-list">
112- <p class="s-i-statistics-text">{{ `${$t('sql.dbTimeLabel')}:${props.data['db_time']} ms` }}</p>
113- <p class="s-i-statistics-text">{{ `${$t('sql.cpuTimeLabel')}:${props.data['cpu_time']} ms` }}</p>
114- <p class="s-i-statistics-text">{{ `${$t('sql.waitTimeLabel')}:${props.data['wait_time']} ms` }}</p>
115- </div>
116- </div>
117- </my-card>
118- </div>
119- <div class="s-i-col-right">
120- <my-card height="300" :title="$t('sql.consumingBreakdownTitle')" :bodyPadding="false">
121- <div class="s-i-consuming">
122- <div class="s-i-consuming-pie">
123- <my-pie
124- :showLegend="false"
125- :center="['50%', '50%']"
126- :data="data.consumingPieData"
127- :color="data.consumingPieColor"
128- :theme="theme"
129- :key="theme"
130- />
131- </div>
132- <div class="s-i-consuming-legend">
133- <div class="s-i-consuming-item" v-for="item in consumingOption" :key="item.value">
134- <span :style="`background-color: ${item.color}`" class="s-i-consuming-item-block"></span>
135- <span class="s-i-consuming-item-label">{{ $t(`sql.consumingOption.${item.label}`) }}:</span>
136- <span class="s-i-consuming-item-value">{{ `${getValue(item.value, props.data)} ms` }}</span>
137- </div>
138- </div>
139- </div>
140- </my-card>
141- </div>
142- </div>
143- </div>
144-</template>
145- 
146<style scoped lang="scss">161<style scoped lang="scss">
147- 
148.statistical-information {162.statistical-information {
149- font-size: 14px;163+ font-size: 14px;
150- // border-top: 1px solid $og-border-color;164+ // border-top: 1px solid $og-border-color;
151 165 
152- .s-i-row {166+ .s-i-row {
153- width: 100%;167+ width: 100%;
154- display: flex;168+ display: flex;
155- justify-content: space-between;169+ justify-content: space-between;
156- margin-bottom: 18px;170+ margin-bottom: 18px;
171+ }
172+ 
173+ .s-i-col-left {
174+ height: inherit;
175+ width: 40%;
176+ }
177+ 
178+ .s-i-col-right {
179+ height: inherit;
180+ width: 58%;
181+ }
182+ 
183+ .s-i-base {
184+ height: inherit;
185+ box-sizing: border-box;
186+ display: flex;
187+ flex-direction: column;
188+ justify-content: space-between;
189+ padding: 18px 40px;
190+ overflow-y: auto;
191+ 
192+ &-item {
193+ margin: 3px 0;
194+ }
195+ &:deep(.el-row) {
196+ line-height: 39px;
197+ border: 1px solid $og-border-color;
198+ border-top: none;
199+ border-right: none;
157 }200 }
158 201 
159- .s-i-col-left {202+ &:deep(.el-col) {
160- height: inherit;203+ padding-left: 5px;
161- width: 40%;204+ border-right: 1px solid $og-border-color;
205+ }
206+ }
207+ 
208+ .s-i-execute {
209+ height: inherit;
210+ margin-top: 5px;
211+ border-top: 1px solid var(--el-color--col-border-color);
212+ overflow-y: scroll;
213+ .s-i-el-col {
214+ background-color: var(--el-color-card-header-color);
162 }215 }
163 216 
164- .s-i-col-right {217+ &:deep(.el-row) {
165- height: inherit;218+ line-height: 39px;
166- width: 58%;219+ border: 1px solid var(--el-color--col-border-color);
220+ border-top: none;
167 }221 }
168 222 
169- .s-i-base {223+ &:deep(.el-row:first-child) {
170- height: inherit;224+ border-right: 1px solid var(--el-color--col-border-color);
171- box-sizing: border-box;
172- display: flex;
173- flex-direction: column;
174- justify-content: space-between;
175- padding: 18px 40px;
176- overflow-y: auto;
177- 
178- &-item {
179- margin: 3px 0;
180- }
181- &:deep(.el-row) {
182- line-height: 39px;
183- border: 1px solid $og-border-color;
184- border-top: none;
185- border-right: none;
186- }
187- 
188- &:deep(.el-col) {
189- padding-left: 5px;
190- border-right: 1px solid $og-border-color;
191- }
192 }225 }
193 226 
194- .s-i-execute {227+ &:deep(.el-col) {
195- height: inherit;228+ padding-left: 5px;
196- margin-top: 5px;229+ // border-top: 1px solid #4A4A4A;
197- border-top: 1px solid var(--el-color--col-border-color);230+ }
198- overflow-y: scroll;231+ }
199- .s-i-el-col {
200- background-color: var(--el-color-card-header-color);
201- }
202 232 
203- &:deep(.el-row) {233+ .s-i-statistics {
204- line-height: 39px;234+ height: inherit;
205- border: 1px solid var(--el-color--col-border-color);235+ display: flex;
206- border-top: none;236+ align-items: center;
207- }237+ overflow-y: auto;
208 238 
209- &:deep(.el-row:first-child) {239+ &-box {
210- border-right: 1px solid var(--el-color--col-border-color);240+ display: flex;
211- }241+ width: 80%;
212- 
213- &:deep(.el-col) {
214- padding-left: 5px;
215- // border-top: 1px solid #4A4A4A;
216- 
217- }
218 }242 }
219 243 
220- .s-i-statistics {244+ &-pie {
221- height: inherit;245+ height: 180px;
222- display: flex;246+ width: 55%;
223- align-items: center;
224- overflow-y: auto;
225- 
226- &-box {
227- display: flex;
228- width: 80%;
229- }
230- 
231- &-pie {
232- height: 180px;
233- width: 55%;
234- }
235- 
236- &-legend {
237- width: calc(100% - 120px);
238- // height: 220px;
239- display: flex;
240- flex-direction: column;
241- justify-content: center;
242- }
243- 
244- &-item {
245- font-size: 12px;
246- display: flex;
247- align-items: center;
248- margin-bottom: 10px;
249- 
250- &-block {
251- display: inline-block;
252- width: 10px;
253- height: 10px;
254- margin-right: 3px;
255- }
256- }
257- 
258- &-list {
259- font-size: 12px;
260- width: 45%;
261- }
262 }247 }
263 248 
264- .s-i-consuming {249+ &-legend {
265- height: inherit;250+ width: calc(100% - 120px);
266- display: flex;251+ // height: 220px;
267- align-items: center;252+ display: flex;
268- padding: 0 0;253+ flex-direction: column;
269- overflow-y: auto;254+ justify-content: center;
270- 
271- &-pie {
272- height: 180px;
273- width: 180px;
274- }
275- 
276- &-legend {
277- width: calc(100% - 180px);
278- height: 220px;
279- display: flex;
280- flex-wrap: wrap;
281- padding-left: 10px;
282- }
283- 
284- &-item {
285- font-size: 12px;
286- display: flex;
287- align-items: center;
288- 
289- &:nth-child(1n) {
290- width: 43%;
291- }
292- 
293- &:nth-child(2n) {
294- width: 57%;
295- }
296- 
297- &-block {
298- display: inline-block;
299- width: 10px;
300- height: 10px;
301- margin-right: 3px;
302- }
303- }
304 }255 }
305 256 
257+ &-item {
258+ font-size: 12px;
259+ display: flex;
260+ align-items: center;
261+ margin-bottom: 10px;
262+ 
263+ &-block {
264+ display: inline-block;
265+ width: 10px;
266+ height: 10px;
267+ margin-right: 3px;
268+ }
269+ }
270+ 
271+ &-list {
272+ font-size: 12px;
273+ width: 45%;
274+ }
275+ }
276+ 
277+ .s-i-consuming {
278+ height: inherit;
279+ display: flex;
280+ align-items: center;
281+ padding: 0 0;
282+ overflow-y: auto;
283+ 
284+ &-pie {
285+ height: 180px;
286+ width: 180px;
287+ }
288+ 
289+ &-legend {
290+ width: calc(100% - 180px);
291+ height: 220px;
292+ display: flex;
293+ flex-wrap: wrap;
294+ padding-left: 10px;
295+ }
296+ 
297+ &-item {
298+ font-size: 12px;
299+ display: flex;
300+ align-items: center;
301+ 
302+ &:nth-child(1n) {
303+ width: 43%;
304+ }
305+ 
306+ &:nth-child(2n) {
307+ width: 57%;
308+ }
309+ 
310+ &-block {
311+ display: inline-block;
312+ width: 10px;
313+ height: 10px;
314+ margin-right: 3px;
315+ }
316+ }
317+ }
306}318}
307</style>319</style>
Mplugins/observability-instance/web-ui/src/pages/sql_detail/system_source/Index.vue+331-144
@@ -1,165 +1,352 @@
1-<script setup lang="ts">1+<template>
2-import moment from 'moment';2+ <div class="system-source">
3-import LazyLine from '../../dashboard/LazyLine.vue';3+ <my-message
4-import { toFixed } from '../../../shared';4+ type="info"
5-import { i18n } from '../../../i18n';5+ :tip="`${$t('dashboard.rangeTimeTip')}(${getRangeTime()})`"
6-import { getDatabaseMetrics } from '../../../api/prometheus';6+ style="margin-bottom: 8px"
7+ :key="i18n.global.locale.value"
8+ />
9+ <div class="s-i-row">
10+ <div class="s-i-col">
11+ <my-card :title="$t('dashboard.cpuUseSituation')" height="255" :bodyPadding="false">
12+ <div id="system_source_0" style="height: 100%">
13+ <LazyLine
14+ :tabId="uuid()"
15+ :formatter="toFixed"
16+ :data="metricsData.cpu"
17+ :xData="metricsData.time"
18+ :max="100"
19+ :min="0"
20+ :interval="25"
21+ :unit="'%'"
22+ />
23+ </div>
24+ </my-card>
25+ </div>
26+ <div class="s-i-col">
27+ <my-card :title="$t('dashboard.memoryUsage')" height="255" :bodyPadding="false">
28+ <div id="system_source_1" style="height: 100%">
29+ <LazyLine
30+ :tabId="uuid()"
31+ :formatter="toFixed"
32+ :data="metricsData.memoryUsed"
33+ :xData="metricsData.time"
34+ :max="100"
35+ :min="0"
36+ :interval="25"
37+ :unit="'%'"
38+ />
39+ </div>
40+ </my-card>
41+ </div>
42+ </div>
43+ <div class="s-i-row">
44+ <div class="s-i-col">
45+ <my-card :title="$t('dashboard.networkTransmissionRate')" height="255" :bodyPadding="false">
46+ <div id="system_source_2" style="height: 100%">
47+ <LazyLine
48+ :tabId="uuid()"
49+ :formatter="toFixed"
50+ :data="metricsData.network"
51+ :xData="metricsData.time"
52+ :unit="'M/S'"
53+ />
54+ </div>
55+ </my-card>
56+ </div>
57+ <div class="s-i-col">
58+ <my-card
59+ :title="$t('resourceMonitor.io.ioUsage')"
60+ height="255"
61+ :legend="[
62+ { color: '#00C7F9', name: $t('metric.read') },
63+ { color: '#37D4D1', name: $t('metric.write') },
64+ ]"
65+ :bodyPadding="false"
66+ >
67+ <div id="system_source_3" style="height: 100%">
68+ <LazyLine
69+ :tabId="uuid()"
70+ :formatter="toFixed"
71+ :data="metricsData.ioUse"
72+ :xData="metricsData.time"
73+ :max="100"
74+ :min="0"
75+ :interval="25"
76+ :unit="'%'"
77+ :tool-tips-sort="'desc'"
78+ :tool-tips-exclude-zero="true"
79+ />
80+ </div>
81+ </my-card>
82+ </div>
83+ </div>
84+ </div>
85+</template>
7 86 
8-const props = withDefaults(defineProps<{87+<script setup lang="ts">
9- fixedRangeTime?: string[],88+import moment from 'moment'
10-}>(), {89+import LazyLine from '@/components/echarts/LazyLine.vue'
11- fixedRangeTime: () => [],90+import { toFixed, uuid } from '@/shared'
91+import { i18n } from '@/i18n'
92+import { getSQLMetrics } from '@/api/sqlDetail'
93+import { useRequest } from 'vue-request'
94+import dayjs from 'dayjs'
95+import utc from 'dayjs/plugin/utc'
96+import timezone from 'dayjs/plugin/timezone'
97+import { useI18n } from 'vue-i18n'
98+const { t } = useI18n()
99+ 
100+dayjs.extend(utc)
101+dayjs.extend(timezone)
102+ 
103+interface LineData {
104+ name: string
105+ data: any[]
106+ [other: string]: any
107+}
108+interface MetricsData {
109+ cpu: LineData[]
110+ memoryUsed: LineData[]
111+ network: LineData[]
112+ ioUse: LineData[]
113+ time: string[]
114+}
115+const metricsData = ref<MetricsData>({
116+ cpu: [],
117+ memoryUsed: [],
118+ network: [],
119+ ioUse: [],
120+ time: [],
12})121})
122+const props = withDefaults(
123+ defineProps<{
124+ dbid: string
125+ fixedRangeTime?: string[]
126+ }>(),
127+ {
128+ dbid: '',
129+ fixedRangeTime: () => [],
130+ }
131+)
13 132 
14const dealTime = (value: string) => {133const dealTime = (value: string) => {
15- return moment(value).format('HH:mm:ss')134+ return moment(value).format('HH:mm:ss')
16}135}
17 136 
18const getRangeTime = () => {137const getRangeTime = () => {
19- return `${dealTime(props.fixedRangeTime[0])} ~ ${dealTime(props.fixedRangeTime[1])}`138+ return `${dealTime(props.fixedRangeTime[0])} ~ ${dealTime(props.fixedRangeTime[1])}`
20}139}
21 140 
22onMounted(() => {141onMounted(() => {
23- getDatabaseMetrics()142+ requestData(
24-});143+ props.dbid,
144+ Math.floor(moment(props.fixedRangeTime[0]).valueOf() / 1000).toString(),
145+ Math.floor(moment(props.fixedRangeTime[1]).valueOf() / 1000).toString(),
146+ '60'
147+ )
148+})
25 149 
150+const { data: indexData, run: requestData } = useRequest(getSQLMetrics, { manual: true })
151+watch(
152+ indexData,
153+ () => {
154+ // clear data
155+ metricsData.value.cpu = []
156+ metricsData.value.memoryUsed = []
157+ metricsData.value.network = []
158+ metricsData.value.ioUse = []
159+ 
160+ const baseData = indexData.value
161+ if (!baseData) return
162+ 
163+ // CPU
164+ {
165+ let tempData: string[] = []
166+ baseData.CPU_DB.forEach((d: number) => {
167+ tempData.push(toFixed(d))
168+ })
169+ metricsData.value.cpu.push({ data: tempData, name: t('resourceMonitor.cpu.dbThread') })
170+ }
171+ {
172+ let tempData: string[] = []
173+ baseData.CPU_TOTAL.forEach((d: number) => {
174+ tempData.push(toFixed(d))
175+ })
176+ metricsData.value.cpu.push({ data: tempData, name: 'Total' })
177+ }
178+ {
179+ let tempData: string[] = []
180+ baseData.CPU_USER.forEach((d: number) => {
181+ tempData.push(toFixed(d))
182+ })
183+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'User' })
184+ }
185+ {
186+ let tempData: string[] = []
187+ baseData.CPU_SYSTEM.forEach((d: number) => {
188+ tempData.push(toFixed(d))
189+ })
190+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'System' })
191+ }
192+ {
193+ let tempData: string[] = []
194+ baseData.CPU_IOWAIT.forEach((d: number) => {
195+ tempData.push(toFixed(d))
196+ })
197+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'IOWait' })
198+ }
199+ {
200+ let tempData: string[] = []
201+ baseData.CPU_NICE.forEach((d: number) => {
202+ tempData.push(toFixed(d))
203+ })
204+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Nice' })
205+ }
206+ {
207+ let tempData: string[] = []
208+ baseData.CPU_IRQ.forEach((d: number) => {
209+ tempData.push(toFixed(d))
210+ })
211+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'IRQ' })
212+ }
213+ {
214+ let tempData: string[] = []
215+ baseData.CPU_SOFTIRQ.forEach((d: number) => {
216+ tempData.push(toFixed(d))
217+ })
218+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Soft IRQ' })
219+ }
220+ {
221+ let tempData: string[] = []
222+ baseData.CPU_STEAL.forEach((d: number) => {
223+ tempData.push(toFixed(d))
224+ })
225+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Steal' })
226+ }
227+ {
228+ let tempData: string[] = []
229+ baseData.CPU_IDLE.forEach((d: number) => {
230+ tempData.push(toFixed(d))
231+ })
232+ metricsData.value.cpu.push({ data: tempData, areaStyle: {}, stack: 'Total', name: 'Idle' })
233+ }
234+ 
235+ // memory
236+ if (baseData.MEMORY_USED) {
237+ let tempData: string[] = []
238+ baseData.MEMORY_USED.forEach((d: number) => {
239+ tempData.push(toFixed(d))
240+ })
241+ metricsData.value.memoryUsed.push({
242+ data: tempData,
243+ areaStyle: {},
244+ name: t('resourceMonitor.memory.memoryUse'),
245+ })
246+ }
247+ if (baseData.MEMORY_DB_USED) {
248+ let tempData: string[] = []
249+ baseData.MEMORY_DB_USED.forEach((d: number) => {
250+ tempData.push(toFixed(d))
251+ })
252+ metricsData.value.memoryUsed.push({
253+ data: tempData,
254+ areaStyle: {},
255+ name: t('resourceMonitor.memory.memoryDBUse'),
256+ })
257+ }
258+ 
259+ // Network
260+ if (baseData.NETWORK_OUT_TOTAL && baseData.NETWORK_OUT_TOTAL.length > 0) {
261+ let tempData: string[] = []
262+ baseData.NETWORK_OUT_TOTAL.forEach((d: number) => {
263+ tempData.push(toFixed(d / 1024 / 1024, 1))
264+ })
265+ metricsData.value.network.push({
266+ data: tempData,
267+ areaStyle: {},
268+ stack: 'Total',
269+ name: 'Out',
270+ lineStyle: {
271+ color: '#0E78DA',
272+ },
273+ })
274+ }
275+ if (baseData.NETWORK_IN_TOTAL && baseData.NETWORK_IN_TOTAL.length > 0) {
276+ let tempData: string[] = []
277+ baseData.NETWORK_IN_TOTAL.forEach((d: number) => {
278+ tempData.push(toFixed(d / 1024 / 1024, 1))
279+ })
280+ metricsData.value.network.push({
281+ data: tempData,
282+ areaStyle: {},
283+ stack: 'Total',
284+ name: 'In',
285+ lineStyle: {
286+ color: '#83CBFF',
287+ },
288+ })
289+ }
290+ 
291+ // io use
292+ for (let key in baseData.IO_UTIL) {
293+ let tempData: string[] = []
294+ baseData.IO_UTIL[key].forEach((element: any) => {
295+ tempData.push(toFixed(element))
296+ })
297+ metricsData.value.ioUse.push({ data: tempData, name: key, key })
298+ }
299+ 
300+ // time
301+ metricsData.value.time = baseData.time
302+ },
303+ { deep: true }
304+)
26</script>305</script>
27 306 
28-<template>
29- <div class="system-source">
30- <my-message
31- type="info"
32- :tip="`${$t('dashboard.rangeTimeTip')}(${getRangeTime()})`"
33- style="margin-bottom: 8px;"
34- :key="i18n.global.locale.value"
35- />
36- <div class="s-i-row">
37- <div class="s-i-col">
38- <my-card :title="$t('dashboard.cpuUseSituation')" height="255" :legend="[
39- {color: '#9CCC65', name: $t('metric.totalCoreNum')},
40- {color: '#00C7F9', name: $t('metric.totalAverageUtilization')}
41- ]" :bodyPadding="false">
42- <div class="linename">
43- <div>{{ $t('metric.totalCoreNum') }}</div>
44- <div id="system_source_0" style="height: 100%;">
45- <LazyLine
46- :color="['#9CCC65', '#00C7F9']"
47- :names="['totalCoreNum', 'totalAverageUtilization']"
48- :name-indexs="[1]"
49- name-fix="1"
50- hasScatterData
51- scatterUnit="%"
52- :scatterOpts="{type: 'line', symbol: 'none'}"
53- :defaultBrushArea="props.fixedRangeTime"
54- :countByDataTimePicker="false"
55- />
56- </div>
57- <div>{{ $t('metric.totalAverageUtilization') }}</div>
58- </div>
59- </my-card>
60- </div>
61- <div class="s-i-col">
62- <my-card :title="$t('dashboard.memoryUsage')" height="255" :legend="[
63- {color: '#00C7F9', name: $t('metric.totalAverageUtilization')}
64- ]" :bodyPadding="false">
65- <div id="system_source_1" style="height: 100%;">
66- <LazyLine
67- :color="['#00C7F9']"
68- :names="['totalAverageUtilization']"
69- :name-indexs="[0]"
70- name-fix="2"
71- unit="%"
72- :defaultBrushArea="props.fixedRangeTime"
73- :countByDataTimePicker="false"
74- />
75- </div>
76- </my-card>
77- </div>
78- </div>
79- <div class="s-i-row">
80- <div class="s-i-col">
81- <my-card :title="$t('dashboard.networkTransmissionRate')" height="255" :legend="[
82- {color: '#00C7F9', name: $t('metric.upload')},
83- {color: '#37D4D1', name: $t('metric.download')}
84- ]" :bodyPadding="false">
85- <div id="system_source_2" style="height: 100%;">
86- <LazyLine
87- :color="['#00C7F9', '#37D4D1']"
88- :names="['upload', 'download']"
89- unit="MB/s"
90- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1048576))"
91- :defaultBrushArea="props.fixedRangeTime"
92- :countByDataTimePicker="false"
93- />
94- </div>
95- </my-card>
96- </div>
97- <div class="s-i-col">
98- <my-card :title="$t('dashboard.diskReadAndWriteRate2')" height="255" :legend="[
99- {color: '#00C7F9', name: $t('metric.read')},
100- {color: '#37D4D1', name: $t('metric.write')}
101- ]" :bodyPadding="false">
102- <div id="system_source_3" style="height: 100%;">
103- <LazyLine
104- :color="['#00C7F9', '#37D4D1']"
105- :names="['read', 'write']"
106- :name-indexs="[0, 1]"
107- name-fix="1"
108- :formatter="(d: string) => toFixed((Number.parseFloat(d) / 1024))"
109- unit="KB/s"
110- :defaultBrushArea="props.fixedRangeTime"
111- :countByDataTimePicker="false"
112- />
113- </div>
114- </my-card>
115- </div>
116- </div>
117- </div>
118-</template>
119- 
120<style scoped lang="scss">307<style scoped lang="scss">
121.linename {308.linename {
122- display: flex;309+ display: flex;
123- width: 100%;310+ width: 100%;
311+ height: 100%;
312+ align-items: center;
313+ position: relative;
314+ justify-content: center;
315+ > div:nth-of-type(2) {
316+ width: calc(100% - 60px);
124 height: 100%;317 height: 100%;
125- align-items: center;318+ margin: 0 10px;
126- position: relative;319+ }
127- justify-content: center;320+ > div:nth-of-type(1),
128- > div:nth-of-type(2) {321+ > div:nth-of-type(3) {
129- width: calc(100% - 60px);322+ color: var(--el-color-line-text-color);
130- height: 100%;323+ font-size: 12px;
131- margin: 0 10px;324+ text-align: center;
132- }325+ position: absolute;
133- > div:nth-of-type(1), > div:nth-of-type(3) {326+ width: 200px;
134- color: var(--el-color-line-text-color);327+ height: 15px;
135- font-size: 12px;328+ }
136- text-align: center;329+ > div:nth-of-type(1) {
137- position: absolute;330+ transform: rotate(-90deg);
138- width: 200px;331+ left: -80px;
139- height: 15px;332+ }
140- }333+ > div:nth-of-type(3) {
141- > div:nth-of-type(1) {334+ transform: rotate(90deg);
142- transform: rotate(-90deg);335+ right: -80px;
143- left: -80px;336+ }
144- }
145- > div:nth-of-type(3) {
146- transform: rotate(90deg);
147- right: -80px;
148- }
149}337}
150.system-source {338.system-source {
339+ .s-i-row {
340+ width: 100%;
341+ display: flex;
342+ justify-content: space-between;
343+ margin-bottom: 18px;
344+ }
151 345 
152- .s-i-row {346+ .s-i-col {
153- width: 100%;347+ height: inherit;
154- display: flex;348+ width: 49%;
155- justify-content: space-between;349+ position: relative;
156- margin-bottom: 18px;350+ }
157- }
158- 
159- .s-i-col {
160- height: inherit;
161- width: 49%;
162- position: relative;
163- }
164}351}
165</style>352</style>
Mplugins/observability-instance/web-ui/src/router/index.ts+101-72
@@ -5,87 +5,116 @@
5import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'5import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
6 6 
7declare module 'vue-router' {7declare module 'vue-router' {
8- // eslint-disable-next-line no-unused-vars8+ // eslint-disable-next-line no-unused-vars
9- interface RouteMeta {9+ interface RouteMeta {
10- icon?: string10+ icon?: string
11- title: string11+ title: string
12- hidden?: boolean12+ hidden?: boolean
13- breadcrumb?: string[]13+ breadcrumb?: string[]
14- }14+ }
15}15}
16 16 
17export const routes: RouteRecordRaw[] = [17export const routes: RouteRecordRaw[] = [
18- {18+ {
19- path: '/vem',19+ path: '/vem',
20- meta: {20+ meta: {
21- icon: 'vite',21+ icon: 'vite',
22- title: 'app.menuName',22+ title: 'app.menuName',
23- },
24- children: [
25- {
26- path: '/vem/dashboard/instance',
27- component: () => import('@/pages/dashboardV2/Index.vue'),
28- meta: {
29- title: 'dashboard.instance',
30- breadcrumb: [],
31- },
32- },
33- {
34- path: '/vem/sessionDetail/:dbid/:id',
35- meta: {
36- icon: 'vite',
37- title: 'session.tabTitle',
38- breadcrumb: ['app.menuName', 'session.tabTitle'],
39- hidden: true,
40- },
41- name: 'SessionDetailLocal',
42- component: () => import('@/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue'),
43- },
44- {
45- path: '/vem/sessionDetail',
46- meta: {
47- icon: 'vite',
48- title: 'session.tabTitle',
49- breadcrumb: ['app.menuName', 'session.tabTitle'],
50- },
51- name: 'SessionDetail',
52- component: () => import('@/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue'),
53- },
54- {
55- path: '/vem/sql_detail/:dbid/:sqlId',
56- meta: {
57- icon: 'vite',
58- title: 'sql.sqlDetail',
59- breadcrumb: ['app.menuName', 'dashboard.instance'],
60- hidden: true,
61- },
62- name: 'SqlDetailLocal',
63- component: () => import('@/pages/sql_detail/Index.vue'),
64- },
65- {
66- path: '/vem/sql_detail',
67- meta: {
68- icon: 'vite',
69- title: 'sql.sqlDetail',
70- breadcrumb: ['app.menuName', 'dashboard.instance'],
71- hidden: true,
72- },
73- name: 'SqlDetail',
74- component: () => import('@/pages/sql_detail/Index.vue'),
75- },
76- ],
77 },23 },
24+ children: [
25+ {
26+ path: '/vem/dashboard/clusters',
27+ component: () => import('@/pages/clusterMonitor/Index.vue'),
28+ meta: {
29+ title: 'dashboard.clusters',
30+ breadcrumb: [],
31+ },
32+ },
33+ {
34+ path: '/vem/clusterDetail/:dbid',
35+ meta: {
36+ icon: 'vite',
37+ title: 'session.tabTitle',
38+ breadcrumb: ['app.menuName', 'session.tabTitle'],
39+ hidden: true,
40+ },
41+ name: 'ClusterDetailLocal',
42+ component: () => import('@/pages/clusterMonitor/cluster/Index.vue'),
43+ },
44+ {
45+ path: '/vem/clusterDetail',
46+ meta: {
47+ icon: 'vite',
48+ title: 'session.tabTitle',
49+ breadcrumb: ['app.menuName', 'session.tabTitle'],
50+ },
51+ name: 'ClusterDetail',
52+ component: () => import('@/pages/clusterMonitor/cluster/Index.vue'),
53+ },
54+ {
55+ path: '/vem/dashboard/instance',
56+ component: () => import('@/pages/dashboardV2/Index.vue'),
57+ meta: {
58+ title: 'dashboard.instance',
59+ breadcrumb: [],
60+ },
61+ },
62+ {
63+ path: '/vem/sessionDetail/:dbid/:id',
64+ meta: {
65+ icon: 'vite',
66+ title: 'session.tabTitle',
67+ breadcrumb: ['app.menuName', 'session.tabTitle'],
68+ hidden: true,
69+ },
70+ name: 'SessionDetailLocal',
71+ component: () => import('@/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue'),
72+ },
73+ {
74+ path: '/vem/sessionDetail',
75+ meta: {
76+ icon: 'vite',
77+ title: 'session.tabTitle',
78+ breadcrumb: ['app.menuName', 'session.tabTitle'],
79+ },
80+ name: 'SessionDetail',
81+ component: () => import('@/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue'),
82+ },
83+ {
84+ path: '/vem/sql_detail/:dbid/:sqlId',
85+ meta: {
86+ icon: 'vite',
87+ title: 'sql.sqlDetail',
88+ breadcrumb: ['app.menuName', 'dashboard.instance'],
89+ hidden: true,
90+ },
91+ name: 'SqlDetailLocal',
92+ component: () => import('@/pages/sql_detail/Index.vue'),
93+ },
94+ {
95+ path: '/vem/sql_detail',
96+ meta: {
97+ icon: 'vite',
98+ title: 'sql.sqlDetail',
99+ breadcrumb: ['app.menuName', 'dashboard.instance'],
100+ hidden: true,
101+ },
102+ name: 'SqlDetail',
103+ component: () => import('@/pages/sql_detail/Index.vue'),
104+ },
105+ ],
106+ },
78]107]
79 108 
80const router = createRouter({109const router = createRouter({
81- history: createWebHashHistory(),110+ history: createWebHashHistory(),
82- routes,111+ routes,
83})112})
84 113 
85router.beforeEach((to, from, next) => {114router.beforeEach((to, from, next) => {
86- if (from.path.includes(`/vem/track_detail/`) && to.path.includes(`/vem/log/track`)) {115+ if (from.path.includes(`/vem/track_detail/`) && to.path.includes(`/vem/log/track`)) {
87- sessionStorage.removeItem('nodes')116+ sessionStorage.removeItem('nodes')
88- }117+ }
89- next()118+ next()
90})119})
91export default router120export default router
Mplugins/observability-instance/web-ui/src/store/monitor.ts+191-186
@@ -5,205 +5,210 @@
5import { defineStore } from 'pinia'5import { defineStore } from 'pinia'
6 6 
7const sourceType = {7const sourceType = {
8- INSTANCE: 'INSTANCE',8+ INSTANCE: 'INSTANCE',
9- AUTOREFRESHTIME: 'AUTOREFRESHTIME',9+ AUTOREFRESHTIME: 'AUTOREFRESHTIME',
10- MANUALREFRESH: 'MANUALREFRESH',10+ MANUALREFRESH: 'MANUALREFRESH',
11- TABCHANGE: 'TABCHANGE',11+ TABCHANGE: 'TABCHANGE',
12- TIMETYPE: 'TIMETYPE',12+ TIMETYPE: 'TIMETYPE',
13- TIMERANGE: 'TIMERANGE',13+ TIMERANGE: 'TIMERANGE',
14}14}
15const timeTypeSelection = {15const timeTypeSelection = {
16- MIN15: '15m',16+ MIN15: '15m',
17- MIN30: '30m',17+ MIN30: '30m',
18- HOUR1: '1h',18+ HOUR1: '1h',
19- HOUR3: '3h',19+ HOUR3: '3h',
20- HOUR6: '6h',20+ HOUR6: '6h',
21- HOUR12: '12h',21+ HOUR12: '12h',
22- DAY1: '1d',22+ DAY1: '1d',
23- DAY2: '2d',23+ DAY2: '2d',
24- DAY7: '7d',24+ DAY7: '7d',
25- CUSTOM: 'CUSTOM',25+ CUSTOM: 'CUSTOM',
26}26}
27interface State {27interface State {
28- // use to refresh data28+ // use to refresh data
29- updateCounter: {29+ updateCounter: {
30- count: number30+ count: number
31- source: string31+ source: string
32- }32+ }
33- clusterId: string33+ clusterId: string
34- instanceId: string34+ instanceId: string
35- tabNow: string35+ node: any
36- autoRefreshTime: number36+ tabNow: string
37- timeType: string37+ autoRefreshTime: number
38- timeRange: string[]38+ timeType: string
39- isManualRangeSelected: boolean39+ timeRange: string[]
40+ isManualRangeSelected: boolean
40 41 
41- databaseData: Record<string, { data: string[]; time: string[]; name: string }[]>42+ databaseData: Record<string, { data: string[]; time: string[]; name: string }[]>
43+ /**
44+ * current tab index
45+ */
46+ tab: number
47+ filters: {
42 /**48 /**
43- * current tab index49+ * auto refresh interval
44 */50 */
45- tab: number51+ refreshTime: number
46- filters: {
47- /**
48- * auto refresh interval
49- */
50- refreshTime: number
51- /**
52- * range time
53- */
54- rangeTime: number
55- /**
56- * custom range time
57- */
58- time: [Date, Date] | null
59- }[]
60- autoRefresh: boolean
61- instanceTimeRange: [Date, Date] | null
62 /**52 /**
63- * brush select53+ * range time
64 */54 */
65- brushRange: string[]55+ rangeTime: number
66 /**56 /**
67- * topsql server57+ * custom range time
68 */58 */
69- fixedRangeTime: Array<string>59+ time: [Date, Date] | null
70- serverData: Record<string, { data: string[]; time: string[]; name: string }[]>60+ }[]
71- promethuesStart: number61+ autoRefresh: boolean
72- promethuesEnd: number62+ instanceTimeRange: [Date, Date] | null
73- promethuesStep: number63+ /**
64+ * brush select
65+ */
66+ brushRange: string[]
67+ /**
68+ * topsql server
69+ */
70+ fixedRangeTime: Array<string>
71+ serverData: Record<string, { data: string[]; time: string[]; name: string }[]>
72+ promethuesStart: number
73+ promethuesEnd: number
74+ promethuesStep: number
74}75}
75export const useMonitorStore = (tabId: string) => {76export const useMonitorStore = (tabId: string) => {
76- return defineStore('monitor-' + tabId, {77+ return defineStore('monitor-' + tabId, {
77- state: (): State => ({78+ state: (): State => ({
78- updateCounter: {79+ updateCounter: {
79- count: 0,80+ count: 0,
80- source: '',81+ source: '',
81- },82+ },
82- clusterId: '',83+ clusterId: '',
83- instanceId: '',84+ instanceId: '',
84- tabNow: '',85+ node: {},
85- autoRefreshTime: 30,86+ tabNow: '',
86- timeType: timeTypeSelection.HOUR1,87+ autoRefreshTime: 30,
87- timeRange: [],88+ timeType: timeTypeSelection.HOUR1,
88- isManualRangeSelected: false,89+ timeRange: [],
90+ isManualRangeSelected: false,
89 91 
90- // below may not be use anymore 052592+ // below may not be use anymore 0525
91- databaseData: {},93+ databaseData: {},
92- tab: 0,94+ tab: 0,
93- filters: [95+ filters: [
94- {96+ {
95- refreshTime: 30,97+ refreshTime: 30,
96- rangeTime: 1,98+ rangeTime: 1,
97- time: null,99+ time: null,
98- },100+ },
99- {101+ {
100- refreshTime: 30,102+ refreshTime: 30,
101- rangeTime: 1,103+ rangeTime: 1,
102- time: null,104+ time: null,
103- },105+ },
104- {106+ {
105- refreshTime: 30,107+ refreshTime: 30,
106- rangeTime: 1,108+ rangeTime: 1,
107- time: null,109+ time: null,
108- },110+ },
109- {111+ {
110- refreshTime: 30,112+ refreshTime: 30,
111- rangeTime: 1,113+ rangeTime: 1,
112- time: null,114+ time: null,
113- },115+ },
114- ],116+ ],
115- autoRefresh: false,117+ autoRefresh: false,
116- instanceTimeRange: null,118+ instanceTimeRange: null,
117- brushRange: [],119+ brushRange: [],
118- fixedRangeTime: [],120+ fixedRangeTime: [],
119- serverData: {},121+ serverData: {},
120- promethuesStart: 0,122+ promethuesStart: 0,
121- promethuesEnd: 0,123+ promethuesEnd: 0,
122- promethuesStep: 60,124+ promethuesStep: 60,
123- }),125+ }),
124- getters: {126+ getters: {
125- refreshTimeForBind: (state: State) => {127+ refreshTimeForBind: (state: State) => {
126- return state.autoRefreshTime128+ return state.autoRefreshTime
127- },129+ },
128 130 
129- // below may not be use anymore 0525131+ // below may not be use anymore 0525
130- refreshTime: (state: State) => {132+ refreshTime: (state: State) => {
131- return state.filters[state.tab].refreshTime133+ return state.filters[state.tab].refreshTime
132- },134+ },
133- sourceType: (state: State) => {135+ sourceType: (state: State) => {
134- return sourceType136+ return sourceType
135- },137+ },
136- timeTypeSelection: (state: State) => {138+ timeTypeSelected: (state: State) => {
137- return timeTypeSelection139+ return state.timeType
138- },140+ },
139- rangeTime: (state: State) => {141+ timeTypeSelection: (state: State) => {
140- return state.filters[state.tab].rangeTime142+ return timeTypeSelection
141- },143+ },
142- time: (state: State) => {144+ rangeTime: (state: State) => {
143- return state.filters[state.tab].time145+ return state.filters[state.tab].rangeTime
144- },146+ },
145- },147+ time: (state: State) => {
146- actions: {148+ return state.filters[state.tab].time
147- updateInstanceAndClusterId(instanceId: string, clusterId: string) {149+ },
148- this.instanceId = instanceId150+ selectedNode: (state: State) => {
149- this.clusterId = clusterId151+ return state.node
150- this.updateCounter = {152+ },
151- count: this.updateCounter.count + 1,153+ },
152- source: sourceType.INSTANCE,154+ actions: {
153- }155+ updateInstanceAndClusterId(instanceId: string, clusterId: string, obj: any) {
154- },156+ this.instanceId = instanceId
155- culRangeTimeAndStep() {157+ this.clusterId = clusterId
156- console.log('DEBUG: culRangeTimeAndStep')158+ this.node = obj
157- let start = 0159+ this.updateCounter = {
158- let end = 0160+ count: this.updateCounter.count + 1,
159- if (this.timeType === timeTypeSelection.CUSTOM) {161+ source: sourceType.INSTANCE,
160- start = Number.parseInt(`${new Date(this.timeRange![0]).getTime() / 1000}`)162+ }
161- end = Number.parseInt(`${new Date(this.timeRange![1]).getTime() / 1000}`)163+ },
162- } else {164+ culRangeTimeAndStep() {
163- let min = 0165+ let start = 0
164- let now = new Date()166+ let end = 0
165- if (this.timeType === timeTypeSelection.MIN15) min = 15167+ if (this.timeType === timeTypeSelection.CUSTOM) {
166- else if (this.timeType === timeTypeSelection.MIN30) min = 30168+ start = Number.parseInt(`${new Date(this.timeRange![0]).getTime() / 1000}`)
167- else if (this.timeType === timeTypeSelection.HOUR1) min = 1 * 60169+ end = Number.parseInt(`${new Date(this.timeRange![1]).getTime() / 1000}`)
168- else if (this.timeType === timeTypeSelection.HOUR3) min = 3 * 60170+ } else {
169- else if (this.timeType === timeTypeSelection.HOUR6) min = 6 * 60171+ let min = 0
170- else if (this.timeType === timeTypeSelection.HOUR12) min = 12 * 60172+ let now = new Date()
171- else if (this.timeType === timeTypeSelection.DAY1) min = 1 * 24 * 60173+ if (this.timeType === timeTypeSelection.MIN15) min = 15
172- else if (this.timeType === timeTypeSelection.DAY2) min = 2 * 24 * 60174+ else if (this.timeType === timeTypeSelection.MIN30) min = 30
173- else if (this.timeType === timeTypeSelection.DAY7) min = 7 * 24 * 60175+ else if (this.timeType === timeTypeSelection.HOUR1) min = 1 * 60
174- start = Number.parseInt(`${(now.getTime() - 1000 * min * 60) / 1000}`)176+ else if (this.timeType === timeTypeSelection.HOUR3) min = 3 * 60
175- end = Number.parseInt(`${now.getTime() / 1000}`)177+ else if (this.timeType === timeTypeSelection.HOUR6) min = 6 * 60
176- }178+ else if (this.timeType === timeTypeSelection.HOUR12) min = 12 * 60
177- console.log('DEBUG: start', start)179+ else if (this.timeType === timeTypeSelection.DAY1) min = 1 * 24 * 60
178- console.log('DEBUG: end', end)180+ else if (this.timeType === timeTypeSelection.DAY2) min = 2 * 24 * 60
179- console.log('(end - start) / 260', Math.round((end - start) / 260))181+ else if (this.timeType === timeTypeSelection.DAY7) min = 7 * 24 * 60
180- return [start, end, Math.max(14, Number.parseInt(`${Math.round((end - start) / 260)}`))]182+ start = Number.parseInt(`${(now.getTime() - 1000 * min * 60) / 1000}`)
181- },183+ end = Number.parseInt(`${now.getTime() / 1000}`)
182- updateTabNow(tabNow: string) {184+ }
183- this.tabNow = tabNow185+ return [start, end, Math.max(14, Number.parseInt(`${Math.round((end - start) / 260)}`))]
184- this.updateCounter = {186+ },
185- count: this.updateCounter.count + 1,187+ updateTabNow(tabNow: string) {
186- source: sourceType.TABCHANGE,188+ this.tabNow = tabNow
187- }189+ this.updateCounter = {
188- },190+ count: this.updateCounter.count + 1,
189- increaseCounter(soucre: string) {191+ source: sourceType.TABCHANGE,
190- if (soucre === sourceType.TIMERANGE || soucre === sourceType.TIMETYPE) {192+ }
191- this.isManualRangeSelected = false193+ },
192- }194+ increaseCounter(soucre: string) {
193- this.updateCounter = {195+ if (soucre === sourceType.TIMERANGE || soucre === sourceType.TIMETYPE) {
194- count: this.updateCounter.count + 1,196+ this.isManualRangeSelected = false
195- source: soucre,197+ }
196- }198+ this.updateCounter = {
197- },199+ count: this.updateCounter.count + 1,
198- manualRangeSelection(timeRange: string[]) {200+ source: soucre,
199- this.isManualRangeSelected = true201+ }
200- this.timeType = timeTypeSelection.CUSTOM202+ },
201- this.timeRange = timeRange203+ manualRangeSelection(timeRange: string[]) {
202- this.updateCounter = {204+ this.isManualRangeSelected = true
203- count: this.updateCounter.count + 1,205+ this.timeType = timeTypeSelection.CUSTOM
204- source: sourceType.TIMERANGE,206+ this.timeRange = timeRange
205- }207+ this.updateCounter = {
206- },208+ count: this.updateCounter.count + 1,
207- },209+ source: sourceType.TIMERANGE,
208- })()210+ }
211+ },
212+ },
213+ })()
209}214}
Mplugins/observability-instance/web-ui/vite.config.ts+65-64
@@ -2,72 +2,73 @@
2/// Copyright (c) 2023 Huawei Technologies Co.,Ltd.2/// Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3///3///
4 4 
5-import { defineConfig, loadEnv } from "vite";5+import { defineConfig, loadEnv } from 'vite'
6-import vue from "@vitejs/plugin-vue";6+import vue from '@vitejs/plugin-vue'
7-import AutoImport from "unplugin-auto-import/vite";7+import AutoImport from 'unplugin-auto-import/vite'
8-import Components from "unplugin-vue-components/vite";8+import Components from 'unplugin-vue-components/vite'
9-import { ElementPlusResolver } from "unplugin-vue-components/resolvers";9+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
10-import { createSvgIconsPlugin } from "vite-plugin-svg-icons";10+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
11-import { resolve } from "path";11+import { resolve } from 'path'
12 12 
13// https://vitejs.dev/config/13// https://vitejs.dev/config/
14export default defineConfig(({ command, mode }) => {14export default defineConfig(({ command, mode }) => {
15- loadEnv(mode, process.cwd());15+ loadEnv(mode, process.cwd())
16- return {16+ return {
17- base: mode === "production" ? "/static-plugin/observability-instance/" : "/",17+ base: mode === 'production' ? '/static-plugin/observability-instance/' : '/',
18- plugins: [18+ plugins: [
19- vue(),19+ vue(),
20- AutoImport({20+ AutoImport({
21- resolvers: [ElementPlusResolver()],21+ resolvers: [ElementPlusResolver()],
22- imports: ["vue", "vue-router"],22+ imports: ['vue', 'vue-router'],
23- dts: "src/auto-imports.d.ts",23+ dts: 'src/auto-imports.d.ts',
24- eslintrc: {24+ eslintrc: {
25- enabled: false,25+ enabled: false,
26- filepath: "./.eslintrc-auto-import.json",26+ filepath: './.eslintrc-auto-import.json',
27- globalsPropValue: true,27+ globalsPropValue: true,
28- },28+ },
29- }),29+ }),
30- Components({30+ Components({
31- resolvers: [31+ resolvers: [
32- ElementPlusResolver({32+ ElementPlusResolver({
33- importStyle: "sass",33+ importStyle: 'sass',
34- }),34+ }),
35- ],
36- dts: "src/components.d.ts",
37- dirs: ["src/components", "src/layout"],
38- }),
39- createSvgIconsPlugin({
40- iconDirs: [resolve(process.cwd(), "src/assets/svg")],
41- symbolId: "icon-[dir]-[name]",
42- inject: "body-first",
43- }),
44 ],35 ],
45- define: {36+ dts: 'src/components.d.ts',
46- "process.env": {37+ dirs: ['src/components', 'src/layout'],
47- mode,38+ }),
48- },39+ createSvgIconsPlugin({
40+ iconDirs: [resolve(process.cwd(), 'src/assets/svg')],
41+ symbolId: 'icon-[dir]-[name]',
42+ inject: 'body-first',
43+ }),
44+ ],
45+ define: {
46+ 'process.env': {
47+ mode,
48+ },
49+ },
50+ resolve: {
51+ alias: {
52+ '@': resolve(__dirname, './src/'),
53+ },
54+ },
55+ css: {
56+ preprocessorOptions: {
57+ scss: {
58+ additionalData: `@use "@/assets/style/theme.scss" as *;@use "@/assets/style/color.scss" as *;`,
49 },59 },
50- resolve: {60+ },
51- alias: {61+ },
52- "@": resolve(__dirname, "./src/"),62+ server: {
53- },63+ proxy: {
54- },64+ '^/instanceMonitoring': 'http://192.168.110.31:9494/plugins/observability-instance',
55- css: {65+ '^/observability': 'http://192.168.110.31:9494/plugins/observability-instance',
56- preprocessorOptions: {66+ '^/sqlDiagnosis': 'http://192.168.110.31:9494/plugins/observability-instance',
57- scss: {67+ '^/wdr': 'http://192.168.110.31:9494/plugins/observability-instance',
58- additionalData: `@use "@/assets/style/theme.scss" as *;@use "@/assets/style/color.scss" as *;`,68+ '^/encryption': 'http://192.168.110.31:9494/plugins/observability-instance',
59- },69+ '^/host': 'http://192.168.110.31:9494/',
60- },70+ '^/hostUser': 'http://192.168.110.31:9494/',
61- },71+ },
62- server: {72+ },
63- proxy: {73+ }
64- '^/observability': 'http://192.168.110.31:9494/plugins/observability-instance',74+})
65- '^/sqlDiagnosis': 'http://192.168.110.31:9494/plugins/observability-instance',
66- '^/wdr': 'http://192.168.110.31:9494/plugins/observability-instance',
67- '^/encryption': 'http://192.168.110.31:9494/plugins/observability-instance',
68- '^/host': 'http://192.168.110.31:9494/',
69- '^/hostUser': 'http://192.168.110.31:9494/'
70- },
71- },
72- };
73-});